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] : [], }, }, };
问题原因分析
核心问题出在部署脚本逻辑与构造函数约束冲突:
- 本地Hardhat环境:默认生成10个测试账户,
ethers.provider.listAccounts()返回的数组长度为10,部署时传入的required=2满足构造函数require(_confirmations <= _owners.length)的约束,因此部署成功。 - Fuji测试网/Remix环境:
- Fuji测试网配置中仅传入1个私钥,
listAccounts()返回的数组长度为1; - 部署时依然传入
required=2,违反了构造函数中_confirmations <= _owners.length的要求,导致交易执行失败,进而触发gas估算错误和gasLimit报错。
- Fuji测试网配置中仅传入1个私钥,
解决方案
方案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
相关产品推荐
相关产品推荐

