跳到主要内容

call/delegatecall

call

用来与其他合约进行交互,返回值为 (bool success, bytes memory data) , success 表示调用是否成功,data 表示调用的函数的返回值

使用方式如下:

contractAddress.call(abi.encodeWithSignature("函数名(...参数类型)", ...params));

如果调用的合约方法是可交易的,则可以指定发送的 eth 数量

contractAddress.call{ value: _value, gas: _gas }(abi.encodeWithSignature(...))

例如

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Receiver {
uint256 private _value = 0;
event Received(address caller, uint amount, string message);
fallback() external payable {
emit Received(msg.sender, msg.value, "Fallback was called");
}
// 设置 _value 的值
function store(uint256 newValue) public payable returns(uint){
_value = newValue;
emit Received(msg.sender, msg.value);
return newValue + 1;
}
// 读取 _value 的值
function retrieve() public view returns (uint256) {
return _value;
}
}

contract Caller {
event Response(bool success, bytes data);

function testCall(address payable _addr) public payable {
// 调用 Receiver 合约的 store 函数,修改 Receiver 中的变量 _value 的值为88
(bool success, bytes memory data) = _addr.call{value: msg.value}(
abi.encodeWithSignature("store(uint256)", 88)
);
emit Response(success, data);
}
}

delegatecall

委托调用,顾名思义就是委托别人去调用,和 call 的区别见下图

通过 delegatecall 调用合约 B 函数时,由于此时上下文是处于合约 A 的,所以函数执行所影响的状态是合约 A 的。

调用方式和 call 类似

contractAddress.delegatecall(abi.encodeWithSignature("函数名(...参数类型)", ...params));

示例:

contract Receiver {
uint256 private _value = 0;

function store(uint256 newValue) public {
_value = newValue;
}

function retrieve() public view returns (uint256) {
return _value;
}
}

contract Caller {
uint256 public _value = 0;

function testDelegateCall(address payable _addr) public payable {
// 通过 delegatecall 调用 Receiver 合约中的 store方法时
// 由于 Receiver 合约此时所处的上下文是在 Caller 合约中。所以对 _value 的赋值是 Caller 中的。
// 导致的最终结果就是 Receiver 中 _value 值仍为0, Caller 中 _value 值为88
(bool success, ) = _addr.delegatecall(
abi.encodeWithSignature("store(uint256)", 88)
);
}
}