写一个”清仓转账”的脚本——把地址里所有余额扫到目标地址,用 EIP-1559。核心代码大致这样:
base_fee = w3.eth.get_block("latest")["baseFeePerGas"]
priority_fee = w3.to_wei(0.1, "gwei")
max_fee_per_gas = base_fee + priority_fee
gas_limit = 30000
fee = gas_limit * max_fee_per_gas
amount = balance - fee
tx = {
"to": w3.to_checksum_address(dst),
"value": amount,
"gas": gas_limit,
"maxFeePerGas": max_fee_per_gas,
"maxPriorityFeePerGas": priority_fee,
"nonce": w3.eth.get_transaction_count(from_address),
"chainId": CHAIN_ID,
"type": 2,
}
跑起来经常报:
insufficient funds for gas * price + value
问题不是一处,是好几个细节叠加。
坑 1:maxFeePerGas 写死等于 baseFee + priority
节点校验时:
balance >= value + gasLimit × maxFeePerGas
baseFee 每个区块都会浮动(EIP-1559 每次可变 ±12.5%)。你算完刚好够,几秒后新块 baseFee 涨了:
max_fee < new_base_fee + priority
要么交易过不了、要么”钱不够”。MetaMask 的做法是:
max_fee_per_gas = base_fee * 2 + priority_fee
base_fee * 2 是常规冗余系数——baseFee 一个区块最多涨 12.5%,2x 完全够缓冲。多花的部分节点会退还给你,只按 effectiveGasPrice 收。
坑 2:gasLimit 硬编码
普通转账 21000 够,Gravity 这类链要 30000。合约调用差别就更大了。别写死,先估算再加冗余:
gas_limit = w3.eth.estimate_gas({
"from": from_address,
"to": dst,
"value": 1,
})
gas_limit = int(gas_limit * 1.2)
* 1.2 是通用的 20% 余量,避免 estimation 边缘 case。
坑 3:priority fee 硬编码 0.1 gwei
网络拥堵时 0.1 gwei 的小费根本进不了下一个块。用节点建议值:
priority_fee = w3.eth.max_priority_fee
或者查 feeHistory 取近期百分位:
history = w3.eth.fee_history(5, "latest", [50])
priority_fee = max(history["reward"][-1][0], w3.to_wei(1, "gwei"))
坑 4:nonce 用了 latest
默认:
w3.eth.get_transaction_count(from_address) # 等价于 "latest"
如果地址有未确认的 pending 交易,latest 会返回过时的 nonce,新交易和旧的撞车,两个都卡住。改成:
nonce = w3.eth.get_transaction_count(from_address, "pending")
坑 5:清仓转账没留冗余
amount = balance - fee 是理论极限。实际因为 baseFee 突变或 estimation 偏差,几乎必然凑不够。留一点小额缓冲:
reserve = w3.to_wei(0.0001, "ether")
amount = balance - fee - reserve
坑 6:chainId 抄错
"chainId": 127001,
链 ID 不匹配的交易节点直接拒。跟节点确认一次:
print(w3.eth.chain_id)
修正后的样板
base_fee = w3.eth.get_block("latest")["baseFeePerGas"]
priority_fee = w3.eth.max_priority_fee
max_fee_per_gas = base_fee * 2 + priority_fee
gas_limit = w3.eth.estimate_gas({
"from": from_address,
"to": dst,
"value": 1,
})
gas_limit = int(gas_limit * 1.2)
fee_ceiling = gas_limit * max_fee_per_gas
reserve = w3.to_wei(0.0001, "ether")
amount = balance - fee_ceiling - reserve
if amount <= 0:
raise RuntimeError("余额不足以支付 Gas + 冗余")
tx = {
"to": w3.to_checksum_address(dst),
"value": amount,
"gas": gas_limit,
"maxFeePerGas": max_fee_per_gas,
"maxPriorityFeePerGas": priority_fee,
"nonce": w3.eth.get_transaction_count(from_address, "pending"),
"chainId": w3.eth.chain_id,
"type": 2,
}
一句话总结
EIP-1559 参数不是”算准就行”,是”抗突变才稳”。maxFee = baseFee * 2 + priority、gas = estimate * 1.2、nonce = pending,三件事一起做,出问题的概率会低一个数量级。
