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

Fuji测试网部署MultiSig合约遇gasLimit错误求助

MultiSig多签合约部署异常排查

本地用Hardhat部署MultiSig合约可正常运行,测试全部通过,但部署到Fuji测试网时触发gasLimit错误;在Remix中部署时返回错误:Gas estimation errored with the following message (see below). The transaction execution will likely fail。

附上相关代码及配置,请求排查问题:

合约代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

contract MultiSig {
    address[] public owners;
    uint public transactionCount;
    uint public required;

    event Confirmation(address indexed sender, uint indexed transactionId);
    event Submission(uint indexed transactionId);
    event Execution(uint indexed transactionId);
    event Deposit(address indexed sender, uint value);

    struct Transaction {
        address payable destination;
        uint value;
        bool executed;
        bytes data;
    }

    mapping(uint => Transaction) public transactions;
    mapping(uint => mapping(address => bool)) public confirmations;

    receive() payable external {
        emit Deposit(msg.sender, msg.value);
    }

    function getOwners() view public returns(address[] memory) {
        return owners;
    }

    function getTransactionIds(bool pending, bool executed) view public returns(uint[] memory) {
        uint count = getTransactionCount(pending, executed);
        uint[] memory txIds = new uint[](count);
        uint runningCount = 0;
        for(uint i = 0; i < transactionCount; i++) {
            if(pending && !transactions[i].executed ||
                executed && transactions[i].executed) {
                txIds[runningCount] = i;
                runningCount++;
            }
        }
        return txIds;
    }

    function getTransactionCount(bool pending, bool executed) view public returns(uint) {
        uint count = 0;
        for(uint i = 0; i < transactionCount; i++) {
            if(pending && !transactions[i].executed ||
                executed && transactions[i].executed) {
                count++;
            }
        }
        return count;
    }

    function executeTransaction(uint transactionId) public {
        require(isConfirmed(transactionId));
        emit Execution(transactionId);
        Transaction storage _tx = transactions[transactionId];
        (bool success, ) = _tx.destination.call{ value: _tx.value }(_tx.data);
        require(success, "Failed to execute transaction");
        _tx.executed = true;
    }

    function isConfirmed(uint transactionId) public view returns(bool) {
        return getConfirmationsCount(transactionId) >= required;
    }

    function getConfirmationsCount(uint transactionId) public view returns(uint) {
        uint count;
        for(uint i = 0; i < owners.length; i++) {
            if(confirmations[transactionId][owners[i]]) {
                count++;
            }
        }
        return count;
    }

    function getConfirmations(uint transactionId) public view returns(address[] memory) {
        address[] memory confirmed = new address[](getConfirmationsCount(transactionId));
        uint runningConfirmed;
        for(uint i = 0; i < owners.length; i++) {
            if(confirmations[transactionId][owners[i]]) {
                confirmed[runningConfirmed] = owners[i];
                runningConfirmed++;
            }
        }
        return confirmed;
    }

    function isOwner(address addr) private view returns(bool) {
        for(uint i = 0; i < owners.length; i++) {
            if(owners[i] == addr) {
                return true;
            }
        }
        return false;
    }

    function submitTransaction(address payable dest, uint value, bytes memory data) public {
        uint id = addTransaction(dest, value, data);
        confirmTransaction(id);
        emit Submission(id);
    }

    function confirmTransaction(uint transactionId) public {
        require(isOwner(msg.sender));
        emit Confirmation(msg.sender, transactionId);
        confirmations[transactionId][msg.sender] = true;
        if(isConfirmed(transactionId)) {
            executeTransaction(transactionId);
        }
    }

    function addTransaction(address payable destination, uint value, bytes memory data) public returns(uint) {
        transactions[transactionCount] = Transaction(destination, value, false, data);
        transactionCount += 1;
        return transactionCount - 1;
        
    }

    constructor(address[] memory _owners, uint _confirmations) {
        require(_owners.length > 0);
        require(_confirmations > 0);
        require(_confirmations <= _owners.length);
        owners = _owners;
        required = _confirmations;
    }
}

部署脚本 deploy.js

const hre = require("hardhat");

async function main() {
  const accounts = await ethers.provider.listAccounts();
  const MultiSig = await ethers.getContractFactory("MultiSig");
  const multiSig = await MultiSig.deploy(accounts, 2);

  await multiSig.deployed();

  console.log("MultiSig deployed to:", multiSig.address);
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

配置文件 hardhat.config.js

require('@nomiclabs/hardhat-ethers');
require('@openzeppelin/hardhat-upgrades');
require("@nomiclabs/hardhat-waffle");
require("dotenv").config()


const FUJI_RPC_URL =
  process.env.FUJI_RPC_URL

const PRIVATE_KEY = 
  process.env.PRIVATE_KEY

const SUBNET_RPC_URL =
  process.env.SUBNET_RPC_URL

const PRIVATE_KEY_ADMIN = 
  process.env.PRIVATE_KEY_ADMIN

module.exports = {
  solidity: "0.8.15",
  paths: {
    artifacts: "./src/backend/artifacts",
    sources: "./src/backend/contracts",
    cache: "./src/backend/cache",
    tests: "./src/backend/test"
    },
  defaultNetwork: 'hardhat',
  networks: {
    hardhat: {
    chainId: 1337 // We set 1337 to make interacting with MetaMask simpler
    },
    fuji: {
    url: FUJI_RPC_URL,
    accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
    },
    subnet: {
    url: SUBNET_RPC_URL,
    accounts: PRIVATE_KEY_ADMIN !== undefined ? [PRIVATE_KEY_ADMIN] : [],
    },
  },
};

问题原因分析

核心问题出在部署脚本逻辑与构造函数约束冲突:

  1. 本地Hardhat环境:默认生成10个测试账户,ethers.provider.listAccounts()返回的数组长度为10,部署时传入的required=2满足构造函数require(_confirmations <= _owners.length)的约束,因此部署成功。
  2. Fuji测试网/Remix环境:
    • Fuji测试网配置中仅传入1个私钥,listAccounts()返回的数组长度为1;
    • 部署时依然传入required=2,违反了构造函数中_confirmations <= _owners.length的要求,导致交易执行失败,进而触发gas估算错误和gasLimit报错。

解决方案

方案1:手动指定多签所有者账户

修改部署脚本,不再依赖listAccounts(),手动传入多个所有者地址,确保数量≥需要的确认数:

async function main() {
  // 替换为你实际的多签所有者地址数组
  const owners = ["0x...", "0x...", "0x..."];
  const requiredConfirmations = 2;

  const MultiSig = await ethers.getContractFactory("MultiSig");
  const multiSig = await MultiSig.deploy(owners, requiredConfirmations);

  await multiSig.deployed();
  console.log("MultiSig deployed to:", multiSig.address);
}

方案2:根据网络动态调整参数

针对不同网络设置不同的所有者数组和确认数,本地保留原逻辑,Fuji测试网手动指定:

async function main() {
  let owners;
  const requiredConfirmations = 2;

  if (hre.network.name === "hardhat") {
    owners = await ethers.provider.listAccounts();
  } else {
    // Fuji测试网指定多个所有者地址
    owners = ["0x...", "0x..."];
  }

  const MultiSig = await ethers.getContractFactory("MultiSig");
  const multiSig = await MultiSig.deploy(owners, requiredConfirmations);

  await multiSig.deployed();
  console.log("MultiSig deployed to:", multiSig.address);
}

方案3:调整Hardhat网络配置

如果希望在Fuji测试网使用多个账户,在hardhat.config.js的fuji网络配置中传入多个私钥:

fuji: {
  url: FUJI_RPC_URL,
  accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY, "第二个私钥", "第三个私钥"] : [],
},

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 08:40:35