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.solpragma 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)
Compile the conctracts:
solc --bin Child.sol Parent.sol
Deploy the contracts
- Copy the
Binary:
section forChild.sol
into Mist's contract deployCONTRACT BYTE CODE
section, and deploy - Repeat for the
Parent.sol
- Copy the
Get the ABI:
solc --abi Child.sol Parent.sol
Watch the Contracts:
- Copy the
Contract JSON ABI
section forChild.sol
into Mist'sWATCH CONTRACT
, along with the corresponding address - Repeat for
Parent.sol
- Copy the
Link the contracts
- Select the
Child
contract - Execute the function
setParent
with the address of the parent contract
- Select the
Set the value within Parent:
- Select the
Parent
contract - Execute the function
setValue
with 999, or something other than 0
- Select the
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
js
.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 anyChild
contracts that were deployed using the binary method.