在以太坊/Solidity里,ABIEncode和ABIDecode是把参数↔二进制数据(bytes)之间相互转换的机制,用于合约调用、跨合约通信、日志解析等。ABI(ApplicationBinaryInterface)是以太坊约定的一套数据编码/解码规则。即
在 以太坊 / Solidity 里,ABI Encode 和 ABI Decode 是把参数 ↔ 二进制数据(bytes)之间相互转换的机制,用于合约调用、跨合约通信、日志解析等。
ABI (Application Binary Interface) 是以太坊约定的一套 数据编码 / 解码规则。即把人类可读的参数,转成 EVM 能识别的 bytes;再把 bytes,还原成人类可读的数据。
ABI Encode: 将高级数据类型(如字符串、结构体、数组等)转换为字节码,用于合约调用。
ABI Decode: 将字节码解析回原始数据类型。
将参数编码为标准 ABI 格式(带填充)。
特点:
function encodeExample() public pure returns (bytes memory) {
// 编码多个参数
return abi.encode(123, "hello", true);
// 返回: 0x000000000000000000000000000000000000000000000000000000000000007b
// 0000000000000000000000000000000000000000000000000000000000000060
// 0000000000000000000000000000000000000000000000000000000000000001
// 0000000000000000000000000000000000000000000000000000000000000005
// 68656c6c6f000000000000000000000000000000000000000000000000000000
}
紧密打包编码(不填充),节省空间,但可能导致哈希碰撞。
特点:
用途:
keccak256function encodePackedExample() public pure returns (bytes memory) {
// 紧密编码,无填充
return abi.encodePacked(uint16(123), "hello", true);
// 返回: 0x007b68656c6c6f01 (更短)
}
// 注意:可能的哈希碰撞问题
// abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c") ❌
编码函数选择器和参数,用于合约调用。
用途:
call / staticcallfunction encodeWithSelectorExample() public pure returns (bytes memory) {
// 编码函数调用: transfer(address,uint256)
bytes4 selector = bytes4(keccak256("transfer(address,uint256)"));
return abi.encodeWithSelector(
selector,
0x1234567890123456789012345678901234567890,
100
);
}
直接使用函数签名编码。
和 encodeWithSelector 类似,但:
function encodeWithSignatureExample() public pure returns (bytes memory) {
return abi.encodeWithSignature(
"transfer(address,uint256)",
0x1234567890123456789012345678901234567890,
100
);
}
解码字节数据为原始类型。
规则:
abi.encode 的结果encodePackedfunction decodeExample(bytes memory data) public pure returns (uint256, string memory, bool) {
// 解码数据
(uint256 num, string memory str, bool flag) = abi.decode(
data,
(uint256, string, bool)
);
return (num, str, flag);
}
// 组合使用
function encodeAndDecode() public pure returns (uint256, string memory) {
// 编码
bytes memory encoded = abi.encode(uint256(42), "test");
// 解码
(uint256 num, string memory str) = abi.decode(encoded, (uint256, string));
return (num, str);
}
| 对比项 | ABI Encode | ABI Decode |
|---|---|---|
| 方向 | 参数 → bytes | bytes → 参数 |
| 用途 | 调用 / 存储 | 解析返回值 |
| 常见方法 | encode / encodePacked |
decode |
| 是否补 32 字节 | encode 会 |
N/A |
| 是否可逆 | encode ✔ | decode ✔ |
contract Caller {
function callOtherContract(address target) public {
bytes memory data = abi.encodeWithSignature(
"setValue(uint256)",
123
);
(bool success, ) = target.call(data);
require(success, "Call failed");
}
}
contract Proxy {
address public implementation;
fallback() external payable {
address impl = implementation;
assembly {
// 复制calldata
calldatacopy(0, 0, calldatasize())
// 委托调用
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
// 复制返回数据
returndatacopy(0, 0, returndatasize())
// 返回或回滚
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
contract MultiSig {
function executeTransaction(
address to,
uint256 value,
bytes memory data
) public {
// data 包含了 ABI 编码的函数调用
(bool success, ) = to.call{value: value}(data);
require(success, "Transaction failed");
}
}
contract EventExample {
event DataStored(uint256 indexed id, bytes data);
function storeData(uint256 id, uint256 value, string memory text) public {
bytes memory encodedData = abi.encode(value, text);
emit DataStored(id, encodedData);
}
}
在您的项目中使用 Web3j:
// 编码参数
Function function = new Function(
"transfer",
Arrays.asList(new Address(toAddress), new Uint256(amount)),
Collections.emptyList()
);
String encodedFunction = FunctionEncoder.encode(function);
// 解码返回值
List<Type> results = FunctionReturnDecoder.decode(
responseData,
function.getOutputParameters()
);
| 场景 | 推荐方法 |
|---|---|
| 合约调用 | encodeWithSignature 或 encodeWithSelector |
| 哈希计算 | encode (避免碰撞) |
| 节省空间 | encodePacked (非签名场景) |
| 数据传输 | encode (标准格式) |
函数名 + 参数
↓
ABI Encode
↓
calldata
↓
EVM 执行
↓
返回 bytes
↓
ABI Decode
↓
返回值
encodePacked 去 decodebytes memory data = abi.encodePacked(a, b);
abi.decode(data, (uint256, uint256)); // ❌
✔ 正确:
encodePacked 只用于 hashabi.decode(data, (address, uint256)); // ❌
必须和 encode 时 一模一样
动态类型:
stringbytesuint[]struct(包含动态成员)👉 ABI encode 会放 offset + length + data
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!