1

Delegatecall can be used in either solidity or assembly.

In solidity, I can get it working with the following code:

bytes memory payload = abi.encodeWithSignature ("get_value(uint16,uint16)", var1, var2);
bool success;
bytes memory result;
(success, result) = my_contract.delegatecall (payload);

The above code calls my_contract.get_value(var1, var2).

How can I do the same thing in assembly? The function is defined like this:

delegatecall (gas, address, in, insize, out, outsize)

If I am in an assembly block, assuming address is supposed to be the address of the contract that contains the function being called, how would it ever know which function I intend to call? When I do it using solidity, it knows this from the encoded payload data that I pass into it. But the assembly function only takes an address. And it always returns 0.

I have not been able to find any good documentation for EVM assembly anywhere on the Internet.

EDIT: Below is one of the attempts I made, using an example from the Internet.

let freememstart := mload (0x40)
calldatacopy (freememstart, 0, calldatasize ())
success := delegatecall (not (0), my_contract, freememstart, calldatasize (), freememstart, 32)

EDIT 2: Another attempt I made, not using calldatacopy.

bytes memory payload = abi.encodeWithSignature ("get_value(uint16,uint16)", var1, var2);
uint payload_size = payload.length * 8;

assembly
{
    let freememstart := mload (0x40)

    mstore (0x40, add (freememstart, payload_size))
    mstore (freememstart, payload)

    let output := mload (0x40)
    mstore (0x40, add (output, 0x20))

    success := delegatecall (not (0), contract_addr, freememstart, payload_size, output, 0x20)
}
7
  • What code did you write? What error did you get?
    – Ismael
    Aug 9, 2020 at 3:45
  • I edited the post to show one of the attempts I made to call it in assembly. I can't see how it could possibly work though, because it has no way of knowing which function I intend to call. I don't get an error message; it returns 0. But I don't get the expected result either. The same thing happens when I try to use call or staticcall in assembly. There is no place to pass it the function hash.
    – Zephyrus
    Aug 9, 2020 at 17:07
  • Does the delegatecall work if used from solidity, ie without using assembly? Remember that if the method do not exists the fallback function will be executed instead, so a bad call might appear to work.
    – Ismael
    Aug 10, 2020 at 5:26
  • Yes, when I call it in solidity, it works.
    – Zephyrus
    Aug 10, 2020 at 17:22
  • The code passes message's calldata to delegatecall as input. Don't you want to pass the payload instead?
    – Ismael
    Aug 10, 2020 at 17:29

2 Answers 2

2

A couple of things you should be aware of:

  • Don't need to copy memory to the free pointer, you can use any memory pointer. The EVM will copy the memory to the target so it will not be modified.
  • The first 32 bytes from payload is the array's length. When passing that memory probably want to skip those bytes. That is the reason behind add(payload, 32).

Something like this should work in your case

bytes memory payload = abi.encodeWithSignature ("get_value(uint16,uint16)", var1, var2);
uint ret;
bool success;

assembly
{
    let output := mload (0x40)
    success := delegatecall(gas(),
        contract_addr,
        add(payload, 32),
        mload(payload),
        output,
        0x20)
    ret := mload(output)
}

1

I suggest you look the existing proxy contracts like OpenZeppelin's Proxy as an example:

https://github.com/OpenZeppelin/openzeppelin-labs/blob/master/upgradeability_using_eternal_storage/contracts/Proxy.sol

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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