¿Puede un contrato inteligente establecer una variable de otro contrato inteligente conocido?

Tengo la siguiente configuración aquí, simplificada, pero aún demuestra mi problema.

Supongamos que tengo un UsuarioA y un UsuarioB que tienen una billetera y un contrato inteligente respectivamente. Cada usuario implementa su propio contrato inteligente.

UsuarioA.sol

pragma solidity ^0.4.2;

contract UserA {

  address public owner;
  address public user;
  address public SCAddress; //address where UserB has mined his contract

 function UserA(string _provider,address _user,address _scaddress) {
        owner = msg.sender;
        user = _user;
        SCAddress = _scaddress;
    }

  function setColor(string c) { //want to set the color of UserB from here
    if (msg.sender == user) { // check if it is userA   
             color = c;
        }
    }

UsuarioB.sol :

pragma solidity ^0.4.2;

contract UserB {

  address public owner;
  address public user; //userA wallet address
  string public color;

 function UserB(address _user) {
        owner = msg.sender;
        user = user;
}

function chooseColor(int number) constant returns (string color) {
            if(number == 1){ return "red";} 
            else if(number == 2){ return "blue";} 
            else if(number == 3){ return "green";}
    } 
}

UsuarioB.js

var Web3 = require('web3');

//connect to testRPC / Geth locally
if (typeof web3 !== 'undefined') {
    web3 = new Web3(web3.currentProvider);
} else {
    // set the provider you want from Web3.providers
    web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

// Checking Ethereum connection status
if (!web3.isConnected()) {
    console.error("Ethereum - no conection to RPC server");
} else {
    console.log("Ethereum - connected to RPC server");
}

var userbABI = ;// lets assume I have it 
var userbAddress = ;// lets assume I have it 

var userbContract = web3.eth.contract(userbABI);
var userbSC = docContract.at(userbAddress);

var result = userbSC.chooseColor.call(1); //red
//Now I want to save this result with UserA calling setColor()
userA.setColor(result);

Mis restricciones:

  • Solo el usuario A puede modificar color.
  • Solo el Usuario B puede llamar a setcolor()través de la ABI del Usuario A.

Mis preguntas:

  • ¿Puede un contrato inteligente establecer una variable de otro contrato inteligente?
  • ¿Es factible mi idea o es completamente irreal?

Sé en este momento que UserB no puede llamar setColor()desde msg.owner=/= UserB sino a UserA .

Gracias por su valioso tiempo y ayuda.

Estoy confundido acerca del control que desea que tengan A & B. ¿A es un usuario administrador que establece algo y luego B es un usuario normal que selecciona una opción? ¿O algo mas?

Respuestas (3)

No estoy seguro de que esto sea lo que quieres, pero podría darte algunas ideas sobre cómo estructurar las cosas.

Solo necesita implementar el contrato ColorPallet. Cualquier dirección desde la que se implemente será el propietario. Solo el propietario puede ampliar la lista cada vez mayor de posibles opciones de color.

El ColorPallet también es una fábrica. Cualquiera puede venir y usar la función newUser() para implementar un contrato de usuario. La propiedad de ese contrato se da a la dirección que lo solicitó. Entonces el "usuario" tiene el control completo del "Usuario".

El "usuario" puede establecer un color. Tiene que estar dentro del alcance y va a comprobar. Entonces, si hay 3 colores, son 0, 1 y 2. 3 o más arrojarán. El administrador puede agregar más opciones.

Tengo el contrato de Usuario almacenando la selección localmente, y también empujando la elección a ColorPallet para mostrar cómo el Usuario puede establecer un valor en un contrato diferente. En este caso, enviando un mensaje.

Descargo de responsabilidad: No hay muchas pruebas. Solo quería mostrar contratos hablando entre sí y un patrón para controlar el acceso a las funciones. ;-)

Espero eso ayude.

pragma solidity ^0.4.6;

contract ColorPallet {

  address public owner; // this user has admin control

  // bytes32 is a better choice in the long run
  bytes32[] public colors;

  mapping(address => uint) public userColorChoices;
  mapping(address => bool) public userHasSelectedColor;

  modifier onlyOwner() {
    if(msg.sender != owner) throw;
    _;
  }

  function ColorPallet() {
    owner = msg.sender;
  }

  // only the admin can create a new color
  function newColor(bytes32 colorName) 
    onlyOwner
    returns(uint index) 
  {
    colors.push(colorName);
    return colors.length-1;
  }

  // contract "User" sets values here. They are organized with different data per user

  function setUserColor(uint colorId) 
    public
  {
    if(colorId >= getColorCount()) throw; // too high
    userColorChoices[msg.sender] = colorId; // msg.sender is the User contract in this context
    userHasSelectedColor[msg.sender] = true;
  }

  function getColorCount() 
    public
    constant
    returns(uint count) 
  {
    return colors.length;
  }

  function newUserContract() 
    public
    returns(address newUserContract)
  {
    User u = new User(msg.sender, this);  // we give ownership to msg.sender, the pallet is "this address"
    return u;
  }
}

contract User {

  address public owner; // this is the user wallet that deployed this contract
  address public pallet;
  uint    public myColor;

  ColorPallet c;

  modifier onlyOwner() {
    if(msg.sender != owner) throw;
    _;
  }

  function User(address userOwner, address pallet) {
    // owner is passed in, so it's the wallet that asked ColorPallet for a new "User" contract
    owner = userOwner;
    // the pallet is the one who asked for this contract to be deployed
    c = ColorPallet(pallet);
  }

  function selectColor(uint colorId) 
    //onlyOwner
    returns(bool success)
  {
    myColor = colorId;                      // set a valid choice locally in this contract
    c.setUserColor(colorId);                // set a valid choice externally in the other contract
    return true;
  }

}

Implemente ColorPallet y agregue algunos colores:

ingrese la descripción de la imagen aquí

Solicite un usuario y anote la dirección implementada:

ingrese la descripción de la imagen aquí

Ya está desplegado. Cree una instancia del contrato en la dirección desplegada.

ingrese la descripción de la imagen aquí

Elija un color válido (dentro del rango) y vea el almacenamiento actualizado.

ingrese la descripción de la imagen aquí

Compruebe el almacenamiento de ColorPallet. Ponga la dirección del contrato de Usuario en la función captadora pública para el mapeo:

ingrese la descripción de la imagen aquí

Muchas gracias por tu respuesta. Me gusta mucho tu idea de que colorPallet es una fábrica. Veré si puedo adaptar tus ideas a mi proyecto. Si no, volveré a ti.
notario público. POR CIERTO. Realmente no necesita "esto" en la función newUser(). msg.sender lo haría en el constructor receptor (en lugar de "paleta"). Lo hice de la manera detallada para que fuera posible jugar y eliminar la ambigüedad de todas las cosas de msg.sender/propietario que están sucediendo. Evite tx.origin a toda costa. Morderá. ;-)
Sí, lo sé, si lees mi comentario en la respuesta de @Sebastian, verás que estaba a punto de irme tx.origin. Traté de entender su código ayer, pero todavía necesito ayuda con estas preguntas: - ¿Está mapping(address => uint) public userColorChoices;almacenando solo la última opción del usuario o es una matriz que ha almacenado todas las opciones del usuario desde el principio? - ¿Cuál es la diferencia entre retorno y devoluciones? Muchas gracias. PD: No sé, pero tal vez debería abrir una sala para hablar de esto.
el mapeo almacena una última opción para todos los usuarios. userColorChoices[address] son ​​bits organizados para que podamos buscarlos por claves de dirección. "devoluciones" está configurando la interfaz de la función - fuera de {}. "return" lo está haciendo - dentro de {}.
Corrección: userHasSelectedColor[] es un bool para que podamos saber si un valor devuelto es deliberado o simplemente un 0 predeterminado. userColorSelected[user] es un uint que significa la selección más reciente de un usuario determinado.

Es posible conectar dos contratos inteligentes entre sí. Al comienzo del archivo, debe importar el contrato de manera muy similar a otros idiomas. Luego puede configurar el otro contrato como un objeto.

UsuarioB.sol :


import "./UserA.sol";

contract UserB {
...
UserA userA
...
function UserB(address userA) {
   userA = UserA(userA);
}

Ahora puede llamar a métodos en "userA" como userA.setColor("blue");

Muchas gracias por su respuesta, pero no se ajusta a mi segundo criterio, que solo userBpuede llamar setColor()y userAsolo puede modificar color.
Estoy confundido. ¿Por qué el usuario B puede llamar a setColor() si no puede cambiar el color?
Bien, UserBdespliega el contacto inteligente B, y luego configura el color. Solo UserBconoce el color de entrada para A. Es por eso que UserBnecesita llamar UserAa la función para establecer el color.
Lo siento, todavía no entiendo lo que estás planeando hacer. Creo que parte de la confusión es que llamas a los contratos inteligentes "Usuario A"/"Usuario B", pero cuando dices "Usuario B" no siempre te refieres al contrato inteligente con el mismo nombre sino a un usuario/remitente de mensaje humano real. ¿Por qué el usuario A puede modificar el color si solo el usuario B conoce el color de entrada?
Actualicé mi publicación original, por lo que tal vez ahora sea más claro para usted cómo uso UserB como un contrato inteligente o su script para implementar/interactuar con él.

Esto es absolutamente posible. Simplemente podría cambiar su UserBun poco.

Puede enviar una dirección a un contrato y viceversa (pero debe asegurarse de que el contrato real en esa dirección coincida, de lo contrario se encontraría con una excepción de tiempo de ejecución):

contract UserB {

  address public owner;
  UserA public user; //userA wallet address
  string public color;

  function UserB(address _user) {
    owner = msg.sender;
    user = UserA(_user);
  }

  function delegateCall(string c) {
    user.setColor(c);
  }
}

Sin embargo, su ejemplo no es tan fácil: cuando llama UserA.setColordesde UserB.delegateCallentonces, msg.senderes la dirección de UserBy no usted (el origen de la transacción). Podría usar, tx.originpero realmente no debería hacerlo y es posible que se elimine en versiones futuras . Puede verificar cómo se maneja esto generalmente en billeteras/tokens, por ejemplo, aquí eche un vistazo a approvey transferFrom. Podría, por ejemplo, establecer su propiedad UserBcomo propietario de UserAcreando una instancia de UserAen el constructor de UserB.

Maldita sea, mientras esperaba una respuesta, leí tx.originy tenía buenas esperanzas, tal vez incluso pensé "esto es, esto es lo que realmente necesito" . Pero ahora, estoy tan confundido, porque quiero exactamente lo contrario de lo que me dijiste. UserBdebe ser el "esclavo" de UserA.