Solidity 102

1. Overloading
2. Library
3. Import
4. Receive ETH
5. Sending ETH
6. Interact with Contract
7. Call
8. Delegatecall
9. Create
10. Create2
11. DeleteContract
12. ABI Encoding and Decoding
13. Hash
14. Function Selector
15. Try Catch
.
Delegatecall

delegatecall is similar to call, is a low level function in Solidity. delegate means entrust/represent, so what does delegatecall entrust?

When user A call contract C via contract B, the executed functions are from contract C, and the execution context (the environment including state and variable) is in contract C: msg.sender is contract B's address, and if state variables are changed due to function call, the affected state variables are in contract C.

execution context of call

And when user A delegatecall contract C via contract B, the executed functions are from contract C, the execution context is in contract B: msg.sender is user A's address, and if state variables are changed due to function call, the affected state variables are in contract B.

execution context of delegatecall

You can understand it like this: a rich businessman entrusts his asset (state variables) to a VC (functions of target contract) for management. The executed functions are from the VC, but the state variables get changed from the businessman.

The syntax of delegatecall is similar to call:

targetContractAddress.delegatecall(binary code);

the binary code is generated by abi.encodeWithSignature:

abi.encodeWithSignature("function signature", parameters separated by comma)

function signature is "functionName(parameters separated by comma)". For example, abi.encodeWithSignature("f(uint256,address)", _x, _addr)。

Unlike call, delegatecall can specify the value of gas when calling a smart contract, but the value of ETH can't be specified.

Attention: using delegatecall could incur risk, make sure the storage layout of state variables of current contract and target contract is same, and target contract is safe, otherwise could cause loss of funds.

delegatecall use cases?

Currently, there are 2 major use cases for delegatecall:

  1. Proxy Contract: separating the storage part and logic part of smart contract: proxy contract is used to store all related variables, and also store the address of logic contract; all functions are stored in the logic contract, and called via delegatecall. When upgrading, you only need to redirect proxy contract to a new logic contract.
  2. EIP-2535 Diamonds: Diamond is a standard that supports building modular smart contract systems that can scale in production. Diamond is a proxy contract with multiple implementation contracts. For more information, check Introduction to EIP-2535 Diamonds.

delegatecall example

Call mechanism: you (A) call contract C via contract B.

Target Contract C

First, we create a target contract C with 2 public variables: num and sender which are uint256 and address respectively; and a function which sets num based on _num, and set sender as msg.sender.

// Target contract C
contract C {
    uint public num;
    address public sender;

    function setVars(uint _num) public payable {
        num = _num;
        sender = msg.sender;
    }
}

Call Initialization Contract B

First, contract B must have the same state variable layout as target contract C, 2 variables and the order is num and sender.

contract B {
    uint public num;
    address public sender;

Next, we use call and delegatecall respectively to call setVars from contract C, so we can understand the difference better.

The function callSetVars calls setVars via call. callSetVars has 2 parameters, _addr and _num, which correspond to contract C's address and the parameter of setVars.

// Calling setVars() of contract C with call, the state variables of contract C will be changed
    function callSetVars(address _addr, uint _num) external payable{
        // call setVars()
        (bool success, bytes memory data) = _addr.call(
            abi.encodeWithSignature("setVars(uint256)", _num)
        );
    }

While function delegatecallSetVars calls setVars via delegatecall. Similar to callSetVars, delegatecallSetVars has 2 parameters, _addr and _num, which correspond to contract C's address and the parameter of setVars.

// Calling setVars() of contract C with delegatecall, the state variables of contract B will be changed
    function delegatecallSetVars(address _addr, uint _num) external payable{
        // delegatecall setVars()
        (bool success, bytes memory data) = _addr.delegatecall(
            abi.encodeWithSignature("setVars(uint256)", _num)
        );
    }
}

Verify on Remix

  1. First we deploy Contract B and contract C

deploy.png

  1. After deployment, check the initial value of state variables in contract C, also the initial value of state variables in contract B.

initialstate.png

  1. Next, call callSetVars in contract B with arguments of contract C's address and 10

call.png

  1. After execution, the state variables in contract C are changed: num is changed to 10, sender is changed to contract B's address

resultcall.png

  1. Next, we call delegatecallsetVars in contract B with arguments of contract C's address and 100

delegatecall.png

  1. Because of delegatecall, the execution context is contract B. After execution, the state variables of contract B are changed: num is changed to 100, sender is changed to your wallet's address. The state variables of contract C are unchanged.

resultdelegatecall.png

Summary

In this lecture, we introduce another low-level function in Solidity, delegatecall. Similar to call, delegatecall can be used to call another contract; the difference between delegatecall and call is execution context, the execution context is C if B call C; but the execution context is B if B delegatecall C. The major use cases for delegatecall are proxy contract and EIP-2535 Diamonds.

PreviousNext