WTF Solidity: 23. Delegatecall
Twitter: @0xAA_Science | @WTFAcademy_
Community: Discord|Wechat|Website wtf.academy
Codes and tutorials are open source on GitHub: github.com/AmazingAng/WTFSolidity
delegatecall
delegatecall is similar to call, is a low level function in Solidity. delegate meas entrust/represent, so what does delegatecallentrust?
When user A call contract C via contract B, the executed functions are from contract C, 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.
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.
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 is from the businessman.
The syntax of delegatecall is simimar 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 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 cotnract 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:
Proxy Contract: separating the storage part and logic part of smart contract:proxy contractis used to store all related variables, and also store the address of logic contract; all functions are stored in thelogic contract, and called via delegatecall. When upgrading, you only need to redirectproxy contractto a newlogic contract.- 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 Initizalization Contract B
First, contract B must have the same state variable layout as target contract C, 2 variabels 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.
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
- First we deploy contract B and contract C
- After deployment, check the initial value of state variables in contract
C, also the initial value of state variables in contractB.
- Next, call
callSetVarsin contractBwith arguments of contractC's address and10
- After execution, the state variables in contract
Care changed:numis changed to 10,senderis changed to contract B's address
- Next, we call
delegatecallsetVarsin contractBwith arguments of contractC's address and100
- Because of
delegatecall, the execution context is contractB. Afte execution, the state variables of contractBare changed:numis changed to 100,senderis changed to your wallet's address. The state variables of contractCare unchanged.
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 of 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 is proxy contract and EIP-2535 Diamons.