1

I am trying, for learning purposes only, to make a mutable smart contract, but I couldn't find anything useful on stackexchange because everything I found was using pragma solidity ^0.4.x;

So, I have 2 contracts

pragma solidity ^0.5.0;

contract first {
    uint calculationResult;
    address public linkedContract;

    function calculate(uint firstNumber, uint secondNumber) public {
        calculationResult = firstNumber * secondNumber;
    }
}

and the other

pragma solidity ^0.5.0;

contract second {
    uint calculationResult;
    address public linkedContract;

    function changeContract(address newContract) public {
        linkedContract = newContract;
    }

    function delegatedCalculation(uint firstNumber, uint secondNumber) public {

    }
}

I am trying to make on the second contract the function delegatedCalculation to use the function from the first contract, so later I can change this to another calculation, I can for example create a third contract where the calculation is a sum, not a multiplication, I have tried 2 ways to do that, the way every tutorial and guide and even stackexchange answer I found does this is

linkedContract.delegatecall(bytes4(keccak256("calculate(uint, uint)")), firstNumber, secondNumber);

but when I try to compile on remix I get a not very helpful error

browser/firstContract.sol:20:9: TypeError: Wrong argument count for function call: 3 arguments given but expected 1. This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.`

And upon reading about that abi.encode I have found that if I do

linkedContract.call(abi.encode("calculate(uint, uint)", firstNumber, secondNumber));

it compiles, but then if I execute it nothing happens. I have created a function

function getReturnedView() public view returns(uint){
    return returnedValue;
}

I compile and run on Remix EVM the first, get it's address, compile the second, run it, execute the changeContract to give it the address of the first one, call the delegatedCalculation, and when I call the getReturnedView() it says just 0: uint256: 0

I can't figure out how to make it work.

2

The delegatecall arguments are incorrect, you should use:

function delegatedCalculation(uint firstNumber, uint secondNumber) public {
        bool status;
        bytes memory result;
        (status, result) = linkedContract.delegatecall(abi.encodePacked(bytes4(keccak256("calculate(uint256,uint256)")), firstNumber, secondNumber));
    }

Or simply you can write the following if there is no return value, as this is the case for your calculate, method:

function delegatedCalculation(uint firstNumber, uint secondNumber) public {
        (bool status,) = c2.delegatecall(abi.encodePacked(bytes4(keccak256("calculate(uint256,uint256)")), firstNumber, secondNumber));
}

Note that:

  1. To calculate the selector you should use uint256 not uint.

  2. Use abi.encodePacked to pack all the parameters into a bytes type.

  3. delegatecall will return a bool indicating success or failure and a bytes with the returned value (if any)

Test it in remix and it works. Hope this helps you.

EDIT: The clarification made in the docs is in the breaking changes section, this is probably what you were looking for.

  • The functions .call(), .delegatecall(), staticcall(), keccak256(), sha256() and ripemd160() now accept only a single bytes argument. Moreover, the argument is not padded. This was changed to make more explicit and clear how the arguments are concatenated. Change every .call() (and family) to a .call("") and every .call(signature, a, b, c) to use .call(abi.encodeWithSignature(signature, a, b, c)) (the last one only works for value types). Change every keccak256(a, b, c) to keccak256(abi.encodePacked(a, b, c)). Even though it is not a breaking change, it is suggested that developers change x.call(bytes4(keccak256("f(uint256)"), a, b) to x.call(abi.encodeWithSignature("f(uint256)", a, b)).
    • Functions .call(), .delegatecall() and .staticcall() now return (bool, bytes memory) to provide access to the return data. Change bool success = otherContract.call("f") to (bool success, bytes memory data) = otherContract.call("f").

You can check sample for call and delegatecall with solidity 0.5 here: https://gist.github.com/Muhammad-Altabba/55ac6974ac9396d723fdfd792d51eb5d

  • OMG it works, can you link me something from the documentation or somewhere else I can read about it? I am a beginner and I am struggling to find content to find how exactly a few things work because apparently there is too much lower lever stuff, more than other programming languages I am used to. Thank you very very much – Igor Marcos Jan 2 at 17:18
  • This answer is incorrect. Both examples should be using abi.encodeWithSelector(), not abi.encodePacked(). The reason you must use abi.encodeWithSelector() is because you including the function selector (bytes4(keccak256("calculate(uint256,uint256)"))) along with the parameters. abi.encodePacked() encodes parameters only, not the selector. – shane Sep 2 at 0:03

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.