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)
);
}
}