ABI编码 #

一、ABI编码概述 #

ABI(Application Binary Interface)编码是Solidity中数据序列化的标准方式,用于合约间通信和数据存储。

二、abi.encode #

2.1 基本用法 #

solidity
contract ABIEncode {
    function encodeUint(uint256 value) public pure returns (bytes memory) {
        return abi.encode(value);
    }
    
    function encodeMultiple(
        uint256 a,
        address b,
        bool c
    ) public pure returns (bytes memory) {
        return abi.encode(a, b, c);
    }
    
    function encodeArray(uint256[] memory arr) public pure returns (bytes memory) {
        return abi.encode(arr);
    }
}

2.2 编码特点 #

solidity
contract EncodeFeatures {
    // abi.encode:包含类型信息
    function encodeWithType(uint256 value) public pure returns (bytes memory) {
        return abi.encode(value);  // 32字节,包含填充
    }
    
    // 每个参数编码为32字节
    function encodeSize() public pure returns (uint256) {
        bytes memory data = abi.encode(uint256(1), uint256(2));
        return data.length;  // 64字节
    }
}

三、abi.encodePacked #

3.1 基本用法 #

solidity
contract ABIEncodePacked {
    function packUint(uint256 value) public pure returns (bytes memory) {
        return abi.encodePacked(value);
    }
    
    function packMultiple(
        uint256 a,
        address b,
        bool c
    ) public pure returns (bytes memory) {
        return abi.encodePacked(a, b, c);
    }
    
    function packString(string memory str) public pure returns (bytes memory) {
        return abi.encodePacked(str);
    }
}

3.2 紧凑编码特点 #

solidity
contract PackedFeatures {
    // abi.encodePacked:紧凑编码,不包含类型信息
    function compareEncoding() public pure returns (bytes memory, bytes memory) {
        uint256 value = 1;
        return (abi.encode(value), abi.encodePacked(value));
        // encode: 32字节
        // encodePacked: 1字节
    }
    
    // 字符串拼接
    function concatStrings(
        string memory a,
        string memory b
    ) public pure returns (string memory) {
        return string(abi.encodePacked(a, b));
    }
    
    // 计算哈希
    function hash(
        address sender,
        uint256 amount,
        uint256 nonce
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(sender, amount, nonce));
    }
}

四、abi.encodeWithSelector #

4.1 基本用法 #

solidity
contract EncodeWithSelector {
    function encodeTransfer(
        address to,
        uint256 amount
    ) public pure returns (bytes memory) {
        return abi.encodeWithSelector(
            bytes4(keccak256("transfer(address,uint256)")),
            to,
            amount
        );
    }
    
    function encodeApprove(
        address spender,
        uint256 amount
    ) public pure returns (bytes memory) {
        return abi.encodeWithSelector(
            0x095ea7b3,  // approve(address,uint256) 的选择器
            spender,
            amount
        );
    }
}

4.2 用于合约调用 #

solidity
contract UseSelector {
    function callTransfer(
        address token,
        address to,
        uint256 amount
    ) public returns (bool) {
        bytes memory data = abi.encodeWithSelector(
            bytes4(keccak256("transfer(address,uint256)")),
            to,
            amount
        );
        
        (bool success, bytes memory result) = token.call(data);
        require(success, "Call failed");
        
        return abi.decode(result, (bool));
    }
}

五、abi.encodeWithSignature #

5.1 基本用法 #

solidity
contract EncodeWithSignature {
    function encodeTransfer(
        address to,
        uint256 amount
    ) public pure returns (bytes memory) {
        return abi.encodeWithSignature(
            "transfer(address,uint256)",
            to,
            amount
        );
    }
    
    // 等价于
    function encodeWithSelectorEquivalent(
        address to,
        uint256 amount
    ) public pure returns (bytes memory) {
        return abi.encodeWithSelector(
            bytes4(keccak256("transfer(address,uint256)")),
            to,
            amount
        );
    }
}

六、abi.decode #

6.1 基本用法 #

solidity
contract ABIDecode {
    function decodeUint(bytes memory data) public pure returns (uint256) {
        return abi.decode(data, (uint256));
    }
    
    function decodeMultiple(
        bytes memory data
    ) public pure returns (uint256, address, bool) {
        return abi.decode(data, (uint256, address, bool));
    }
    
    function decodeArray(bytes memory data) public pure returns (uint256[] memory) {
        return abi.decode(data, (uint256[]));
    }
}

6.2 编码解码示例 #

solidity
contract EncodeDecode {
    struct User {
        address wallet;
        string name;
        uint256 balance;
    }
    
    function encodeUser(User memory user) public pure returns (bytes memory) {
        return abi.encode(user);
    }
    
    function decodeUser(bytes memory data) public pure returns (User memory) {
        return abi.decode(data, (User));
    }
    
    function roundTrip(User memory user) public pure returns (User memory) {
        bytes memory data = encodeUser(user);
        return decodeUser(data);
    }
}

七、实际应用示例 #

7.1 签名验证 #

solidity
contract SignatureVerification {
    function verify(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) public pure returns (bool) {
        bytes32 r;
        bytes32 s;
        uint8 v;
        
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }
        
        return ecrecover(hash, v, r, s) == signer;
    }
    
    function hashMessage(
        address to,
        uint256 amount,
        uint256 nonce
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(to, amount, nonce));
    }
}

7.2 跨合约调用 #

solidity
contract CrossContractCall {
    function callFunction(
        address target,
        string memory functionName,
        uint256 value
    ) public returns (bytes memory) {
        bytes memory data = abi.encodeWithSignature(
            functionName,
            value
        );
        
        (bool success, bytes memory result) = target.call(data);
        require(success, "Call failed");
        
        return result;
    }
}

八、总结 #

ABI编码要点:

函数 说明
abi.encode 完整编码(含类型)
abi.encodePacked 紧凑编码
abi.encodeWithSelector 带选择器编码
abi.encodeWithSignature 带签名编码
abi.decode 解码

使用建议:

  • 使用encode进行标准编码
  • 使用encodePacked节省空间
  • 使用decode解析数据

接下来,让我们学习内联汇编!

最后更新:2026-03-27