4. Calldata
Calldata
也就是我们发送给函数的编码参数。也就是发送给EVM的数据。
每个calldata长度为32字节或者64个字符,一般calldata分为两种:
- Static
- Dynamic
如何编码
如果对类型进行编码,直接传递到abi.encode()
中来生成原始调用数据
如果对特定接口函数,我们需要使用abi.encodeWithSelector(selector, parameters)
interface A {
function transfer(uint256[] memory ids, address to) virtual external;
}
contract B {
function a(uint256[] memory ids, address to) external pure returns(bytes memory) {
return abi.encodeWithSelector(A.transfer.selector, ids, to);
}
}
如何解码
如果使用abi.encode编码的,我们可以使用abi.decode来解码:
(uint256 a, uint256 b) = abi.decode(data, (uint256, uint256))
Static Variables
假如我们在和下面的合约交互:
pragma solidity 0.8.17;
contract Example {
function transfer(uint256 amount, address to) external;
}
我们想要传递的参数是:
amount: 1300655506
address: 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
那将会生成一段:0x000000000000000000000000000000000000000000000000000000004d866d9200000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45
我们该怎么阅读呢,其实我们只需要吧0x删去,然后以32字节为一组分开就好:
0x
// uint256
000000000000000000000000000000000000000000000000000000004d866d92
// address
00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45
Function
我们都知道函数选择器是函数加上参数然后进行keccak256
哈希之后的前四个字节,所以如果我们想抗药调用transfer(uint256,address)
,我们就需要先:
keccak256("transfer(uint256,address)");
然后将结果的前四个字节b7760c8f
拼接到calldata中:
0xb7760c8f000000000000000000000000000000000000000000000000000000004d866d9200000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45
Dynamic Variables
动态变量是非固定大小的类型,包括 bytes
、 string
和动态数组 <T>[]
,以及固定数组 <T>[N]
。
动态类型的结构始终以偏移量开始,偏移量是动态类型开始位置的十六进制表示。例如,十六进制 20
表示 32-bytes
。一旦到达偏移量,就会有一个较小的数字表示类型的长度。也就是说第一个32字节是偏移量,第二个32字节是长度,其余为元素
我们用一个例子形象的表明一下,比如一个字符串string
类型,值是Hello World!
:
0x
0000000000000000000000000000000000000000000000000000000000000020
000000000000000000000000000000000000000000000000000000000000000c
48656c6c6f20576f726c64210000000000000000000000000000000000000000
第一个表明偏移量是20也就是十进制的32,所以我们跳过直接去下一行,值是0c,十进制的12,表明我们的字符串有12个字符,最后一行就是元素。