Skip to content

Commit

Permalink
delegatecall example
Browse files Browse the repository at this point in the history
  • Loading branch information
XieJunhua committed May 8, 2024
1 parent a21e0ab commit 76ac55d
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 0 deletions.
52 changes: 52 additions & 0 deletions src/examples/DelegateCall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract B {
uint256 public num;
address public sender;
uint256 public value;

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

function collate_propagate_storage(bytes16 data) public payable {
num = 1e5;
}
}

contract A {
uint256 public num;
address public sender;
uint256 public value;

function setVarsDelegateCall(address _contract, uint256 _num) public payable {
(bool success, bytes memory data) = _contract.delegatecall(abi.encodeWithSignature("setVars(uint256)", _num));
}

function setVarsCall(address _contract, uint256 _num) public payable {
(bool success, bytes memory data) = _contract.call(abi.encodeWithSignature("setVars(uint256)", _num));
}

function delegeCallBurn(address _contract, uint256 _num) public payable {
bytes16 h = 0x20010db8000000000000000000000001;
(bool success, bytes memory data) = _contract.delegatecall(abi.encodeWithSignature("burn(uint256)", h));
}
}

contract C {
uint256 public value;
uint256 public num;

address public sender;

function setVarsDelegateCall(address _contract, uint256 _num) public payable {
(bool success, bytes memory data) = _contract.delegatecall(abi.encodeWithSignature("setVars(uint256)", _num));
}

function setVarsCall(address _contract, uint256 _num) public payable {
(bool success, bytes memory data) = _contract.call(abi.encodeWithSignature("setVars(uint256)", _num));
}
}
94 changes: 94 additions & 0 deletions test/examples/DelegateCall.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import { Test, console2 } from "forge-std/src/Test.sol";
import { A, B, C } from "../../src/examples/DelegateCall.sol";

contract DelegateCallTest is Test {
A a;
B b;
C c;
address alice = makeAddr("alice");

function setUp() public {
a = new A();
b = new B();
c = new C();
}

function testCall() public {
vm.startPrank(alice);
vm.deal(alice, 1 ether);
a.setVarsCall{ value: 1 ether }(address(b), 11); // call setVars by call
assertEq(b.num(), 11);
assertEq(b.sender() == address(a), true); // the msg.sender in contract b is a, is not alice
assertEq(b.value(), 0); // contract b will not receive any ether

vm.stopPrank();
}

function testDelegateCall() public {
vm.startPrank(alice);
vm.deal(alice, 1 ether);
a.setVarsDelegateCall{ value: 1 ether }(address(b), 11); // call setVars by call
console2.log("value from contact b ----------");
console2.log(b.num());
console2.log(b.sender());
console2.log(b.value());
console2.log("------------");
// all the value in the contact b will not change
// 实际上仅仅改变的是contract a中的值,相当于是把合约b中的function提到a中来执行,
// 所以就需要两个合约中的存储结构是一致的。因为合约a执行合约b中的方法的时候对变量进行修改是按照变量在b中的槽位来进行修改

assertEq(a.num(), 11);
assertEq(a.sender(), alice); // the msg.sender in contract b is a, is not alice
assertEq(a.value(), 1 ether); // contract b will not receive any ether
vm.stopPrank();
}

function testDelegateCallNameConfict() public {
vm.startPrank(alice);
vm.deal(alice, 1 ether);
a.delegeCallBurn{ value: 1 ether }(address(b), 11); // call setVars by call
console2.log("value from contact b ----------");
console2.log(b.num());
console2.log(b.sender());
console2.log(b.value());
console2.log("------------");
// all the value in the contact b will not change
// 实际上仅仅改变的是contract a中的值,相当于是把合约b中的function提到a中来执行,
// 所以就需要两个合约中的存储结构是一致的。因为合约a执行合约b中的方法的时候对变量进行修改是按照变量在b中的槽位来进行修改

// 可以通过合约代码看到,用户可能以为调用的是burn方法,其实通过代理调用的并不是burn方法,而是collate_propagate_storage(bytes16)
// 这是因为函数签名发生了冲突,函数签名是取该函数hash的前4位,而delegatecall是通过函数签名的方式去对应合约地址中寻找函数,导致找到的函数有可能不是我们想要的
console2.log("value from contact b ----------");
console2.log(a.num());
console2.log(a.sender());
console2.log(a.value());
console2.log("------------");

vm.stopPrank();
}

function testCDelegateCall() public {
vm.startPrank(alice);
vm.deal(alice, 1 ether);
c.setVarsDelegateCall{ value: 1 ether }(address(b), 11); // call setVars by call
console2.log("value from contact b ----------");
console2.log(b.num());
console2.log(b.sender());
console2.log(b.value());
console2.log("------------");
// all the value in the contact b will not change
// 实际上仅仅改变的是contract a中的值,相当于是把合约b中的function提到a中来执行,
// 所以就需要两个合约中的存储结构是一致的。因为合约a执行合约b中的方法的时候对变量进行修改是按照变量在b中的槽位来进行修改
// 当合约C的布局和合约b的布局不一致的时候,就会出现问题,因为合约C中的变量在b中的槽位不一致
// delegatecall执行的方法会按照合约b中的槽位给c中的变量进行复制
console2.log("value from contact c ----------");
console2.log(c.num());
console2.log(c.sender());
console2.log(c.value());
console2.log("------------");
vm.stopPrank();
}
}

0 comments on commit 76ac55d

Please sign in to comment.