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,s2 EIP-1559 Transaction#
增加了动态费率:
maxPriorityFeePerGas
maxFeePerGas交易类型:
type = 23 EIP-2930 Transaction#
支持 access list:
type = 14 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 bytescalldata 中表示为:
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#
复杂交易需要:
tracetrace 可以看到:
internal calls
delegatecall
staticcall
transfer常见工具:
- Tenderly
- Hardhat
十六、链上解析常见数据#
解析交易通常关注:
token transfers
swap events
logs
internal transactions日志结构:
event signature
topics
dataevent signature:
keccak256("Transfer(address,address,uint256)")十七、事件日志解析#
日志结构:
log
├─ address
├─ topics[]
└─ datatopics:
topic0 = event signature
topic1 = indexed param
topic2 = indexed param示例:
Transfer(address,address,uint256)topics:
topic0 event hash
topic1 from
topic2 todata:
amount十八、EVM 解析核心知识#
EVM Calldata 解析笔记#
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: