第5节:call&staticcall
小白入门:https://github.com/dukedaily/solidity-expert ,欢迎star转发,文末加V入群。
职场进阶: https://dukeweb3.com
call是一种底层调用合约的方式,可以在合约内调用其他合约,call语法为:
//(bool success, bytes memory data) = addr.call{value: valueAmt, gas: gasAmt}(abi.encodeWithSignature("foo(string,uint256)", 参数1, 参数2)
其中:
1. success:执行结果,一定要校验success是否成功,失败务必要回滚
2. data:执行调用的返回值,是打包的字节序,需要解析才能得到调用函数的返回值(后续encode_decode详解)
当调用fallback方式给合约转ether的时候,建议使用call,而不是使用transfer或send方法
(bool success, bytes memory data) = addr.call{value: 10}("")
对于存在的方法,不建议使用call方式调用。
(bool success, bytes memory data) = _addr.call(abi.encodeWithSignature("doesNotExist()"));
注意,当调用的方法不存在,且合约又未实现fallback时,交易会调用成功,但是第一个参数为:false,所以使用call调用后一定要检查success状态
完整demo:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Receiver {
event Received(address caller, uint amount, string message);
fallback() external payable {
emit Received(msg.sender, msg.value, "Fallback was called");
}
function foo(string memory _message, uint _x) public payable returns (uint) {
emit Received(msg.sender, msg.value, _message);
return _x + 1;
}
}
contract ReceiverWithOutFallback {
event Received(address caller, uint amount, string message);
function foo(string memory _message, uint _x) public payable returns (uint) {
emit Received(msg.sender, msg.value, _message);
return _x + 1;
}
}
contract Caller {
event Response(bool success, bytes data);
function testCallFoo(address payable _addr) public payable {
// You can send ether and specify a custom gas amount
(bool success, bytes memory data) = _addr.call{value: msg.value, gas: 5000}(
abi.encodeWithSignature("foo(string,uint256)", "call foo", 123)
);
emit Response(success, data);
}
// Calling a function that does not exist triggers the fallback function.
function testCallDoesNotExist(address _addr) public {
(bool success, bytes memory data) = _addr.call(
abi.encodeWithSignature("doesNotExist()")
);
emit Response(success, data);
}
}
STATICCALL:
Since byzantium staticcall can be used as well. This is basically the same as call, but will revert if the called function modifies the state in any way.
与CALL相同,但是不允许修改任何状态变量,是为了安全🔐考虑而新增的OPCODE
在Transparent模式的代理合约逻辑中,就使用了staticcall,从而让proxyAmin能够免费的调用父合约的admin函数,从而从slot中返回代理合约的管理员。这部分会在合约升级章节介绍。
function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("admin()")) == 0xf851a440 (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); require(success); return abi.decode(returndata, (address)); }