如何让Wagmi useWaitForTransaction同步Hardhat上MetaMask的交易状态?
Wagmi与MetaMask交易状态不同步问题解决
MetaMask本地节点交易的特殊逻辑
- 本地Hardhat节点环境下,MetaMask会先缓存用户签名后的交易哈希,再等待节点打包确认。它的交易状态更新依赖自身的定期轮询机制,而非实时接收节点推送的事件。如果Hardhat挖矿速度过快,MetaMask的轮询还没来得及拉取最新区块,就会出现前端已经拿到确认,但MetaMask仍显示pending的情况。
- MetaMask维护独立的交易历史,只有当它主动查询到交易已被打包进区块时,才会更新状态,这个过程比Wagmi直接从节点获取数据要慢。
你的代码问题点
useWaitForTransaction设置confirmations:1后,Wagmi会在交易被Hardhat节点打包的第一时间返回成功状态,但此时MetaMask的轮询可能还没完成,导致状态不同步。- 组件中仅通过
approveTx?.status判断交易状态,没有考虑MetaMask的状态延迟,导致前端提前显示成功。
解决方案
1. 触发MetaMask强制同步
交易确认后,调用MetaMask的RPC接口触发它拉取最新区块,更新交易状态:
import { ethers } from 'ethers'; const forceMetaMaskSync = async () => { if (window.ethereum) { // 请求最新区块号,触发MetaMask同步 await window.ethereum.request({ method: 'eth_blockNumber' }); } };
在useWaitForTransaction的onSuccess回调中调用这个函数:
const { data: approveTx, isError, isLoading } = useWaitForTransaction({ hash: approveWeth?.data?.hash, confirmations: 1, onSuccess(data) { console.log('approve tx successful data ', data); forceMetaMaskSync(); // 触发同步 }, onError(err) { console.log('approve tx error data ', err); } });
2. 同步MetaMask的交易状态查询
在组件中定期通过MetaMask的provider查询交易状态,直到确认MetaMask已识别交易完成:
import { useState, useEffect } from 'react'; import { ethers } from 'ethers'; function WethApproval() { // 新增状态跟踪MetaMask侧的交易状态 const [metaMaskTxConfirmed, setMetaMaskTxConfirmed] = useState(false); const [approvalLimit, setApprovalLimit] = useState(0); const approveWeth = useContractWrite({ ...wethContractConfig, functionName: 'approve', onSuccess(data) { console.log('approve success hash ', data); } }); const { data: approveTx, isError, isLoading } = useWaitForTransaction({ hash: approveWeth?.data?.hash, confirmations: 1, onSuccess(data) { console.log('approve tx successful data ', data); }, onError(err) { console.log('approve tx error data ', err); } }); // 定期查询MetaMask侧的交易状态 useEffect(() => { let interval; if (approveWeth?.data?.hash) { interval = setInterval(async () => { if (!window.ethereum) return; const provider = new ethers.providers.Web3Provider(window.ethereum); const tx = await provider.getTransaction(approveWeth.data.hash); // 当交易已被打包进区块时,标记MetaMask侧已确认 if (tx?.blockNumber) { setMetaMaskTxConfirmed(true); clearInterval(interval); } }, 1000); } return () => clearInterval(interval); }, [approveWeth.data?.hash]); function ActionButton() { if (approveWeth.isLoading) { return ( <button disabled={true}> Confirm in Metamask </button> ) } if (!approveWeth.data) { return ( <button onClick={() => { approveWeth?.write({ args: [_grantee, _approvalLimit] }) }}> Approve </button> ) } // 优先以MetaMask侧的确认状态为准 if (metaMaskTxConfirmed) { return ( <button disabled={true}> Receipt : {approveTx?.transactionHash} </button> ); } return ( <button disabled={true}>Pending... </button> ) } // 组件其余部分不变... }
3. 调整Hardhat挖矿节奏
修改hardhat.config.js,设置合理的挖矿间隔,给MetaMask足够的同步时间:
module.exports = { networks: { hardhat: { mining: { auto: true, interval: [2000, 4000] // 2-4秒挖一个块,模拟真实链的打包速度 } } } };
总结
问题根源在于Wagmi直接从Hardhat节点获取交易确认,而MetaMask的状态更新依赖自身轮询,两者同步节奏不一致。通过触发MetaMask强制同步、同步查询MetaMask交易状态、调整挖矿节奏,就能让前端状态和MetaMask保持一致。
内容的提问来源于stack exchange,提问作者holangun
相关产品推荐
相关产品推荐

