4、交易数据解析:交易结构(nonce, gas, value, data)、解码Input Data(ABI编码)、实战:解析一笔USDT转账
各位同学,欢迎来到第四节课。前面我们聊了区块、地址、哈希这些基础概念,今天咱们要动真格的了——直接拆解一笔交易。
你可能听过一句话:「链上一切皆交易」。但交易到底长什么样?里面那些 nonce、gas、data 字段是干嘛的?尤其是那个神秘的 input data,里面到底藏着什么信息?
今天我就带你一层层剥开它。放心,剥完你会发现,其实没那么玄乎。
4.1 交易结构:一笔交易到底长什么样?
先看一个最原始的交易 JSON 结构。这是以太坊上真实的一笔交易,我简化了一下字段:
{
"hash": "0xabc...123",
"nonce": "0x5",
"from": "0xAbc...",
"to": "0xDef...",
"value": "0x2386f26fc10000",
"gas": "21000",
"gasPrice": "0x3b9aca00",
"input": "0xa9059cbb000000000000000000000000..."
}
每个字段都有它的使命。我按自己的理解,给你拆开讲。
4.1.1 nonce:交易序号,防重放攻击
nonce 不是随机数,而是「交易计数」。它表示从某个地址发出的第几笔交易。
举个例子:你的地址第一次发交易,nonce=0;第二次发,nonce=1。矿工收到交易时,会检查 nonce 是否连续。如果 nonce=5 的交易先到了,nonce=4 的还没到,矿工会把 nonce=5 的交易先放着,等 nonce=4 到了再一起打包。
4.1.2 gas 与 gasPrice:交易的「油费」
gas 是交易消耗的计算量单位。比如普通 ETH 转账需要 21000 gas,而调用合约可能消耗 10 万甚至更多。
gasPrice 是你愿意为每单位 gas 支付的价格,单位是 wei(1 ETH = 10^18 wei)。
总手续费 = gasUsed × gasPrice。这个公式你肯定见过,但我想强调一点:gasPrice 不是越高越好。我见过有人把 gasPrice 设成 1000 gwei,结果一笔转账花了 0.02 ETH 手续费——完全没必要。
4.1.3 value:转账金额
value 表示转账的 ETH 数量,单位是 wei。比如 0x2386f26fc10000 转换成十进制是 10^16 wei,也就是 0.01 ETH。
注意:如果交易是调用合约,value 可能为 0。比如你调用 Uniswap 的 swap 函数,value=0,但实际你是在用合约里的代币做交换。
4.1.4 data / input:交易的「灵魂」
这是今天最核心的部分。data 字段(也叫 input data)包含了交易要执行的指令。如果是普通 ETH 转账,data 为空。但如果是合约调用,data 里就藏着函数名和参数。
说白了,data 就是一段二进制指令,告诉合约:「嘿,帮我执行这个函数,参数是这些。」
4.2 解码 Input Data:ABI 编码的秘密
你看到的 data 是一长串十六进制,比如:
0xa9059cbb000000000000000000000000abc...0000000000000000000000000000000000000000000000000000000000000064
这串东西怎么读?答案是:用 ABI(Application Binary Interface)编码规则。
4.2.1 ABI 编码的基本规则
ABI 编码其实不复杂。它把 data 分成三部分:
- 函数选择器(4 字节):函数签名的 Keccak-256 哈希的前 4 字节。比如
transfer(address,uint256)的哈希前 4 字节是0xa9059cbb。 - 参数数据(动态长度):每个参数按 32 字节对齐。地址类型占 32 字节(左补零),uint256 占 32 字节。
举个例子:transfer("0xabc...", 100) 的 data 就是:
- 函数选择器:
0xa9059cbb - 地址参数(32 字节):
0x000000000000000000000000abc... - 金额参数(32 字节):
0x0000000000000000000000000000000000000000000000000000000000000064(100 的十六进制是 0x64)
你看,是不是很清晰?
4.2.2 常见函数签名速查
我整理了几个最常用的函数签名,你肯定用得上:
| 函数 | 签名 | 选择器(前4字节) |
|---|---|---|
| transfer | transfer(address,uint256) | 0xa9059cbb |
| transferFrom | transferFrom(address,address,uint256) | 0x23b872dd |
| approve | approve(address,uint256) | 0x095ea7b3 |
| swapExactTokensForTokens | swapExactTokensForTokens(uint256,uint256,address[],address,uint256) | 0x38ed1739 |
你可以在 https://www.4byte.directory 上查任意函数签名。我个人习惯是写个脚本自动查,省得手动复制。
4.3 实战:解析一笔 USDT 转账
光说不练假把式。咱们直接拿一笔真实的 USDT 转账来解析。
4.3.1 获取交易数据
假设我们有一笔交易,hash 是 0xabc...123。通过 Etherscan 或 RPC 接口,我们拿到它的 data:
0xa9059cbb0000000000000000000000004a9d5b6b4c1d2e3f8a7b9c0d1e2f3a4b5c6d7e8f0000000000000000000000000000000000000000000000000000000000000064
4.3.2 手动解码
第一步:看前 4 字节 0xa9059cbb。查表可知,这是 transfer(address,uint256)。
第二步:从第 5 字节开始,取 32 字节作为地址参数:
0x0000000000000000000000004a9d5b6b4c1d2e3f8a7b9c0d1e2f3a4b5c6d7e8f
去掉前导零,得到地址:0x4a9d5b6b4c1d2e3f8a7b9c0d1e2f3a4b5c6d7e8f。
第三步:再取 32 字节作为金额参数:
0x0000000000000000000000000000000000000000000000000000000000000064
转换成十进制:0x64 = 100。注意,USDT 是 6 位小数,所以实际金额是 100 / 10^6 = 0.0001 USDT。
所以这笔交易的意思是:从发送方地址转账 0.0001 USDT 给 0x4a9d...e8f。
4.3.3 用代码自动解码
手动解码太慢了。我写了个 Python 脚本,用 web3.py 自动解析:
from web3 import Web3
# 连接节点
w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_PROJECT_ID'))
# 交易哈希
tx_hash = '0xabc...123'
tx = w3.eth.get_transaction(tx_hash)
# 解码 data
# 注意:需要合约的 ABI
usdt_abi = [{
"constant": False,
"inputs": [
{"name": "_to", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "transfer",
"type": "function"
}]
contract = w3.eth.contract(address=tx['to'], abi=usdt_abi)
decoded = contract.decode_function_input(tx['input'])
print(decoded)
# 输出: (, {'_to': '0x4a9d...e8f', '_value': 100})
你看,几行代码就搞定了。我个人建议你把这个脚本保存下来,以后解析任何 ERC-20 转账都能用。
web3.eth.get_code() 获取合约字节码,然后反编译。但更简单的方法是去 Etherscan 上直接复制 ABI。
4.4 知识体系总览
为了让你更直观地理解今天的内容,我画了一张图:
这张图把今天的内容串起来了。从左到右:先理解交易结构,再掌握 ABI 编码规则,最后动手解析一笔真实交易。每一步都是下一环的基础。
4.5 避坑指南与个人经验
最后,分享几个我踩过的坑:
- data 长度不对? 检查函数签名是否正确。我遇到过有人把
transfer和transferFrom搞混,导致解码结果完全错误。 - 参数顺序别搞反。 比如
transfer(address,uint256),地址在前,金额在后。如果顺序反了,解析出来的地址会变成金额,金额会变成地址——那画面太美我不敢看。 - 注意小数位。 USDT 是 6 位小数,USDC 也是 6 位,但很多代币是 18 位。解析金额时一定要除以 10^decimals,否则数字会大得离谱。
嗯,今天就到这里。记住:解析交易 data 是链上数据分析的基本功。你如果能熟练解码任意一笔交易,那跨链套利的机会识别就成功了一半。
公众号:蓝海资料掘金营,微信 deep3321