持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
前言
ABI 全称是 Application Binary Interface,翻译过来就是:应用程序二进制接口,简单来说就是 以太坊的调用合约时的接口说明,合约编译的时候都会生成对应的abi和字节码。
函数编码函数
- abi.encode(…) returns (bytes) 计算参数的编码。
function encodeString() public pure returns (bytes memory) {
bytes memory someString = abi.encode("some string");
return someString;
}
调用合约方法可以得到
0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e67000000000000000000000000000000000000000000
相对应的解码函数,参数传入编码后的结果,和转码成的类型即可
function decodeString() public pure returns (string memory) {
string memory someString = abi.decode(encodeString(), (string));
return someString;
}
- abi.encodePacked(…) returns (bytes):计算参数的紧密打包编码
function encodeStringPacked() public pure returns (bytes memory) {
bytes memory someString = abi.encodePacked("some string");
return someString;
}
这个编码方式还有另外一种实现方式
function encodeStringBytes() public pure returns (bytes memory) {
bytes memory someString = bytes("some string");
return someString;
}
对比这2种方式发现,第二种还能更省gas,因为第一个是复制内存,第二个只是转换指针类型。 转换指针类型这种方式显然更省gas。
- abi. encodeWithSelector(bytes4 selector, …) returns (bytes): 计算函数选择器和参数的 ABI 编码
function getSelectorOne() public pure returns (bytes4 selector) {
selector = bytes4(keccak256(bytes("transfer(address,uint256)")));
}
function getDataToCallTransfer(address someAddress, uint256 amount)
public
pure
returns (bytes memory)
{
return abi.encodeWithSelector(getSelectorOne(), someAddress, amount);
}
函数选择器,官方文档定义如下: 一个函数调用数据的前 4 字节,指定了要调用的函数。这就是某个函数签名的 Keccak(SHA-3)哈希的前 4 字节(高位在左的大端序)
即可得到这个函数选择器。
接着通过encodeWithSelector 即可得到函数选择器和参数的 ABI 编码
0xa9059cbb0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000001
可以看到前四个字节就是函数选择器,后面的表示参数,即地址和金额 2个参数的编码,每个参数用三十二个字节表示,不足的补0。
- abi.encodeWithSignature(string signature, …) returns (bytes): 等价于* abi.encodeWithSelector(bytes4(keccak256(signature), …)
function getSelectorTwo() public view returns (bytes4 selector) {
bytes memory functionCallData = abi.encodeWithSignature(
"transfer(address,uint256)",
address(this),
123
);
selector = bytes4(
bytes.concat(
functionCallData[0],
functionCallData[1],
functionCallData[2],
functionCallData[3]
)
);
}
function getCallData() public view returns (bytes memory) {
return abi.encodeWithSignature("transfer(address,uint256)", address(this), 123);
}
这个方法本质上也是得到函数的选择器。
通过函数abi编码调用合约
function callTransferFunctionDirectlyThree(address someAddress, uint256 amount)
public
returns (bytes4, bool)
{
(bool success, bytes memory returnData) = s_selectorsAndSignaturesAddress.call(
abi.encodeWithSignature("transfer(address,uint256)", someAddress, amount)
);
return (bytes4(returnData), success);
}
学会了函数选择器,我们就可以直接通过函数选择器加上参数编码后的结果直接调用,写了个方法测试如下
可以看到通过call方法传入编码结果, 可以直接调用了转账的方法。
总结
abi编码函数主要就是以上的使用方式,重点是函数选择器,掌握这个对在以后合约开发是比较重要的。