Calling the Function of Another Contract in Solidity

When we write Smart Contracts, we can write it in such a way that they can interact with existing deployed contracts. This feature is very powerful as it allows code reusability, ie treating deployed contracts like libraries. There has been a lot of effort done in this space but is still controversial at the time of writing. For example what would happen if the reused contracts are faulty (just like what happened to parity multi-sig wallet hack)?

In this article, I am not debating about the immutability of deployed contracts or whether we should or should not interact with deployed contracts. Instead, I will be focusing on the different techniques to call functions of deployed contracts. I can see some use cases for it and I’ll leave it up to the readers to implement what they believe in.

Let’s say we have deployed a very simple contract called “Deployed” that allows user to set a variable.

pragma solidity ^0.4.18;
contract Deployed {
    uint public a = 1;

function setA(uint _a) public returns (uint) {
a = _a;
return a;
}

}

and we want to deploy another contract later called “Existing” to change the variable of “a” in the “Deployed” contract.

pragma solidity ^0.4.18;
contract Deployed {

function setA(uint) public returns (uint) {}

function a() public pure returns (uint) {}

}
contract Existing  {

Deployed dc;

function Existing(address _t) public {
dc = Deployed(_t);
}

function getA() public view returns (uint result) {
return dc.a();
}

function setA(uint _val) public returns (uint result) {
dc.setA(_val);
return _val;
}

}

We do not need the full implementation of the “Deployed” contract, but rather just the function signatures as required by the ABI. Since we have the address of the “Deployed” contract, we could initialised the “Existing” contract with the address and interact with the “Deployed” contract using the existing setA and getA functions accordingly.

This is easy and actually the recommended way to interact with deployed contracts. However, what if we don’t have the ABI of the deployed contract? We can still call the “setA” function of the deployed contract.

pragma solidity ^0.4.18;
contract ExistingWithoutABI  {

address dc;

function ExistingWithoutABI(address _t) public {
dc = _t;
}

function setA_Signature(uint _val) public returns(bool success){
require(dc.call(bytes4(keccak256("setA(uint256)")),_val));
return true;
}
}

Function signatures are 4 bytes long and the formula to generate it is to hash it with the keccak256 function, like so:

bytes4(keccak256(“setA(uint256)”))

We could pass a value to setA inside the call method. However, since the call (as well as delegatecall) method simply passes value over to the contract address and will not get any returned value, it doesn’t know if setA has done its job correctly or not unless we check out the state of the “Delegate” contract.

What if we want to get the returned value from setA? Unfortunately, there is no way to do that unless we use solidity’s assembly code. Are you ready?

pragma solidity ^0.4.18;
contract ExistingWithoutABI  {

address dc;

function ExistingWithoutABI(address _t) public {
dc = _t;
}

function setA_ASM(uint _val) public returns (uint answer) {

bytes4 sig = bytes4(keccak256("setA(uint256)"));
        assembly {
            // move pointer to free memory spot
let ptr := mload(0x40)
            // put function sig at memory spot
mstore(ptr,sig)
            // append argument after function sig
mstore(add(ptr,0x04), _val)

let result := call(
15000, // gas limit
sload(dc_slot), // to addr. append var to _slot to access storage variable
0, // not transfer any ether
ptr, // Inputs are stored at location ptr
0x24, // Inputs are 36 bytes long
ptr, //Store output over input
0x20) //Outputs are 32 bytes long

if eq(result, 0) {
revert(0, 0)
}

answer := mload(ptr) // Assign output to answer var
mstore(0x40,add(ptr,0x24)) // Set storage pointer to new space
}
    }
}

Solidity’s assembly code starts with the “assembly” keyword and wrapped in {}. I hope my comments in the code is clear. To get the returned value of setA without ABI, we have to understand how memory works in the EVM. Free memory is available at the 64th Byte (0x40), so we first move our memory pointer there. Then, we append the hexadecimals of the function signature and its argument sequentially at that spot. The function signature is 4 Bytes (0x04) and the argument is 32 Bytes (0x20), so we have 36 Bytes in total (0x24).

Once done, we do the magic “call” which stores the result back in the 64th Byte spot and returns a boolean, ie 1 or 0. The transaction will revert if call fails (returns a 0). Assuming everything is successful, we return the value at the 64th Byte spot, which is the answer we want.

Try the code in remix and see for yourself.

Happy coding.

Like what you read? Give Bernard Peh a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.