You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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 &nbsp; : &nbsp; {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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 13:52:48