没有ABI怎么解码Calldata:函数选择器与参数步骤
没有 ABI 也能解码 Call dat a——靠函数选择器(4 字节)在公开数据库里查"函数名",然后按规则手拆参数。 查不到名字也照样能拆出参数值,只是看不到参数名而已。
1. 先搞懂 Calldata 的结构:三块
一笔交易的 Input Data(Calldata)长这样:0xa9059cbb0000000000000000000000003f5047bdb647dc39c88625e17bdbffee905a9f440000000000000000000000000000000000000000000011c9a62d04ed0c80000
拆成两块:
函数选择器(Function Selector):前 4 个字节(
a9059cbb)。它是函数签名(如transfer(address,uint256))的 Keccak-256 哈希的前 4 字节,用来告诉合约"你要调用哪个函数"。参数数据:4 字节之后的所有内容。每 32 字节(64 个十六进制字符)编码一个参数,按顺序排列。
前置条件:你手里有一笔交易的 TxHash,或者直接拿到了 Calldata 字符串。
2. 第一步:查函数选择器对应哪个函数名
步骤1:用 4byte.directory 查函数选择器
做什么:把前 4 字节输入 4byte.directory 数据库,查对应的函数签名。
怎么做:
情况A(网页访问):打开 4byte.directory,在搜索框输入 4 字节选择器(如
0xa9059cbb)。数据库会返回所有匹配的函数签名列表。情况B(API 调用):GET
https://www.4byte.directory/api/v1/signatures/?hex_signature=0xa9059cbb,返回 JSON 结果。
做到什么程度算完成:你拿到了一个或多个可能的函数签名(如
transfer(address,uint256))。如果有多个匹配项,说明不同的函数签名可能撞上了同一个选择器,需要根据参数数量来筛选。
4byte.directory 是目前最大的函数选择器公开数据库,收录了海量签名映射。
步骤2:如果 4byte.directory 查不到,用 Etherscan 的已验证合约兜底
做什么:如果数据库里没有记录,试试通过 Eth erscan 的 API 获取该合约的完整 ABI。
怎么做:调用
https://api.etherscan.io/api?module=contract&action=getabi&address=目标合约地址。如果该合约已开源验证,会返回完整 ABI。有了完整 ABI,用 Web3.py 或 ethers.js 即可精确解码。做到什么程度算完成:你拿到了完整 ABI 或至少确认了函数名。
3. 第二步:手拆参数(没有 ABI 也能拆)
即使查不到函数名,你照样能把参数值拆出来。
步骤3:按 32 字节分块提取参数
做什么:去掉函数选择器之后,把剩余数据按每 64 个字符(32 字节)切分。
怎么做:以
0xa9059cbb0000000000000000000000003f5047bdb647dc39c88625e17bdbffee905a9f440000000000000000000000000000000000000000000011c9a62d04ed0c80000为例:选择器:
a9059cbb第 1 个参数(address):
0000000000000000000000003f5047bdb647dc39c88625e17bdbffee905a9f44→ 去掉 12 字节填充后得到0x3f5047bdb647dc39c88625e17bdbffee905a9f44第 2 个参数(uint256):
00000000000000000000000000000000000000000000011c9a62d04ed0c80000→ 十进制5250000000000000000000(18 位小数后为 5250)
做到什么程度算完成:你得到了参数值的列表。
常见失败原因:多个函数签名使用了相同的选择器。例如
transfer(address,uint256)和transfer(address,address)不可能撞,但不同参数类型确实可能产生相同选择器,这就是"选择器碰撞"。Nethereum 文档也明确指出,同一个选择器可能匹配多个函数签名。这种情况下,需要结合实际参数长度来推断调用的具体是哪个函数。
风险提醒:4byte.directory 是用户提交的数据库,未经审计,里面可能包含错误或误导性条目。不要仅凭查到的函数名就认为调用内容绝对安全。
做完以上操作,怎么确认自己解码对了?
用已经验证过的 ERC-20 转账交易做一次测试:抄一个 transfer 的 Calldata,按上述步骤拆出地址和金额——如果地址和你看到的接收方一致、金额对得上,说明方法正确。之后遇到陌生合约,如果 4byte.directory 查到多个匹配,用参数长度来排除不可能的那几个。
