做链上转账时,想把账户余额全部转走,需要提前扣除 Gas 费。EIP-1559 下的费用计算和普通 Legacy 交易有些区别,容易算错。
常见的错误写法
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
这里有两个问题:
问题一:fee 计算偏高但不算错
gas_limit * max_fee_per_gas 是最坏情况下的费用上限,实际扣款是:
actual_fee = gas_used × (base_fee + priority_fee)
gas_used ≤ gas_limit,所以实际花费通常比预留值少。转账类交易 gas_used 基本等于 gas_limit,误差不大;合约调用误差可能较大。
问题二:maxFeePerGas 应该 > base_fee + priority_fee
EIP-1559 规范里,maxFeePerGas 是你愿意支付的上限,真正矿工收到的是:
effective_gas_price = base_fee + priority_fee
如果下一个区块 base_fee 涨了,用 maxFeePerGas = base_fee + priority_fee 可能导致交易因 maxFeePerGas < 新 base_fee 而被拒绝。
正确写法
def send_all_balance(w3, private_key, to_address, chain_id):
account = w3.eth.account.from_key(private_key)
from_address = account.address
balance = w3.eth.get_balance(from_address)
nonce = w3.eth.get_transaction_count(from_address)
# base_fee 加 20% 缓冲,防止下一块涨价
base_fee = w3.eth.get_block("latest")["baseFeePerGas"]
base_fee_with_buffer = int(base_fee * 1.2)
priority_fee = w3.to_wei(0.1, "gwei")
max_fee_per_gas = base_fee_with_buffer + priority_fee
gas_limit = 21000 # 纯转账固定 21000;合约调用需要 estimateGas
# 预留的 Gas 费上限
max_gas_cost = gas_limit * max_fee_per_gas
amount = balance - max_gas_cost
if amount <= 0:
print(f"余额不足:balance={w3.from_wei(balance, 'ether')} ETH")
return None
tx = {
"from": from_address,
"to": w3.to_checksum_address(to_address),
"value": amount,
"gas": gas_limit,
"maxFeePerGas": max_fee_per_gas,
"maxPriorityFeePerGas": priority_fee,
"nonce": nonce,
"chainId": chain_id,
"type": 2,
}
signed = w3.eth.account.sign_transaction(tx, private_key)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"发送: {w3.from_wei(amount, 'ether')} ETH")
print(f"Gas 预留: {w3.from_wei(max_gas_cost, 'ether')} ETH")
print(f"TX: {tx_hash.hex()}")
return tx_hash.hex()
EIP-1559 费用结构
| 字段 | 含义 |
|---|---|
baseFeePerGas | 协议自动设置,每笔交易必须支付,销毁 |
maxPriorityFeePerGas | 给矿工的小费,激励打包 |
maxFeePerGas | 用户愿意支付的上限 |
| 实际每 Gas 费用 | min(maxFeePerGas, baseFee + priorityFee) |
实际从账户扣除:
actual_cost = gas_used × effective_gas_price
其中 effective_gas_price = baseFee + priorityFee(不超过 maxFeePerGas)。
多余的 (maxFeePerGas - effectiveGasPrice) × gasUsed 会退还给发送方,但退还发生在交易上链后,所以发送前账户余额必须 ≥ gasLimit × maxFeePerGas。
纯转账 vs 合约调用
纯 ETH 转账 gas 固定是 21000,可以直接写死。合约调用要用 estimate_gas:
gas_limit = w3.eth.estimate_gas({
"from": from_address,
"to": contract_address,
"data": encoded_data,
})
gas_limit = int(gas_limit * 1.1) # 加 10% 缓冲
合约执行失败时 gas 仍会被扣,所以建议先 call 验证不会 revert 再发送。
常见的链 gas_limit
不同链的标准转账 gas 不完全一样,普通 EVM 链一般是 21000,部分 L2 有所不同:
GAS_LIMITS = {
"ethereum": 21000,
"polygon": 21000,
"bsc": 21000,
"gravity": 30000, # 某些链会高一些
}
不确定的情况下用 estimate_gas 最安全。
一句话总结
EIP-1559 转账预留费用用 gasLimit × maxFeePerGas,maxFeePerGas 要比当前 baseFee 高出一定缓冲(一般 20%),防止下一块 baseFee 上涨导致交易失败。纯转账 gas 固定 21000,合约调用用 estimateGas。
