想在 DApp 里优雅地处理以太坊交易?无论是 Wagmi 的 React Hook 魔力,还是原生 JavaScript 的灵活组合,本文围绕 MetaMask 交易、Wagmi发送交易、Gas估算、交易状态追踪 等核心关键词,提供可落地的完整范式与防坑须知。
什么场景需要 MetaMask 交易 SDK
开发者常遇到这些槽点:
用户点击按钮却迟迟看不到结果,Gas 报溢,交易被链上回滚。借助 MetaMask SDK,你可以:
- 实时追踪交易状态,用户不再一脸懵。
- 精准估算 Gas,避免“Gas不足”红色弹窗。
- 人性化错误提示,挽回手滑点“拒绝”或余额不足的尴尬。
- 支持多重签名、批量交易 等高级模式。
Wagmi:三步写出发送交易的 React 组件
Wagmi 提供了功能完备的 React Hook,开箱即用。下面给出两种典型场景:标准转账与带 Gas 估算的合约交互。
1. 基础转账:0.1 ETH 极速到账
import { parseEther } from 'viem'
import { useSendTransaction, useWaitForTransactionReceipt } from 'wagmi'
export default function SendTransaction() {
const { data: hash, error, isPending, sendTransaction } = useSendTransaction()
const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({ hash })
const handleSend = async () => {
sendTransaction({ to: '0xRecipientAddress', value: parseEther('0.1') })
}
return (
<>
<button disabled={isPending} onClick={handleSend}>
{isPending ? '等待签名中…' : '发送 0.1 ETH'}
</button>
{hash && (
<p>
交易哈希:{hash.slice(0, 10)}…
{isConfirming && '⏳ 等待确认…'}
{isConfirmed && '✅ 交易确认成功!'}
</p>
)}
{error && <p style={{ color: 'red' }}>错误:{error.shortMessage}</p>}
</>
)
}
- 关键词贯穿:组件暴露的
isPending
、isConfirmed
等布尔值即是“交易状态追踪”的核心指标。 - 用户体验:按钮禁用、状态图标,减少重复点击。
2. 进阶用法:动态 Gas 估算 + 合约交互
import {
useSendTransaction,
useWaitForTransactionReceipt,
useEstimateGas,
} from 'wagmi'
export default function AdvancedTransaction() {
const tx = { to: '0xDaiContract', data: '0x1234', value: 0n }
// 1. 预先估算 Gas,无需死记硬背
const { data: gasEstimate } = useEstimateGas(tx)
// 2. 发送时带上估算值
const { sendTransaction } = useSendTransaction({
...tx,
gas: gasEstimate,
onSuccess: (hash) => console.log('交易已广播', hash),
})
return (
<button onClick={() => sendTransaction()}>
使用 Gas 估算发交易
</button>
)
}
要点:
useEstimateGas
提前避免“out of gas”浪费。- 返回值自动订阅区块事件,完成“交易状态追踪”,开发者无感更新。
原生 JavaScript:无框架的扫码即用方案
如果你开发的是纯 HTML 页面或小程序,排斥 React,也可以直接用浏览器注入的 ethereum
Provider 处理交易。
1. 标准转账 RPC 组合
async function sendTransaction(recipient, amount) {
try {
const accounts = await ethereum.request({ method: 'eth_requestAccounts' })
const from = accounts[0]
// 金额转 wei
const value = `0x${(amount * 1e18).toString(16)}`
const tx = { from, to: recipient, value }
const txHash = await ethereum.request({
method: 'eth_sendTransaction',
params: [tx],
})
return txHash
} catch (err) {
if (err.code === 4001) throw new Error('用户已拒绝交易')
throw err
}
}
2. 轮询交易回执
function watchTransaction(txHash) {
return new Promise((resolve, reject) => {
const check = async () => {
const receipt = await ethereum.request({
method: 'eth_getTransactionReceipt',
params: [txHash],
})
if (receipt) {
receipt.status === '0x1' ? resolve(receipt) : reject('交易失败')
} else {
setTimeout(check, 2000)
}
}
check()
})
}
3. 动态 Gas 估算
async function estimateGas(txParams) {
const gasHex = await ethereum.request({
method: 'eth_estimateGas',
params: [txParams],
})
const safeGas = BigInt(gasHex) * 120n / 100n // 20% 缓冲
return safeGas.toString(16)
}
执行流程:用户点击按钮 → JS 弹 MetaMask → 监听器每隔 2 秒轮询 receipt → 页面更新提示“交易确认成功”。
最佳实践:稳稳避免翻车
关注点 | 推荐方案 |
---|---|
交易安全 | 所有地址做正则校验,到账前二次弹窗让用户确认金额、地址。 |
错误码处理 | 4001 用户拒绝、-32603 余额不足,需要给出具体下一步,比如换网络或充值。 |
Gas估算策略 | 在估算值上加 20% 缓冲,夜间高峰时可考虑 30%。 |
并发限制 | 同一时间只能有一笔 pending,设置全局锁防止重复发送。 |
FAQ:5 个碉堡级高频疑问
Q1:为什么 MetaMask 估算的 Gas 与实际不符?
A:合约内部逻辑可能会随着合约状态变化而变化,建议保守地加 10~20% 的 buffer。若链上拥堵,可再提升 gasPrice 而非单纯提高 gasLimit。
Q2:交易pending 卡住怎么办?
A:在状态监听里,若超过 5 分钟仍未确认,提示用户手动 SPEED UP
(MetaMask 内置加速器),或调用 eth_getTransactionByHash
读取 gasPrice 做比较。
Q3:Wagmi 能在服务端渲染(SSR)里用吗?
A:Wagmi 依赖客户端钱包方法,建议在浏览器端执行;Next.js 场景可使用 dynamic(() => import(..), { ssr: false })
做懒加载。
Q4:弹窗中如何展示“等待区块确认”动画?
A:利用 isConfirming
布尔值,结合 CSS 加载条形成“从0到6个确认”的渐进式提示,可同步展示当前 blockNumber
与目标差异。
Q5:用 vanilla JS 可以做批量交易吗?
A:可以。反复调用 eth_sendTransaction
并在前端做状态映射即可,但需仔细控制 nonce 顺序;若使用 EIP 1559,可构造 maxPriorityFeePerGas
、maxFeePerGas
双字段。
下一步:追加功能升级路线
- 用户认证:二维码登录 + SIWE(Sign-In with Ethereum)。
- 网络管理:一键切到 Polygon、Arbitrum 等多链。
- 合约交互:封装通用 CallData Builder,减少低爆击手工编码。
看完这篇,你已经在 MetaMask 交易、Wagmi发送交易、Gas估算、交易状态追踪 四大维度迈入高手行列。祝你在主网上线 DApp 十次无事故!