基于@imtbl/zkevm-contracts部署NFT至IMX zkEVM遇执行回滚错误
部署IMX zkEVM NFT合约时执行回滚错误排查
错误信息
error: ProviderError: unable to apply transaction even for the highest gas limit 30000000: execution reverted at HttpProvider.request (C:\src\gumi\contracts\node_modules\hardhat\src\internal\core\providers\http.ts:88:21) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async EthersProviderWrapper.send (C:\src\gumi\contracts\node_modules\@nomiclabs\hardhat-ethers\src\internal\ethers-provider-wrapper.ts:13:20)
部署脚本
/* eslint-disable prettier/prettier */ /* eslint-disable dot-notation */ /* eslint-disable spaced-comment */ /* eslint-disable no-unused-vars */ /* eslint-disable camelcase */ /* eslint-disable node/no-missing-import */ /* eslint-disable no-process-exit */ import * as dotenv from "dotenv"; import { ethers } from "hardhat"; import { craft__factory } from "../typechain"; import { insertRecord } from "../build_artifact"; import fs from 'fs' dotenv.config(); // get baseURI from .env const BASE_URI = process.env.BASE_URI!; async function main() { const signers = await ethers.getSigners(); const address = signers[0]; const contractURI = "ipfs"; const operatorAllowlist = address.address; console.log(address.address, BASE_URI, contractURI, operatorAllowlist); const factory = new craft__factory(address); const contract = await factory.deploy(address.address, BASE_URI, contractURI, operatorAllowlist); const nft = await contract.deployed(); const data = fs.readFileSync("artifacts/src/Gumicraft.sol/Gumicraft.json", 'utf8'); const json = JSON.parse(data); const name = "Gumicraft"; console.log(`${name} deployed to: ${nft.address}\nwith artifact ${json}`); await insertRecord([name, nft.address, json]); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
NFT合约代码
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.17; import "../base/NFT.sol"; contract craft is NFT { // a constructor that takes a baseURI, an imxContract and an array of defaultOperators constructor( address owner_, string memory baseURI_, string memory contractURI_, address operatorAllowlist_ ) NFT( owner_, "craft", "GCRAFT", baseURI_, contractURI_, operatorAllowlist_, owner_, 1000 ) { } }
基础NFT合约代码
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.17; import {Counters} from "@openzeppelin/contracts/utils/Counters.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {ImmutableERC721} from "@imtbl/zkevm-contracts/contracts/token/erc721/preset/ImmutableERC721.sol"; abstract contract NFT is ImmutableERC721, ReentrancyGuard { event NftMinted(address to, uint256 id, string url); struct Nft { uint256 id; string url; } using Counters for Counters.Counter; Counters.Counter private _tokenIds; mapping(uint256 => string) private tokenParameters; constructor( address owner_, string memory name_, string memory symbol_, string memory baseURI_, string memory contractURI_, address operatorAllowlist_, address receiver_, uint96 feeNumerator_ ) ImmutableERC721( owner_, name_, symbol_, baseURI_, contractURI_, operatorAllowlist_, receiver_, feeNumerator_ ) { } function setTokenParams(uint256 tokenId, string memory tokenParams) public virtual onlyRole(MINTER_ROLE) { // validate that string is not empty require( bytes(tokenParams).length > 0, "NFT: token params can not be empty" ); tokenParameters[tokenId] = tokenParams; } function getTokenFullURL(uint256 tokenId) public view virtual returns (string memory) { return string( abi.encodePacked( baseURI, contractURI, tokenId, tokenParameters[tokenId] ) ); } function createToken(address to, string memory tokenParams) public payable virtual nonReentrant onlyRole(MINTER_ROLE) returns (uint256) { _tokenIds.increment(); uint256 newTokenId = _tokenIds.current(); super._safeMint(to, newTokenId); setTokenParams(newTokenId, tokenParams); emit NftMinted(to, newTokenId, getTokenFullURL(newTokenId)); return newTokenId; } function getTokensFor(address ownerId) public view returns (Nft[] memory) { uint256 balance = balanceOf(ownerId); require(balance > 0, "NFT: no tokens for this owner"); uint256 total = _tokenIds.current(); Nft[] memory tokens = new Nft[](balance); uint256 start = 1; for (uint256 i = 1; i < total; i++) { address _owner = ownerOf(i); if (_owner != ownerId) { continue; } else { tokens[start] = Nft(i, getTokenFullURL(i)); start++; if (start == balance) { break; } } } return tokens; } }
排查背景
已重构基类、移除构造函数内逻辑调用,但仍收到cannot estimate gas; transaction may fail or may require manual gas limit错误,怀疑合约实例化存在问题。
可能的排查方向及修复方案
- 构造函数参数有效性检查:
ImmutableERC721的构造函数可能对参数有隐性约束,比如feeNumerator_通常代表手续费万分比,需确认是否不超过10000(部分实现的上限)。另外,operatorAllowlist_传入部署者地址,需确认该地址是否符合合约对白名单地址的要求,是否需要提前满足特定权限条件。 - 环境变量验证:
部署脚本中BASE_URI来自环境变量,若未正确设置会传入空字符串,可能触发ImmutableERC721内部的参数校验。执行部署前务必打印BASE_URI确认其值不为空,且格式符合IPFS/HTTP URI规范。 - 手动指定Gas参数:
IMX zkEVM的Gas机制与以太坊主网存在差异,自动估算Gas容易失败。修改部署脚本,在deploy方法中手动指定Gas限制和价格:const contract = await factory.deploy( address.address, BASE_URI, contractURI, operatorAllowlist, { gasLimit: 50000000, gasPrice: ethers.utils.parseUnits("20", "gwei") } ); - 依赖版本兼容性排查:
确认@imtbl/zkevm-contracts版本与Solidity 0.8.17兼容,查看该包的官方文档或仓库说明,确保使用的版本支持当前编译器版本。若存在版本冲突,尝试降级或升级依赖包。 - 修复
getTokensFor函数逻辑错误:
该函数存在数组索引越界和遍历遗漏问题,虽不影响部署,但会导致后续调用失败:start初始值为1,导致数组第一个元素tokens[0]未赋值,应改为start = 0- 循环条件
i < total会漏掉最后一个token(当i = total时),应改为i <= total
修复后的代码:
function getTokensFor(address ownerId) public view returns (Nft[] memory) { uint256 balance = balanceOf(ownerId); require(balance > 0, "NFT: no tokens for this owner"); uint256 total = _tokenIds.current(); Nft[] memory tokens = new Nft[](balance); uint256 start = 0; for (uint256 i = 1; i <= total; i++) { address _owner = ownerOf(i); if (_owner == ownerId) { tokens[start] = Nft(i, getTokenFullURL(i)); start++; if (start == balance) { break; } } } return tokens; } - 启用Hardhat调试日志:
在hardhat.config.js中开启详细日志,获取更具体的回滚原因:module.exports = { solidity: "0.8.17", networks: { imxZkEVM: { url: "YOUR_IMX_ZKEVM_RPC_URL", accounts: [process.env.PRIVATE_KEY], loggingEnabled: true } } };
内容的提问来源于stack exchange,提问作者Ronald Moore
相关产品推荐
相关产品推荐

