题目
前端调用 transfer(address,uint256) 时,ABI 起什么作用?calldata 的前 4 个字节是什么,后面的参数又如何组织?
考察目标
- 是否知道 ABI 是编码和解码所需的接口描述,不是部署在前端的合约代码。
- 是否理解函数选择器来自规范化函数签名的 Keccak-256 哈希前 4 字节。
- 是否能说明调用数据、返回值和事件日志需要不同的解码信息。
考察函数选择器、参数编码和返回值解码在前端合约交互中的作用。
前端调用 transfer(address,uint256) 时,ABI 起什么作用?calldata 的前 4 个字节是什么,后面的参数又如何组织?
ABI 描述函数、事件、错误及其参数类型,前端库据此编码 calldata 并解码返回值。函数调用的前 4 字节是规范化函数签名 Keccak-256 哈希的前 4 字节,例如签名包含函数名和参数类型但不包含返回类型;第 5 字节开始是按 ABI 规则编码的参数。EVM 收到的只是字节,ABI 主要供调用方和工具正确解释这些字节。
JSON ABI 通常包含函数名、输入、输出、状态可变性、事件和自定义错误。前端库可以用它完成:
eth_call 返回值。ABI 编码不是自描述格式。只有一段返回字节时,如果不知道预期类型,无法可靠地判断它应该被解释成整数、地址还是其他结构。
函数选择器是规范化函数签名 Keccak-256 哈希的前 4 字节。transfer(address,uint256) 中:
uint 写作 uint256。从第 5 字节开始是参数编码。静态值通常占用 32 字节槽;字符串、动态数组等动态类型在头部保存偏移量,实际内容位于后部。
事件与函数不同。非匿名事件通常把完整事件签名哈希放在 topic0,它是 32 字节,不是 4 字节函数选择器;被 indexed 的参数进入 topics,其余参数在 data 中编码。
import { encodeFunctionData } from "viem";
const data = encodeFunctionData({
abi: erc20Abi,
functionName: "transfer",
args: [recipient, 1_000_000n],
});
const selector = data.slice(0, 10); // 0x + 8 个十六进制字符,即 4 字节
const encodedArguments = `0x${data.slice(10)}`;
业务代码应让经过校验的 ABI 和合约地址一起来自目标链部署配置,不能仅凭调用成功就认定地址是预期合约。
topic0 当成 4 字节函数选择器。发现这道题有问题? 反馈此题