EIP-1559 Link to heading

EIP-1559 解决的核心问题:Gas 费机制改革 Link to heading

传统以太坊的 Gas 费机制存在诸多问题:

  • 价格波动剧烈:用户难以预测合理的 Gas 价格,经常出现过高或过低的情况
  • 拍卖机制低效:所有交易通过 Gas 价格竞拍,导致费用不可预测
  • 用户体验差:需要手动设置 Gas 价格,普通用户难以掌握
  • 网络拥堵时费用暴涨:高峰期 Gas 费可能增长数十倍

EIP-1559 在伦敦硬分叉(2021年8月)中引入全新的费用机制:

  1. 引入 基础费用(Base Fee),由协议自动调整,提供费用预测性
  2. 添加 优先费用(Priority Fee),用户可选择支付小费加速交易
  3. 部分 Gas 费被销毁,减少 ETH 供应量,具有通缩效应
  4. 改善用户体验,钱包可以更好地估算合理费用

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()