什么是 ABI 编码?
ABI 编码是 Solidity 合约 ABI 规范中把函数调用转换为字节的规则:先是 4 字节选择器,随后是打包成 32 字节字的参数;静态值内联存放,动态值(bytes、字符串、动态数组)放在头部之后,头部记录其字节偏移量。ABI 编码器根据签名和取值构造这段 calldata;解码器则做逆向操作。
布局方式
以 f(uint256,uint32[],bytes10,bytes) 传入 (0x123, [0x456, 0x789], "1234567890", "Hello, world!") 为例,编码结果为:选择器 0x8be65246,然后是四个字组成的头部(uint256、指向数组的偏移量 0x80、右填充的 bytes10、指向 bytes 的偏移量 0xe0),再是尾部:数组长度 2 及其两个元素,接着是 bytes 长度 13 和填充后的数据。点击上方的“Solidity 文档 f()”可逐字查看。
规则简述
- 每个值占用 32 字节的整数倍。整数和地址左填充,
bytesN右填充。 - 负整数采用 256 位二进制补码。
- 动态值通过字节偏移量引用,偏移量从所在元组或数组主体的起始处计算。
- 静态类型
T的静态数组T[k]内联存放;动态数组T[]以其长度开头。 - 元组递归遵循同样的头部/尾部规则,结构体和嵌套数组正是这样编码的。
实例演示:一笔 ERC-20 转账
transfer(address,uint256),接收方为 0x1111111111111111111111111111111111111111,金额 25 USDT(原始单位 25,000,000),编码后共 68 字节:选择器 a9059cbb,然后是 12 个零字节的左填充加 20 字节地址,再是 25,000,000 = 0x17d7840 左填充为一个 32 字节字。任何区块浏览器中的每一笔 ERC-20 转账都是这个形态,所以解码器仅凭选择器就能识别函数并还原参数。
相关工具
单独计算选择器可使用函数选择器工具;把代币数量换算为原始整数可使用代币小数位换算器;查看单个 32 字节字可使用十六进制转十进制换算器。