call是address类型的低级成员函数,它用来与其他合约交互。它的返回值为(boll,data),分别对应call是否成功以及目标函数的返回值。
1.call是solidity官方推荐通过触发fallback或receive函数发送ETH的方法。
2.当我们不知道对方合约源代码或ABI,就没法生成合约变量;这是我们可以通过call调用对方合约的函数
call的使用格式:
目标合约地址.call(二进制编码);
abi.encodeWithSignature("函数签名", 逗号分隔的具体参数)
函数签名为“函数名(逗号分隔的参数类型)”。例如
abi.encodeWithSignature("f(uint256,address)", _x, _addr)
。
另外call
在调用合约时可以指定交易发送的ETH
数额和gas
:
目标合约地址.call{value:发送数额, gas:gas数额}(二进制编码);
从代码来看:
contract OtherContract {
uint256 private _x = 0; // 状态变量x
// 收到eth的事件,记录amount和gas
event Log(uint amount, uint gas);
fallback() external payable{}
// 返回合约ETH余额
function getBalance() view public returns(uint) {
return address(this).balance;
}
// 可以调整状态变量_x的函数,并且可以往合约转ETH (payable)
function setX(uint256 x) external payable{
_x = x;
// 如果转入ETH,则释放Log事件
if(msg.value > 0){
emit Log(msg.value, gasleft());
}
}
// 读取x
function getX() external view returns(uint x){
x = _x;
}
}
这个合约包含一个状态变量x,一个在收到ETH时触发的事件Log,三个函数:
getBalance():返回合约ETH余额
setX():external payable函数,可以设置x的值,并向合约发送ETH
getX():读取x的值
利用call调用目标函数
1.Response事件:利用call合约调用目标合约函数。要定义Response事件,输出call返回success和data,可以方便我们返回值。
event Response(bool success, bytes data);
2.调用setX函数
我们定义callSetX
函数来调用目标合约的setX()
,转入msg.value
数额的ETH
,并释放Response
事件输出success
和data
:
function callSetX(address payable _addr, uint256 x) public payable {// call setX(),同时可以发送ETH
(bool success, bytes memory data) = _addr.call{value: msg.value}(
abi.encodeWithSignature("setX(uint256)", x)
);
emit Response(success, data); //释放事件
}
接下来我们调用callSetX
把状态变量_x
改为5,参数为OtherContract
地址和5
,由于目标函数setX()
没有返回值,因此Response
事件输出的data
为0x
,也就是空。
3.调用getX函数
下面我们调用getX()
函数,它将返回目标合约_x
的值,类型为uint256
。我们可以利用abi.decode
来解码call
的返回值data
,并读出数值。
function callGetX(address _addr) external returns(uint256){// call getX()
(bool success, bytes memory data) = _addr.call(
abi.encodeWithSignature("getX()")
);
emit Response(success, data); //释放事件
return abi.decode(data, (uint256));
}
经过abi.decode,最终返回值为5。
4.调用不纯在的函数
function callNonExist(address _addr) external{ // call getX()
(bool success, bytes memory data) = _addr.call(
abi.encodeWithSignature("foo(uint256)")
);
emit Response(success, data); //释放事件
}
我们call
了不存在的foo
函数。call
仍能执行成功,并返回success
,但其实调用的目标合约fallback
函数。