1

I have the following setup here, simplified, but still demonstrates my problem.

Let's assume I have a UserA and UserB which have a wallet and a smart contract respectively. Each User deploy his own smart contract.

UserA.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;
        }
    }

UserB.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";}
    } 
}

UserB.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);

My constrains:

  • Only UserA is allowed to modify color.
  • Only UserB is allowed to call setcolor() trough UserA ABI.

My questions:

  • Can a smart contract set a variable from an other smart-contract?
  • Is my idea feasible or is it completely unrealistic?

I know right now, that UserB is not allowed to call setColor() since msg.owner =/= UserB but to UserA.

Thank you for your valuable time and help.

  • I'm confused about the control you want A & B to have. A is an admin user who sets something and then B is a regular user who selects an option? Or something else? – Rob Hitchens - B9lab Feb 15 '17 at 3:56
1

I'm not certain this is what you want, but it might give you some ideas about how to structure things.

You only need to deploy the ColorPallet contract. Whatever address it deploys from will be the owner. Only the owner can extend the ever-growing list of possible color choices.

The ColorPallet is also a factory. Anyone can come along and use the newUser() function to deploy a User contract. Ownership of that contract is given to the address that asked for it. So the "user" has complete control of the "User".

The "user" can set a color. It needs to be in range and it's going to check. So if there are 3 colors, they are 0, 1 and 2. 3 or higher will throw. The admin can add more choices.

I have the User contract storing the selection locally, and also pushing the choice to the ColorPallet to show how the User can indeed set a value in a different contract. In this case, by sending a message.

Disclaimer: Not much testing. I just wanted to show contracts talking to each other and a pattern for controlling access to functions. ;-)

Hope it helps.

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;
  }

}

Deploy the ColorPallet and add a few colors:

enter image description here

Ask for a User and note the deployed address:

enter image description here

It's already deployed. Instantiate the contract at the deployed address.

enter image description here

Choose a valid (in range) color and see the storage updated.

enter image description here

Check the ColorPallet's storage. Put the User contract's address into the public getter function for the mapping:

enter image description here

  • Thanks a lot for your answer. I really like your idea that colorPallet is a factory. I'll see if I can adapt your ideas to my project. If not, I'll come back to you. – FrenchieiSverige Feb 15 '17 at 9:24
  • 1
    np. BTW. It really doesn't need "this" in the newUser() function. msg.sender would do it on the receiving constructor (in place of "pallet"). I did it the verbose way to make it possible to fiddle around and disambiguate all the msg.sender/owner stuff that's going on. Avoid tx.origin at all costs. It will bite. ;-) – Rob Hitchens - B9lab Feb 15 '17 at 9:45
  • Yeah I know, if you read my comment under @Sebastian 's answer, you will see that I was about to go with tx.origin. I've tried to understand your code yesterday, but I still need some help on these questions: - Is mapping(address => uint) public userColorChoices; only storing the lastest choice of the user or is it an array which has stored all choices from user since the beginnig? - What is the difference between return and returns? Thanks a lot. PS: I don't know, but maybe I should open a room to talk about this. – FrenchieiSverige Feb 16 '17 at 12:54
  • mapping is storing one latest choice for all users. userColorChoices[address] are bits organized so we can look them up by address keys. "returns" is setting up the function interface - outside {}. "return" is doing it - inside {}. – Rob Hitchens - B9lab Feb 16 '17 at 20:37
  • Correction: userHasSelectedColor[] is a bool so we can know if a returned val is deliberate or just a default 0. userColorSelected[user] is a uint meaning the most recent selection of a given user. – Rob Hitchens - B9lab Feb 16 '17 at 22:52
1

It is possible to connect two smart contracts with each other. At the beginning of the file you need to import the contract very similar to other languages. Then you can set the other contract as an object.

UserB.sol:


import "./UserA.sol";

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

Now you can call methods on "userA" like userA.setColor("blue");

  • Thanks a lot for your answer, but it does not fit my second criteria, which is only userB can call setColor(), and userA can only modify color. – FrenchieiSverige Feb 14 '17 at 15:59
  • Im confused. Why is User B allowed to call setColor() if he is not allowed to change the color??? – Max Binnewies Feb 14 '17 at 16:40
  • Okay, UserB deploys the smart contact B, and after that set the color. Only UserB knows the input color for A. That is why UserB needs to call UserA function to set the color. – FrenchieiSverige Feb 14 '17 at 16:51
  • Sorry, I still do not understand what you are planning to do. I think part of the confusion is that you call the smart contracts "UserA"/"UserB", but when you say "UserB" you dont always mean the smart contract with same name but an actual human user/message sender. Why can userA modify color if only userB knows the input color? – Max Binnewies Feb 14 '17 at 16:58
  • I updated my original post, so maybe it is now more clear for you how I use UserB as either a smart contract or its script in order to deploy/interact with it. – FrenchieiSverige Feb 14 '17 at 19:12
1

This is absolutely possible. You could simply change your UserB a bit.

You can cast an address to a contract and vice versa (but you have to make sure that the actual a contract at that address is actually matching else you'd run into a runtime exception):

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);
  }
}

Your example is not quite as easy though: when you call UserA.setColor from UserB.delegateCall then the msg.sender is the address of UserB and not you (the origin of the transaction). You could use tx.origin but your really should not do that and might be removed in future releases. You can check how this is generally being dealt with in wallets/tokens, e.g. here take a look at approve and transferFrom. You could e.g. set your UserB as the owner of UserA by creating an instance of UserA in the constructr of UserB.

  • Damn, while waiting an answer, I read about tx.origin and had good hope in it, maybe even thought "this it it, this is what I excalty need". But now, I'm so confused, because I want the exact opposite of what you told me. UserB should be the "slave" of UserA. – FrenchieiSverige Feb 14 '17 at 16:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.