EIP-1559 Link to heading
EIP-1559 解决的核心问题:Gas 费机制改革 Link to heading
传统以太坊的 Gas 费机制存在诸多问题:
- 价格波动剧烈:用户难以预测合理的 Gas 价格,经常出现过高或过低的情况
- 拍卖机制低效:所有交易通过 Gas 价格竞拍,导致费用不可预测
- 用户体验差:需要手动设置 Gas 价格,普通用户难以掌握
- 网络拥堵时费用暴涨:高峰期 Gas 费可能增长数十倍
EIP-1559 在伦敦硬分叉(2021年8月)中引入全新的费用机制:
- 引入 基础费用(Base Fee),由协议自动调整,提供费用预测性
- 添加 优先费用(Priority Fee),用户可选择支付小费加速交易
- 部分 Gas 费被销毁,减少 ETH 供应量,具有通缩效应
- 改善用户体验,钱包可以更好地估算合理费用
EIP-1559 通用交易结构 Link to heading
新的 Type 2 交易结构:
// EIP-1559 交易参数
{
Transaction {
to: Address // 交易的接收者
nonce: Hex // 发送者的nonce(已发送交易数量的计数)
type: Hex // 交易类型, 0(legcy) 或 1(EIP-2930) 或 2(EIP-1559)
value: Hex // 交易携带的主币数量, 单位是 wei
data: Hex // 交易携带的数据 (ABI 编码)
maxPriorityFeePerGas?: Hex // EIP-1559:每单位 gas 优先费用, type=2时提供
maxFeePerGas?: Hex // EIP-1559:每单位 gas 最大费用, type=2时提供
gas: Hex // 可使用的最大 gas 数量(gasLimit)
}
}
费用计算公式:
实际费用 = min(maxFeePerGas, baseFee + maxPriorityFeePerGas) × gasUsed
销毁费用 = baseFee × gasUsed
矿工收入 = min(maxPriorityFeePerGas, maxFeePerGas - baseFee) × gasUsed
基础费用调整机制:
新baseFee = 当前baseFee × (1 + (gasUsed - gasTarget) / gasTarget / 8)
RLP 序列化、HASH、签名 Link to heading
广播交易、节点验证、执行打包 Link to heading
-
RPC 节点收到交易
-
验证:签名正确、确保交易费够、GasLimit 不超限制、chainID 匹配、Nonce
是否符合预期
-
放入交易池(mempool)
-
从交易池取交易(一般按交易手续费排序)、EVM 执行
Example Link to heading
import { createWalletClient, http, parseEther, parseGwei, type Hash, type TransactionReceipt } from 'viem'
import { prepareTransactionRequest } from 'viem/actions'
import { mnemonicToAccount, privateKeyToAccount, type PrivateKeyAccount } from 'viem/accounts'
import { foundry } from 'viem/chains'
import { createPublicClient, type PublicClient, type WalletClient } from 'viem'
import dotenv from 'dotenv'
dotenv.config()
async function sendTransactionExample(): Promise<Hash> {
try {
// const privateKey = generatePrivateKey()
// 1. 从环境变量获取私钥
const privateKey = process.env.PRIVATE_KEY as `0x${string}`
if (!privateKey) {
throw new Error('请在 .env 文件中设置 PRIVATE_KEY')
}
// 使用助记词 推导账户
// const account = mnemonicToAccount('xxxx xxx ')
// 使用私钥 推导账户
const account: PrivateKeyAccount = privateKeyToAccount(privateKey)
const userAddress = account.address
console.log('账户地址:', userAddress)
// 创建公共客户端
const publicClient: PublicClient = createPublicClient({
chain: foundry,
transport: http(process.env.RPC_URL)
})
// 检查网络状态
const blockNumber = await publicClient.getBlockNumber()
console.log('当前区块号:', blockNumber)
// 获取当前 gas 价格
const gasPrice = await publicClient.getGasPrice()
console.log('当前 gas 价格:', parseGwei(gasPrice.toString()))
// 查询余额
const balance = await publicClient.getBalance({
address: userAddress
})
console.log('账户余额:', parseEther(balance.toString()))
// 查询nonce
const nonce = await publicClient.getTransactionCount({
address: userAddress
})
console.log('当前 Nonce:', nonce)
// 2. 构建交易参数
const txParams = {
account: account,
to: '0x01BF49D75f2b73A2FDEFa7664AEF22C86c5Be3df' as `0x${string}`, // 目标地址
value: parseEther('0.001'), // 发送金额(ETH)
chainId: foundry.id,
type: 'eip1559' as const, // 使用 const 断言确保类型正确
chain: foundry, // 添加 chain 参数
// EIP-1559 交易参数
maxFeePerGas: gasPrice * 2n, // 最大总费用为当前 gas 价格的 2 倍
maxPriorityFeePerGas: parseGwei('1.5'), // 最大小费
gas: 21000n, // gas limit
nonce: nonce,
}
// 或 自动 Gas 估算 及参数验证和补充
const preparedTx = await prepareTransactionRequest(publicClient, txParams)
console.log('准备后的交易参数:', {
...preparedTx,
maxFeePerGas: parseGwei(preparedTx.maxFeePerGas.toString()),
maxPriorityFeePerGas: parseGwei(preparedTx.maxPriorityFeePerGas.toString()),
})
// 创建钱包客户端
const walletClient: WalletClient = createWalletClient({
account: account,
chain: foundry,
transport: http(process.env.RPC_URL)
})
// // 方式 1:直接发送交易
const txHash1 = await walletClient.sendTransaction(preparedTx)
console.log('交易哈希:', txHash1)
// 方式 2 :
// 签名交易
const signedTx = await walletClient.signTransaction(preparedTx)
console.log('Signed Transaction:', signedTx)
// 发送交易 eth_sendRawTransaction
const txHash = await publicClient.sendRawTransaction({
serializedTransaction: signedTx
})
console.log('Transaction Hash:', txHash)
// 等待交易确认
const receipt: TransactionReceipt = await publicClient.waitForTransactionReceipt({ hash: txHash })
console.log('交易状态:', receipt.status === 'success' ? '成功' : '失败')
console.log('区块号:', receipt.blockNumber)
console.log('Gas 使用量:', receipt.gasUsed.toString())
return txHash
} catch (error) {
console.error('错误:', error)
if (error instanceof Error) {
console.error('错误信息:', error.message)
}
if (error && typeof error === 'object' && 'details' in error) {
console.error('错误详情:', error.details)
}
throw error
}
}
// 执行示例
sendTransactionExample()