0

I can't quite seem to figure out an issue preventing contract to contract function calls.
I'm feeling fairly confident it's something with how I'm deploying.

Preface: I have a Parent and Child contract, where the child can invoke calls of the parent. The child holds an address of the parent, that is set after deployment and should be updatable in-case a new Parent contract is deployed.

Contracts:

IParent.sol
pragma solidity >0.4.23 <0.5.0;

interface IParent {
    function setValue(int v) external;
    function getValue() external view returns (int);                                                         
    function getSender() external view returns (address);
}
Parent.sol
pragma solidity >0.4.23 <0.5.0;

import "./IParent.sol";

contract Parent is IParent {
    int value;

    function setValue(int v) public {
        value = v;
    }

    function getValue() external view returns (int) {
        return value;
    }

    function getSender() external view returns (address) {
        return msg.sender;
    }
}
Child.sol
pragma solidity >0.4.23 <0.5.0;

import "./IParent.sol";

contract Child {
    IParent parent;

    function setParent(address a) public {
        parent = IParent(a);
    }

    function getValue() external view returns (int value) {
        return parent.getValue();
    }

    function getSender() external view returns (address value) {
        return parent.getSender();
    }
}

Steps

(I'm using Mist to do the deployment to avoid any issues I may have introduced and try an isolate the issue. I also get the same outcome when deploying using web3js)

  1. Compile the conctracts:

    solc --bin Child.sol Parent.sol

  2. Deploy the contracts

    • Copy the Binary: section for Child.sol into Mist's contract deploy CONTRACT BYTE CODE section, and deploy
    • Repeat for the Parent.sol
  3. Get the ABI:

    solc --abi Child.sol Parent.sol

  4. Watch the Contracts:

    • Copy the Contract JSON ABI section for Child.sol into Mist's WATCH CONTRACT, along with the corresponding address
    • Repeat for Parent.sol
  5. Link the contracts

    • Select the Child contract
    • Execute the function setParent with the address of the parent contract
  6. Set the value within Parent:

    • Select the Parent contract
    • Execute the function setValue with 999, or something other than 0

From here, if you view the Child contract, both the value and msg.sender values reflect defaults.

I've also tried the below, within the Child:

function getValueSuccess() external view returns (bool) {
    return parentAddress.call(bytes4(keccak256("getValue()")));
}

This returns true, but the below always returns 0:

function getDummyValue() external view returns (int) {                                                       
    parent.getValue();                                                                                   
    return 999;                                                                                          
} 

Also, using the below assembly works:

function getValue() external view returns (int value) {
    address addr = address(parent);
    bytes4 sig = bytes4(keccak256("getValue()"));

    assembly {
       let o := mload(0x40) // Empty storage pointer
       mstore(o,sig)        // Push function signature to memory (function signature is 4 bytes/0x04)
       //mstore(add(ptr,0x40), someInt32Argument); // Append function argument after signature
       // From here, the call data size (input) would be functiona signature size + sum(argument size)
       //   4bytes + 0 in this case, or 4bytes + 32bytes in the above commented `mstore`

       let success := call(
           15000,           // Gas limit
           addr,            // To address
           0,               // No ether to transfer
           o,               // Input location ptr
           0x04,            // Input size (0)
           o,               // Store oputput over input
           0x20)            // Output size (32 bytes)

       value := mload(o)
       mstore(0x40,add(o,0x04))
    }
}

It seems like the cast from parent address to IParent isn't working, as all calls seem to fail, unless we use assembly or address.call(), which I'm assuming just send the message to the address, avoiding the type constraint (like using reflection)

Also, if I just merge all the contracts into a single file, copy and paste that into Mist's SOLIDITY CONTRACT SOURCE CODE box, deploying each of them from the drop-down. Everything works as expected.

What step(s) am I missing in the above to get this working as expected?


Update 1

If I deploy a working set of contracts, using Mist to deploy source code, then deploy the Parent using the binary in Mist, I'm able to successfully link/call it from the child.
So it's looking like something specifically with the Child deployment.

Update 2

This only works with the above test contract. Once I moved this into my actual contract, I'm presented with the same problem

I ran across this question, where @smarx had responded with:

FYI, you can do this instead (and call it from a client the same way):
NonFungibleToken public nft;

function setNFTAddress(NonFungibleToken _nft) onlyOwner{
nft = _nft;
}

For whatever reason, this seems to work, whereas just casting the address to the contract doesn't??

Why would this work:

function setParent(Parent p) external {
    parent = p;
}

but not this:

function setParent(address addr) external {
    parent = Parent(addr);
}

...and even more confusing, why would they both work when deploying using the source code, in Mist, and only the first work when using the binaries?

versions:

  • geth: 1.8.13-stable
  • solc: 0.4.24+commit.e67f0147.Linux.g++ (Docker image: ethereum/solc:stable)
  • mist: 0.10.0
  • Did you deploy this to a public network (mainnet, Ropsten, etc.)? If so, please link to the contracts (parent and child). – user19510 Aug 12 '18 at 21:26
  • The fact that this works if you compile in Mist but not if you just paste in the byte code suggests that there might be a problem with the latter workflow. (Maybe compilation isn't working or you're deploying the wrong byte code or Mist is broken when using byte code.) I don't have Mist, so I'm unable to attempt a repro. – user19510 Aug 12 '18 at 21:28
  • you can enable tracing an trace all the instructions being executed, maybe this would help – Nulik Aug 12 '18 at 22:31
  • @smarx, just a local/private instance. I was originally deploying using web3js, but ended up using Mist to rule out anything I may have borked with my js. – Justin Aug 13 '18 at 0:03
  • It seems like it's specific to the Child contract. Just re-deployed the contracts, using the source code method in Mist, instead of binaries. Then linked the child to one of the prior parent contracts that was deployed using the binary method. That seems to work, it's just any Child contracts that were deployed using the binary method. – Justin Aug 13 '18 at 0:41
0

Enable new EVM features by adding Byzantium to the config section of your genesis.json:

  "config": {
    "chainId": 1234,
    "homesteadBlock": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "eip160Block": 0,
    "byzantiumBlock": 0
},

Thanks to @chriseth, of Solidity, for helping find this

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.