【Solidity】Gas estimation failed-Gas estimation errored with the following message

本文介绍了一种在Solidity智能合约开发过程中遇到的Gas估算失败问题及其解决方案。通过改变数据存储结构,从直接使用地址映射到成绩列表转变为使用索引映射的方式,成功避免了Gas估算错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在写完合约在remix部署测试的时候遇到了这么一个问题,一旦向合约里写数据就出现如下错误:
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
如下图所示:
这里写图片描述
网上一直搜索不到解决方法,最后经过自己的一番折腾后,还是解决了!
先说一下想实现的功能,尝试用合约存储四六级成绩,因为可以多次参加考试,所以每个人可以有多次四六级成绩记录。
原先合约代码:

pragma solidity ^0.4.23;
import "./Ownable.sol";
contract StudentFactory is Ownable{
    struct CET4{
        uint32 time; 
        uint32 grade;
    }

    struct CET6{
        uint32 time; 
        uint32 grade;
    }
    mapping (address=>CET4[]) public addrToCET4; 
    mapping (address=>CET6[]) public addrToCET6;
    mapping (address=>uint) public addrCET4Count; 
    mapping (address=>uint) public addrCET6Count; 
}

contract StudentHelper is StudentFactory{
    function addCET4To(address _addr,uint32 _time,uint32 _grade) public onlyOwner{
        CET4[] storage CET4List = addrToCET4[_addr]; 
        CET4List[CET4List.length] = CET4(_time,_grade);
        addrCET4Count[_addr]++;
    }

    function addCET6To(address _addr,uint32 _time,uint32 _grade) public onlyOwner{
        CET6[] storage CET6List = addrToCET6[_addr]; 
        CET6List[CET6List.length] = CET6(_time,_grade);
        addrCET6Count[_addr]++;
    }
}

虽然编译没问题,但是一旦调用addCET4To来写入成绩的时候就会出现Gas estimation failedERROR,根据字面意思看是因为计算不出来所需GAS的费用,即使强制执行transaction也会失败,博主也是小白,推测是GAS开销太大了导致的问题,期待有哪位大佬能出详细的原因解释。

后来博主改变了思路,引入了CET4ListCET6List来存储成绩列表,并使mapping (address=>CET4[]) public addrToCET4mapping (address=>CET6[]) public addrToCET6变为mapping (uint=>address) internal CET4IndexToAddrmapping (uint=>address) internal CET6IndexToAddr
先通过for循环找出符合地址的CET4[]CET6[]的索引,再用索引去访问数组中具体的成绩。合约代码如下所示:

pragma solidity ^0.4.23;
import "./Ownable.sol";
contract StudentFactory is Ownable{
    struct CET4{
        uint32 time; 
        uint32 grade;
    }

    struct CET6{
        uint32 time; 
        uint32 grade;
    }
    CET4[] CET4List; // 四级成绩列表
    CET6[] CET6List; // 六级成绩列表
    mapping (uint=>address) internal CET4IndexToAddr; // 四级成绩序号到地址的映射
    mapping (uint=>address) internal CET6IndexToAddr; // 六级成绩序号到地址的映射
    mapping (address=>uint) public addrCET4Count; 
    mapping (address=>uint) public addrCET6Count; 
}

contract StudentHelper is StudentFactory{
    // 给某个地址添加四级成绩记录
    function addCET4To(address _addr,uint32 _time,uint32 _grade) public onlyOwner{
        uint index = CET4List.push(CET4(_time,_grade))-1;
        CET4IndexToAddr[index] = _addr;
        addrCET4Count[_addr]++;
    }

    // 给某个地址添加六级成绩记录
    function addCET6To(address _addr,uint32 _time,uint32 _grade) public onlyOwner{
        uint index = CET6List.push(CET6(_time,_grade))-1;
        CET6IndexToAddr[index] = _addr;
        addrCET6Count[_addr]++;
    }

    // 获得某个地址的四级成绩
    function getCET4ByAddr(address _addr) view public returns (uint32[],uint32[]) {
        uint32[] memory timeList = new uint32[](addrCET4Count[_addr]); 
        uint32[] memory gradeList = new uint32[](addrCET4Count[_addr]);
        uint counter = 0;    
        for (uint i = 0; i < CET4List.length; i++) {
            if(CET4IndexToAddr[i]==_addr){
                timeList[counter] = CET4List[i].time;
                gradeList[counter] = CET4List[i].grade;
                counter++;
            }
        }
        return(timeList,gradeList);
    }

    // 获得某个地址的六级成绩
    function getCET6ByAddr(address _addr) view public returns (uint32[],uint32[]) {
        uint32[] memory timeList = new uint32[](addrCET6Count[_addr]); 
        uint32[] memory gradeList = new uint32[](addrCET6Count[_addr]);
        uint counter = 0;    
        for (uint i = 0; i < CET6List.length; i++) {
            if(CET6IndexToAddr[i]==_addr){
                timeList[counter] = CET6List[i].time;
                gradeList[counter] = CET6List[i].grade;
                counter++;
            }
        }
        return(timeList,gradeList);
    }
}

问题解决~

<think>好的,我现在需要帮助用户解决Remix部署合约时出现的Gas估算失败的问题。首先,我得回顾用户提到的引用内容,尤其是引用[2],里面提到部署程序太大导致所需Gas超过区块上限,无法部署合约,并且使用MetaMask测试时会弹出警告,强行执行也没用。这说明问题可能出在合约的大小或Gas Limit的设置上。 接下来,我需要整理可能导致Gas估算失败的几个常见原因。根据经验,合约代码量过大、构造函数复杂、依赖库未优化、Gas Limit设置不足以及区块链网络拥堵都可能导致这个问题。用户提到的引用[2]直接指出合约大小是一个关键因素,因此我需要首先考虑优化合约体积。 然后,针对每个可能的原因,我需要给出具体的解决步骤。比如优化合约代码,包括减少不必要的函数、使用库来存储重复代码、拆分合约等。另外,构造函数里的复杂逻辑也可能消耗大量Gas,需要简化或拆分初始化步骤。还有,检查是否所有依赖库都正确导入并且优化过,避免重复代码。 关于Gas Limit设置,用户可能在Remix中手动调整Gas Limit,但需要注意区块的Gas上限。引用[2]提到强行部署也没用,所以可能需要更根本的优化。此外,网络拥堵时Gas价格高涨,选择低峰时段部署可能有效。 最后,生成相关问题,帮助用户进一步了解Gas优化和部署问题。需要确保所有建议都符合引用内容,并且逻辑清晰,步骤明确。同时,保持回答结构清晰,分点说明,便于用户理解和操作。</think>### Remix部署Gas估算失败的解决方案 在Remix部署智能合约时出现"Gas估算失败"错误,通常由合约复杂性、构造函数参数或区块链网络限制导致。以下是分步解决方案: 1. **优化合约体积** - 使用`SPDX-License-Identifier`代替完整License声明 - 删除未使用的import语句和库依赖 - 将重复逻辑抽象为独立库(如使用`library`关键字) - 示例优化: ```solidity // 优化前 import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; // 优化后 import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; // 仅保留必要依赖 ``` 2. **简化构造函数** - 避免在构造函数中执行复杂计算 - 将初始化操作拆分为独立函数 - 示例改造: ```solidity // 改造前 constructor(address[] memory _admins) { for(uint i=0; i<_admins.length; i++){ admins[_admins[i]] = true; } } // 改造后 constructor() {} // 空构造函数 function initialize(address[] calldata _admins) external onlyOwner { // 初始化逻辑 } ``` 3. **Gas Limit调整** - 在MetaMask中手动设置Gas Limit(建议值:3,000,000) - 在Remix的"Deploy & Run Transactions"模块设置: $$ Gas\ Limit = 3 \times 10^6 $$ - 注意当前区块链的区块Gas上限(可通过Etherscan查看) 4. **分拆合约** - 当合约超过24KB大小限制时,采用代理模式: ```solidity // 主合约 contract Main { address logicContract; function upgradeLogic(address _new) external { logicContract = _new; } } // 逻辑合约 contract Logic { function execute() external { // 业务逻辑 } } ``` 5. **网络选择策略** - 优先在本地开发链(如Ganache)测试部署 - 正式部署时选择低Gas时段(可通过Gas跟踪网站监控) [^2]: 部署时合约体积过大会导致Gas需求超过区块上限,需要优化代码结构或采用分拆方案
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄嘉成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值