23-03-04

First Post:

Last Update:

2023-03-04

call方法以及abi编解码

了解以下编码

更多信息->查看文章

abi.encodeCall

bytes memory data = abi.encodeCall(MyContract.myFunction.selector, [arg1, arg2]);

abi.encodeCall是Solidity中的一个函数,它可以用来对函数调用进行编码。它接受一个函数选择器和一组参数,并返回一个字节数组,该字节数组可以用来调用指定的函数

优点:有类型检查(不是字符串形式)

abi.encodePacked

将给定参数根据其所需最低空间编码。它类似 abi.encode,但是会把其中填充的很多 0 省略

比如,只用 1 字节来编码 uint 类型。当你想省空间,并且不与合约交互的时候,可以使用 abi.encodePacked,例如算一些数据的 hash(可作数据比较) 时

abi.encodeWithSignature

与 abi.encode 功能类似,只不过第一个参数为函数签名,比如”foo(uint256,address)”。当调用其他合约的时候可以使用
函数签名:bytes4(keccak(“函数名(函数类型)”))

abi.endeWithSelector

Selector

相关文章->HERE

selector 可以通过两种方式获取,一种是查询 function.selector,另一种就是自己计算(如下)

return bytes4(keccak256("函数名(参数类型)") == return 合约名(address).函数名.selector

与 abi.encodeWithSignature 功能类似,只不过第一个参数为函数选择器,为函数签名 Keccak 哈希的前 4 个字节

abi.decode

abi.decode 用于解码 abi.encode 生成的二进制编码,将它还原成原本的参数。

(bool b, uint ui, string memory) = abi.decode(data,(bool, uint, string));

写法

  • address.call{value:xxx, gas:xxx}([bytes]) == address.call.value(xxx).gas(xxx)([bytes])【右侧用法已被弃用】

  • address.call{value:xxx, gas:xxx}(abi.encodeWithSignature("函数名称(参数类型)"), 参数) == address.call{value:xxx,gas:xxx}(abi.encodeWithSelector(bytes4(keccak256("参数类型"))),参数)

返回值

(bool success, bytes data)

call函数的返回结果是一个bool与bytes的元组,表示是否成功调用或者失败引起了EVM异常。该方法无法直接访问函数返回结果(因为需要事先知道编码和返回结果大小)

call()的返回结果即使成功,并不能说操作成功了,只是没有出现异常,有可能调用到了fallback()函数

调用另一个合约的方法

  • 带参情况

(bool success, bytes memory data) = _addr.call{value: msg.value, gas: 5000}(abi.encodeWithSignature("foo(string,uint256)", "call foo", 123)) ); 花括号指定发送以太币数量以及可用gas数量,根据地址_to对应合约寻找函数foo(string,uint256),后面跟着两个参数

  • 无参情况

(bool success, bytes memory data) = _addr.call{value: msg.value}(abi.encodeWithSignature("doesNotExist()"));

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
contract Receiver {
event received(string _callMsg, address _addr);
function foo(string memory funcName, uint num) payable external returns(uint){
emit received(funcName, msg.sender);
return num + 1;
}
fallback() external{
emit received("fail", msg.sender);
}
}
contract TestCall {
function testCallFoo(address payable addr_to_be_called) payable public returns(bytes memory) {
(bool ok, bytes memory data) = addr_to_be_called.call{value:msg.value, gas:5000}(abi.encodeWithSignature("foo(string, uint)", "testCallFoo", 10));
require(ok, "failed");
return data;
}
function testDonotExist(address payable addr_to_be_called) payable public returns(bytes memory) {
(bool ok, bytes memory data) = addr_to_be_called.call{value:msg.value, gas:5000}(abi.encodeWithSignature("DonotExist()"));
require(ok, "failed");
return data;
}
}

委托调用:Delegatecall

写法

address.delegatecall(abi.encodeCall(A.funA,(arg1, arg2, ...)))

例子

正常的调用其他合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;
contract Callee {
uint public x;
uint public value;
function setX(uint _x) public returns(uint){
x = _x;
return x;
}
function setXandEther(uint _x) public payable returns(uint, uint) {
x = _x;
value = msg.value;
return (x, value);
}
}
contract Caller {
function setX(Callee cl, uint _x) public returns(uint x) {
x= cl.setX(_x);
}
function setXWithAddress(address addr, uint _x) public returns(uint x) {
Callee cl = Callee(addr);
x = cl.setX(_x);
}
function setXAndEther(Callee cl, uint _x) public payable returns(uint x, uint value) {
(x, value) = cl.setXandEther{value:msg.value}(_x);
}
}

特别是cl.setXandEther{value:msg.value}(_x);里面的{value:msg.value}不要忘记