# 以太坊 ABI 编码机制 **Published by:** [Untitled](https://paragraph.com/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936/) **Published on:** 2024-12-06 **URL:** https://paragraph.com/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936/abi ## Content 以太坊智能合约的 ABI(Application Binary Interface),即应用程序二进制接口,是合约与外界交互的桥梁。ABI 编码是将函数调用及其参数序列化为以太坊虚拟机(EVM)可识别的二进制格式的关键步骤。它是一种 规则,用来定义外部如何与智能合约交互,包括函数调用和数据编码。 1、什么是 ABI 编码? 以太坊的 ABI 编码定义了智能合约如何接收输入参数,以及如何返回结果。ABI 编码的主要特点是: 1.定长类型(如 uint256、address)直接编码为固定的 32 字节。 2.动态长度类型(如 string、bytes)使用偏移量指向实际数据存储位置,确保灵活性和一致性。 2、ABI 编码规则 在调用智能合约函数时,数据编码的组成包括两部分: 1.函数选择器(4 字节) 它是函数签名(函数名 + 参数类型)的哈希值的前 4 字节。 例如,transfer(address,uint256) 的函数选择器是 0xa9059cbb。 2.参数编码 固定大小参数(如 uint256、address):编码为 32 字节 的数据,左对齐。 动态大小参数(如 string、bytes):先编码偏移量(位置指针),再编码实际数据。 3、具体说明 (1) 固定长度类型 固定长度类型直接编码为 32 字节: uint256:数字以十六进制形式表示,左侧补零。 address:地址固定为 20 字节,同样以十六进制表示,左侧补零,右对齐。 示例: function transfer(address _to, uint256 _amount) public; 调用这个函数的参数是: _to: 0x000000000000000000000000000000000000dEaD _amount: 1000 编码过程: 1. 计算函数选择器: • Keccak256("transfer(address,uint256)")[:4] = 0xa9059cbb 2. 编码参数: • _to(address):编码为 0x000000000000000000000000000000000000dEaD(32 字节,左对齐)。 • _amount(uint256):编码为 0x00000000000000000000000000000000000000000000000000000000000003e8(1000 的十六进制)。 最终编码结果: 1、为什么1000的16进制后,在最终编码的时候要把0x去掉? 0x 是一种 人类可读的表示形式,用来表示数据是十六进制数字。在 EVM 和 ABI 编码中: • 数据需要以 纯二进制或十六进制格式存储和传输。 • 为了节约空间并与规范兼容,ABI 编码不保留 0x 前缀。 因此: • 1000 转换为十六进制是 0x03e8。 • 在编码时去掉 0x,只保留 有效数字部分(03e8),然后左对齐填充到 32 字节。 2、为什么 address 要转换为小写,并且长度固定? 小写转换: 以太坊地址不区分大小写,但 ABI 编码要求地址统一为小写,这是为了: • 避免大小写不一致导致错误(例如手动输入时)。 • 符合常见的编码和解析工具的要求。 长度变化(固定为 32 字节): address 在以太坊中占用 20 字节(160 位),但 ABI 编码规则规定: • 所有固定大小类型(如 address, uint256, bool)在编码时必须扩展为 32 字节。 • 这是为了在 EVM 中保持统一的内存对齐,便于处理和解析数据。 例如: • 地址 0x000000000000000000000000000000000000dEaD(20 字节)。 • 编码时填充到 32 字节: 000000000000000000000000000000000000000000000000000000000000dead 注意这里的 dEaD 自动转换为小写 dead。 总结 • 去掉 0x 是因为 ABI 编码传输的是数据本身,而不是表示形式。 • 小写地址 是为了规范化和工具兼容性。 • 32 字节对齐 是 EVM 内部操作的设计要求。 这两点遵循的都是 ABI 编码的基本原则:一致性、简洁性和高效性 (2) 动态大小类型 动态大小类型的编码在以太坊 ABI 中遵循严格的规则,其核心思想是:使用偏移量和实际数据分离存储。 动态类型包括: 字符串 (string) 字节数组 (bytes) 动态大小数组 (T[],如 uint256[]) 这些类型长度不固定,因此需要额外的信息来表示数据的大小和位置。 编码规则 对于动态类型: 1. 存储偏移量 • 动态数据的起始位置存储在 32 字节的偏移量中(相对于整个编码起始位置)。 • 偏移量通常紧接着固定类型的编码。 2. 存储数据长度 • 偏移量指向的实际数据区域,第一段存储的是动态类型的长度(以字节为单位)。 3. 存储数据本体 • 数据紧随长度信息进行存储。 • 如果数据长度不足 32 字节,使用零填充对齐到 32 字节。 举例说明 假设有以下函数: function example(string memory name, uint256 age) public pure {} 调用 example("Alice", 25),其编码步骤如下: 1. 函数选择器(4 字节) example(string,uint256) 的函数签名哈希为: keccak256("example(string,uint256)") => 0xcdcd77c0 2. 编码固定类型 uint256 age 的值是 25,编码为 32 字节: • 类型:uint256 • 值:25 • 固定类型在 ABI 编码中,总是编码为一个 32 字节的固定长度。 • 编码过程: • 将十进制 25 转为十六进制:0x19 • 填充到 32 字节(左侧补零): 3. 编码动态类型 string 动态数据的编码分三部分: • 偏移量: 假设动态数据起始偏移量为 64(从整个编码起点开始计算),编码为: • 类型:string(动态类型) • 动态类型的值存储在编码的后部,为了找到动态类型的值,需要一个 偏移量。 • 偏移量是相对于整个编码起点的字节位置。 偏移量计算: • 假设编码结构如下: • 第 0-31 字节:age 的值 • 第 32-63 字节:name 的偏移量 • 动态数据从 第 64 字节 开始存储。 因此: • name 的偏移量为 64,编码为 32 字节(十六进制): • 长度: "Alice" 长度为 5,编码为: • 类型:string(动态类型) • 动态类型的实际数据存储包括: • 数据的长度(以字节为单位)。 • 数据本体(对齐到 32 字节)。 长度计算: • "Alice" 的长度为 5 字节(每个字符占 1 字节)。 • 编码为 32 字节(十六进制): • 数据: "Alice" 的 ASCII 值编码为: • 动态类型的本体存储在长度字段之后,需要对齐到 32 字节。 数据编码: • "Alice" 的 ASCII 值为: A: 0x41, l: 0x6c, i: 0x69, c: 0x63, e: 0x65 • 拼接成字节序列: • 对齐到 32 字节(右侧补零): 完整编码 整合所有部分: 动态类型(如 string)在 ABI 编码中的处理方式: 1. 偏移量 指向数据在编码中的位置。 2. 长度 说明数据的字节数。 3. 数据本体 对齐到 32 字节存储。 总结 以太坊 ABI 编码机制为智能合约与外部交互提供了一种标准化的方法。通过固定长度类型和动态长度类型的分段处理,既保证了灵活性,又兼顾了高效性。 从开发者的角度,理解 ABI 编码有助于: 1. 手动调试智能合约调用数据。 2. 优化参数传递,减少 Gas 消耗。 3. 设计更加兼容的合约接口。 ## Publication Information - [Untitled](https://paragraph.com/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936/): Publication homepage - [All Posts](https://paragraph.com/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936): Subscribe to updates