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

如何使用ethers处理NestJS中区块链连续交易的nonce问题?

解决NestJS+Ethers.js多交易并发下的Nonce问题

问题场景

你在NestJS项目中实现了activateCompany函数,会连续调用addController和increaseAllowance两个区块链操作,但每个操作都重新创建ethers.Wallet实例,导致多交易并发时出现nonce值过低的错误,手动传入布尔值控制nonce的方式在并发场景下失效。

你的核心代码如下:

activateCompany函数

async activateCompany(data: CompanyActivateRequest): Promise<void> {
    (...)
    await this.blockchain.addController(newWallet.address);
    await this.blockchain.increaseAllowance(newWallet.address,data.monthlyBalance);
    (...)
}

addController函数

async addController(walletAddress: string): Promise<void> {
    try {
        const wallet = new ethers.Wallet(process.env.OWNER_PRIVATE_KEY,this._provider);
        const contract = new ethers.Contract(process.env.CONTRACT_ADDRESS, this._abi, wallet);
        await contract.addController(walletAddress);
    } catch (error) {
        throw new InternalServerErrorException(['blockchain-error']);
    }
}

increaseAllowance函数

async increaseAllowance(walletAddress: string, amount: number, increaseNonce: boolean): Promise<void> {
    try {
        const wallet = new ethers.Wallet(process.env.OWNER_PRIVATE_KEY, this._provider);
        const contract = new ethers.Contract(process.env.CONTRACT_ADDRESS, this._abi, wallet);
        await contract.increaseAllowance(walletAddress, amount);
    } catch (error) {
        throw new InternalServerErrorException(['blockchain-error']);
    }
}

问题根源

每次调用区块链操作时都重新创建ethers.Wallet实例,会导致每个实例独立获取nonce值,并发场景下多个实例可能拿到相同的nonce,后发送的交易会因为nonce过低被拒绝。手动修改nonce的方式无法应对动态变化的交易队列,自然失效。

解决方案

1. 复用Wallet与Contract实例

在Blockchain服务的生命周期内只初始化一次Wallet和Contract,让ethers自动管理nonce——每次发送交易后,Wallet会自动递增nonce,并发场景下会将交易加入队列按顺序发送,从根本上避免nonce冲突。

修改后的Blockchain服务示例:

import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { ethers } from 'ethers';

@Injectable()
export class BlockchainService {
  private _provider: ethers.providers.JsonRpcProvider;
  private readonly wallet: ethers.Wallet;
  private readonly contract: ethers.Contract;

  constructor() {
    // 初始化Provider(匹配你的Hardhat本地RPC地址)
    this._provider = new ethers.providers.JsonRpcProvider(process.env.HARDHAT_RPC_URL || 'http://localhost:8545');
    // 仅初始化一次Wallet
    this.wallet = new ethers.Wallet(process.env.OWNER_PRIVATE_KEY, this._provider);
    // 仅初始化一次Contract
    this.contract = new ethers.Contract(process.env.CONTRACT_ADDRESS, this._abi, this.wallet);
  }

  async addController(walletAddress: string): Promise<void> {
    try {
      await this.contract.addController(walletAddress);
    } catch (error) {
      this.handleNonceError(error, () => this.contract.addController(walletAddress));
    }
  }

  async increaseAllowance(walletAddress: string, amount: number): Promise<void> {
    try {
      // 注意:如果合约使用wei单位,需将金额转换为wei(示例假设是18位小数的代币)
      const weiAmount = ethers.utils.parseUnits(amount.toString(), 18);
      await this.contract.increaseAllowance(walletAddress, weiAmount);
    } catch (error) {
      this.handleNonceError(error, () => this.contract.increaseAllowance(walletAddress, ethers.utils.parseUnits(amount.toString(), 18)));
    }
  }

  /**
   * 统一处理nonce过低错误,自动重试
   */
  private async handleNonceError(error: any, retryFn: () => Promise<any>) {
    if (error.message?.includes('nonce too low')) {
      // 获取当前钱包的pending状态交易数,重置Wallet的nonce
      const currentNonce = await this._provider.getTransactionCount(this.wallet.address, 'pending');
      this.wallet.nonce = currentNonce;
      // 重试交易
      await retryFn();
    } else {
      throw new InternalServerErrorException(['blockchain-error']);
    }
  }
}

2. 优化并发交易处理

复用Wallet实例后,ethers会自动将并发的交易加入内部队列,按顺序发送并维护nonce的连续性,无需手动干预。如果遇到极端场景下的nonce同步问题,上述代码中的handleNonceError方法会自动重置nonce并重试交易。

3. 注意金额单位转换

你的increaseAllowance函数中直接传入amount数值,如果合约要求的是wei单位(绝大多数以太坊生态合约都是如此),需要用ethers.utils.parseUnits将金额转换为对应的wei值,否则会出现金额错误或交易失败。

额外提示

  • 避免手动修改nonce:除非有特殊业务需求,否则不要手动干预Wallet的nonce值,依赖ethers的自动管理即可。
  • 测试并发场景:可以用Postman或编写单元测试模拟多请求并发,验证修改后的代码是否解决nonce问题。

内容的提问来源于stack exchange,提问作者Tomás Martínez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 23:17:12