EVM交易构造和解析
一、EVM 交易基础结构
EVM 交易本质是 对账户或合约的一次调用。
标准交易字段:
Transaction
├─ nonce
├─ gasPrice / maxFeePerGas
├─ gasLimit
├─ to
├─ value
├─ data
├─ chainId
└─ signature (v,r,s)
关键字段说明:
| 字段 | 作用 |
|---|---|
| nonce | 防止重放,账户交易序号 |
| to | 目标地址(EOA 或合约) |
| value | 转账 ETH 数量 |
| data | 调用合约函数数据 |
| gasLimit | 最大 gas |
| gasPrice | 每单位 gas 价格 |
如果:
data = 0x
则是普通转账。
二、EVM 交易类型
常见交易类型:
1 Legacy Transaction
旧交易结构:
nonce
gasPrice
gasLimit
to
value
data
v,r,s
2 EIP-1559 Transaction
增加了动态费率:
maxPriorityFeePerGas
maxFeePerGas
交易类型:
type = 2
3 EIP-2930 Transaction
支持 access list:
type = 1
4 Account Abstraction / 特殊结构
例如:
- EIP-4337
- EIP-7702
会出现:
call struct
batch calls
三、EVM calldata 编码
EVM 合约调用通过:
data (calldata)
编码规则来自 ABI encoding。
基本结构:
4 bytes function selector
N bytes parameters
四、Function Selector
selector 计算方法:
keccak256("functionSignature")[0:4]
例如:
transfer(address,uint256)
计算:
keccak256("transfer(address,uint256)")
取前 4 bytes:
a9059cbb
所以:
0xa9059cbb
表示 ERC20 transfer。
常见 selector:
| selector | 函数 |
|---|---|
| a9059cbb | transfer |
| 095ea7b3 | approve |
| 23b872dd | transferFrom |
| 70a08231 | balanceOf |
| dd62ed3e | allowance |
五、ABI 参数编码规则
ABI 使用:
32 bytes slot
参数对齐方式:
uint256 → 32 bytes
address → 32 bytes (左填充)
bool → 32 bytes
示例:
transfer(address,uint256)
calldata:
a9059cbb
0000000000000000000000008c99bc6137cfe81a295fff86de795e0557b60f46
00000000000000000000000000000000000000000000000000000000000f4240
解释:
selector
address
amount
六、动态参数编码
如果函数包含:
string
bytes
array
struct
使用 offset 编码。
结构:
selector
offset
...
dynamic data
示例:
function foo(string)
编码:
selector
offset (0x20)
length
data
七、EVM 地址编码
EVM 地址:
20 bytes
calldata 中表示为:
32 bytes
示例:
0000000000000000000000008c99bc6137cfe81a295fff86de795e0557b60f46
规则:
前 12 bytes 填充 0
后 20 bytes 地址
八、数字编码
EVM 所有整数:
uint256
编码:
32 bytes big-endian
示例:
0x0f4240
转十进制:
1000000
九、交易构造流程
构造交易通常步骤:
1 定义函数
2 ABI encode 参数
3 构造 calldata
4 填充交易字段
5 签名
6 广播
示例(ethers.js):
const iface = new ethers.Interface(abi)
const data = iface.encodeFunctionData(
"transfer",
[to, amount]
)
const tx = {
to: token,
data: data,
value: 0
}
十、交易签名
EVM 使用:
ECDSA secp256k1
签名字段:
r
s
v
交易 hash:
keccak256(rlp(tx))
签名后:
rawTransaction
十一、交易解析流程
解析一笔交易通常:
1 获取 transaction
2 读取 data
3 解析 selector
4 查 ABI
5 decode 参数
流程图:
transaction
↓
data
↓
function selector
↓
ABI
↓
decode parameters
十二、Batch / Multicall 交易
DeFi 常见:
multicall
结构:
call[]
├─ target
├─ calldata
└─ value
例如:
swap
transfer
transfer
十三、EVM 常见编码格式
| 类型 | 编码 |
|---|---|
| calldata | hex |
| address | 20 bytes |
| hash | 32 bytes |
| signature | 65 bytes |
EVM 基本全部使用:
hex (0x)
十四、Calldata 解析工具
常见工具:
- Foundry
- Tenderly
- ethers.js
常用命令:
cast 4byte
cast decode-calldata
十五、交易 trace
复杂交易需要:
trace
trace 可以看到:
internal calls
delegatecall
staticcall
transfer
常见工具:
- Tenderly
- Hardhat
十六、链上解析常见数据
解析交易通常关注:
token transfers
swap events
logs
internal transactions
日志结构:
event signature
topics
data
event signature:
keccak256("Transfer(address,address,uint256)")
十七、事件日志解析
日志结构:
log
├─ address
├─ topics[]
└─ data
topics:
topic0 = event signature
topic1 = indexed param
topic2 = indexed param
示例:
Transfer(address,address,uint256)
topics:
topic0 event hash
topic1 from
topic2 to
data:
amount
十九、EIP-7702 Gas 代付流程
EIP-7702 实现了 账户抽象,允许合约代替用户支付 Gas。
流程
用户签名
↓
Relayer 收到签名
↓
Relayer 发链上交易
↓
7702 delegate contract 执行逻辑
↓
Sponsor/Paymaster 出 gas
各方职责
| 角色 | 职责 |
|---|---|
| 用户 | 签名授权,不需要主币 |
| Relayer | 最终广播者,收取用户签名后构造并广播交易 |
| Delegate Contract | 决定执行规则,验证签名,执行逻辑 |
| Sponsor/Paymaster | 实际支付 Gas 费用 |
关键特点
- 用户不需要主币:Gas 由 Sponsor 代付
- Relayer 广播:承担链上交易的实际发送
- Delegate Contract:可编程的执行规则,决定谁可以代理、收取多少手续费等
与传统交易的区别
传统交易:
用户 → 链上(用户签名 + 广播,自身付 gas)
7702 代付:
用户签名 → Relayer → 链上(Relayer 广播,Sponsor 付 gas)
二十、EVM 解析核心知识
1. 基本概念
- calldata:以太坊 EVM 中交易调用合约的输入数据。
msg.data在 Solidity 中对应 calldata- 包含函数选择器 + 函数参数编码
- 函数选择器(function selector):
- 前 4 字节(8 个 hex 字符)
- 由函数签名(
functionName(type1,type2,...))经过 Keccak256 哈希取前 4 字节生成
- 参数编码:
- 使用 ABI 编码,固定长度类型按 32 字节对齐,动态类型附带偏移和长度信息
2. 解析步骤
- 拆分选择器与参数
- calldata:
0xa9059cbb000000000000000000000000e84e684c... - 前 4 字节
a9059cbb→ 查函数对应的 selector →transfer(address,uint256)
- calldata:
- 解析参数
- 固定长度类型(uint256、address、bool…):
- 直接按 32 字节切片
- 地址
address在低 20 字节
- 动态类型(string、bytes、数组):
- 前面存偏移量(从 calldata 开始的字节位置)
- 偏移位置存长度,再后面是实际数据
- 固定长度类型(uint256、address、bool…):
- 多函数调用(multicall / 7702 struct)
- calldata 可能包含多个子调用
- 每个子调用有:
target地址- 子调用
callData(单独解析) value(支付的 ETH 数量)
3. 常用工具
- 在线/GUI: