# 以太坊 ABI 编码机制

By [Untitled](https://paragraph.com/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936) · 2024-12-06

---

          以太坊智能合约的 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\. **设计更加兼容的合约接口**。

---

*Originally published on [Untitled](https://paragraph.com/@0xc994df6bd78b5e78d4e820a57d65b4efd33bf936/abi)*
