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

如何捕获web3.js中_fireError抛出的未处理交易回滚错误?

问题

调用web3.js的mintBatch方法时,交易被EVM回滚,但自定义的错误捕获逻辑未触发——既没打印"mintBatch failed",还出现未处理运行时错误。

调用代码

contract.methods.mintBatch(quantity, data.nonce, data.hash, data.signature, data.nftIds, 1)
  .send({ from: walletId, value: res * quantity }, (err: any, res: any) => {
    if (err) {
      console.error('mintBatch failed, error: ', err);
      reject();
    } else {
      console.log('mintBatch succeed, res: ', res);
      resolve({
        txHash: res,
        nftIds: data.nftIds
      });
    }
  })

未处理错误信息

Unhandled Runtime Error
Error: Transaction has been reverted by the EVM:
{
"blockHash": "0x2d7a0a4e5469e68250eaee0c52eb80559d6f9e2a8873ab4f7b2f47e5e9b973b3",
"blockNumber": 11284056,
"contractAddress": null,
"cumulativeGasUsed": 368714,
"effectiveGasPrice": 2500000009,
"from": "somewhere",
"gasUsed": 76047,
"logsBloom": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": false,
"to": "somewhere else",
"transactionHash": "a random transaction hash",
"transactionIndex": 2,
"type": "0x2",
"events": {}
}

错误来源:web3.js内部_fireError函数

var _fireError = function (error, emitter, reject, callback, optionalData) {
    /*jshint maxcomplexity: 10 */
    // add data if given
    if (!!error && typeof error === 'object' && !(error instanceof Error) && error.data) {
        if (!!error.data && typeof error.data === 'object' || Array.isArray(error.data)) {
            error.data = JSON.stringify(error.data, null, 2);
        }
        error = error.message + "\n" + error.data;
    }
    if (typeof error === 'string') {
        error = new Error(error);
    }
    if (typeof callback === 'function') {
        callback(error, optionalData);
    }
    if (typeof reject === 'function') {
        // suppress uncatched error if an error listener is present
        // OR suppress uncatched error if an callback listener is present
        if (emitter &&
            (typeof emitter.listeners === 'function' &&
                emitter.listeners('error').length) || typeof callback === 'function') {
            emitter.catch(function () { });
        }
        // reject later, to be able to return emitter
        setTimeout(function () {
            reject(error);
        }, 1);
    }
    if (emitter && typeof emitter.emit === 'function') {
        // emit later, to be able to return emitter
        setTimeout(function () {
            emitter.emit('error', error, optionalData);
            emitter.removeAllListeners();
        }, 1);
    }
    return emitter;
};

解决方案

从_fireError的逻辑来看,错误会同时触发callback、reject和emitter的error事件,但原代码未覆盖所有触发路径,导致未捕获错误。可按以下方式处理:

1. 同时监听send()返回的Promise和error事件

web3.js的send()返回PromiEvent(兼具Promise和事件发射器特性),需同时处理两种错误触发方式:

contract.methods.mintBatch(quantity, data.nonce, data.hash, data.signature, data.nftIds, 1)
  .send({ from: walletId, value: res * quantity })
  .on('error', (err) => {
    console.error('mintBatch failed (event listener): ', err);
    reject(err);
  })
  .then((receipt) => {
    console.log('mintBatch succeed, receipt: ', receipt);
    resolve({
      txHash: receipt.transactionHash,
      nftIds: data.nftIds
    });
  })
  .catch((err) => {
    console.error('mintBatch failed (promise catch): ', err);
    reject(err);
  });

2. 避免重复触发reject

若代码包裹在new Promise()中,需标记错误已处理,防止重复调用reject:

const mintPromise = new Promise((resolve, reject) => {
  const sendPromiEvent = contract.methods.mintBatch(quantity, data.nonce, data.hash, data.signature, data.nftIds, 1)
    .send({ from: walletId, value: res * quantity });

  let isRejected = false;
  const handleError = (err) => {
    if (!isRejected) {
      console.error('mintBatch failed: ', err);
      isRejected = true;
      reject(err);
    }
  };

  sendPromiEvent.on('error', handleError);
  sendPromiEvent.catch(handleError);

  sendPromiEvent.then((receipt) => {
    console.log('mintBatch succeed, receipt: ', receipt);
    resolve({
      txHash: receipt.transactionHash,
      nftIds: data.nftIds
    });
  });
});

3. 组件层面捕获异步错误

web3.js用setTimeout异步触发错误,React组件需显式处理Promise错误:

const handleMint = async () => {
  try {
    const result = await mintPromise;
    // 处理成功逻辑
  } catch (err) {
    console.error('Mint failed: ', err);
    // 向用户展示错误提示
  }
};

也可使用React的ErrorBoundary组件,捕获组件树中所有未处理的JavaScript错误。


内容的提问来源于stack exchange,提问作者Terry Windwalker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 12:24:47