我正在尝试将我的合同部署到goerli测试网,但hardhat不断抛出这个错误:
Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="execution reverted", method="estimateGas", transaction={"from":"0xb5E8683Aa524069C5714Fc2D8c3e64F78f2862fb","data":"0x6103e86009556... (I shortened it down)","accessList":null}, error={"name":"ProviderError","_stack":"ProviderError: HttpProviderError\n at HttpProvider.request (C:\\Programming\\Personal Projects\\NFT Minting Dapp\\smart-contract\\node_modules\\hardhat\\src\\internal\\core\\providers\\http.ts:78:19)\n at LocalAccountsProvider.request (C:\\Programming\\Personal Projects\\NFT Minting Dapp\\smart-contract\\node_modules\\hardhat\\src\\internal\\core\\providers\\accounts.ts:187:34)\n at processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async EthersProviderWrapper.send (C:\\Programming\\Personal Projects\\NFT Minting Dapp\\smart-contract\\node_modules\\@nomiclabs\\hardhat-ethers\\src\\internal\\ethers-provider-wrapper.ts:13:20)","code":3,"_isProviderError":true,"data":"0xbfc6c337000000000000000000000000fd7bfa171b5b81b79c245456e986db2f32fbfadb"}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.7.2) at Logger.makeError (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\@ethersproject\logger\src.ts\index.ts:269:28) at Logger.throwError (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\@ethersproject\logger\src.ts\index.ts:281:20) at checkError (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\@ethersproject\providers\src.ts\json-rpc-provider.ts:78:20) at EthersProviderWrapper.<anonymous> (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\@ethersproject\providers\src.ts\json-rpc-provider.ts:642:20) at step (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:48:23) at Object.throw (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:29:53) at rejected (C:\Programming\Personal Projects\NFT Minting Dapp\smart-contract\node_modules\@ethersproject\providers\lib\json-rpc-provider.js:21:65) at processTicksAndRejections (node:internal/process/task_queues:95:5) { reason: 'execution reverted', code: 'UNPREDICTABLE_GAS_LIMIT', method: 'estimateGas', transaction: { from: '0xb5E8683Aa524069C5714Fc2D8c3e64F78f2862fb', data: '0x6103e86009556... (I shortened it down)', accessList: null }
这是我的合同:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; /// @title John's ERC721A Contract /// @author John Pioc (www.johnpioc.com) /// @notice This contract can be used to mint ERC721A standard NFTs with industry standard functionality - whitelisted addresses, reveals, NFT metadata, etc. import "erc721a/contracts/ERC721A.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import {DefaultOperatorFilterer} from "./DefaultOperatorFilterer.sol"; error CallerIsNotUser(); error InvalidWhitelistAllocation(); error MintingTooMany(); error InsufficientFunds(); error MintingOverCollectionSize(); error SaleIsClosed(); error MintingOverWhitelistAllocation(); error InvalidProof(); error URIQueryForNonexistentToken(); contract NFT is ERC721A, Ownable, DefaultOperatorFilterer { using Strings for uint256; enum SaleState { CLOSED, WHITELIST, PUBLIC } uint256 public collectionSize = 1000; uint256 public publicMintPrice = 0.1 ether; uint256 public publicMaxMintAmount = 3; uint256 public whitelistAllocation = 500; uint256 public whitelistMintPrice = 0.05 ether; uint256 public whitelistMaxMintAmount = 1; bytes32 public whitelistMerkleRoot; string public unrevealedUri = "https://exampleUnrevealedUri.com"; string public baseUri = "https://exampleUri.com/"; bool public isRevealed; SaleState public saleState; /// @notice Modifier to verify that caller doesn't come from a contract modifier callerIsUser() { if (tx.origin != msg.sender) revert CallerIsNotUser(); _; } constructor() ERC721A ("NFT", "NFT") { isRevealed = false; saleState = SaleState.CLOSED; } /// @notice Function to mint NFTs during the public sale /// @param _mintAmount Number of NFTs to mint function publicMint(uint64 _mintAmount) public payable callerIsUser { if (_numberMinted(msg.sender) - _getAux(msg.sender) + _mintAmount > publicMaxMintAmount) revert MintingTooMany(); if (totalSupply() + _mintAmount > collectionSize) revert MintingOverCollectionSize(); if (saleState != SaleState.PUBLIC) revert SaleIsClosed(); if (msg.value < _mintAmount * publicMintPrice) revert InsufficientFunds(); _safeMint(msg.sender, _mintAmount); } /// @notice Function to mint NFTs during the whitelist sale /// @param _merkleProof Merkle Proof for caller's address /// @param _mintAmount Number of NFTs to mint function whitelistMint(bytes32[] calldata _merkleProof, uint64 _mintAmount) public payable callerIsUser { if (_getAux(msg.sender) + _mintAmount > whitelistMaxMintAmount) revert MintingTooMany(); if (totalSupply() + _mintAmount > whitelistAllocation) revert MintingOverWhitelistAllocation(); if (saleState != SaleState.WHITELIST) revert SaleIsClosed(); if (msg.value < _mintAmount * whitelistMintPrice) revert InsufficientFunds(); bytes32 leaf = keccak256(abi.encodePacked(msg.sender)); if(!(MerkleProof.verify(_merkleProof, whitelistMerkleRoot, leaf))) revert InvalidProof(); _setAux(msg.sender, _getAux(msg.sender) + _mintAmount); _safeMint(msg.sender, _mintAmount); } /// @notice Sets a new collection size /// @dev Only owner can call this function /// @param _collectionSize New Collection Size function setCollectionSize(uint256 _collectionSize) public onlyOwner { collectionSize = _collectionSize; } /// @notice Sets a new public mint price /// @dev Only owner can call this function /// @param _publicMintPrice New public mint price function setPublicMintPrice(uint256 _publicMintPrice) public onlyOwner { publicMintPrice = _publicMintPrice; } /// @notice Sets a new public max mint amount /// @dev Only owner can call this function /// @param _publicMaxMintAmount New public max mint amount function setPublicMaxMintAmount(uint256 _publicMaxMintAmount) public onlyOwner { publicMaxMintAmount = _publicMaxMintAmount; } /// @notice Sets a new whitelist allocation /// @dev Only owner can call this function. New whitelist allocation cannot be greater than collection size /// @param _whitelistAllocation New whitelist allocation function setWhitelistAllocation(uint256 _whitelistAllocation) public onlyOwner { if (_whitelistAllocation > collectionSize) revert InvalidWhitelistAllocation(); whitelistAllocation = _whitelistAllocation; } /// @notice Sets a new whitelist mint price /// @dev Only owner can call this function /// @param _whitelistMintPrice New whitelist mint price function setWhitelistMintPrice(uint256 _whitelistMintPrice) public onlyOwner { whitelistMintPrice = _whitelistMintPrice; } /// @notice Sets a new whitelist max mint amount /// @dev Only owner can call this function /// @param _whitelistMaxMintAmount New whitelist max mint amount function setWhitelistMaxMintAmount(uint256 _whitelistMaxMintAmount) public onlyOwner { whitelistMaxMintAmount = _whitelistMaxMintAmount; } /// @notice Sets a new whitelist merkle root /// @dev Only owner can call this function /// @param _whitelistMerkleRoot New whitelist merkle root function setWhitelistMerkleRoot(bytes32 _whitelistMerkleRoot) public onlyOwner { whitelistMerkleRoot = _whitelistMerkleRoot; } /// @notice Sets a new unrevealed URI /// @dev Only owner can call this function /// @param _unrevealedUri New unrevealed URI function setUnrevealedUri(string memory _unrevealedUri) public onlyOwner { unrevealedUri = _unrevealedUri; } /// @notice Sets a new base URI /// @dev Only owner can call this function /// @param _baseUri New base URI function setBaseUri(string memory _baseUri) public onlyOwner { baseUri = _baseUri; } /// @notice Toggles reveal from false to true, vice versa /// @dev Only owner can call this function. Starts at false function toggleRevealed() public onlyOwner { isRevealed = !isRevealed; } /// @notice Sets a new sale state /// @dev Only owner can call this function. 0 = CLOSED, 1 = WHITELIST, 2 = PUBLIC /// @param _saleState new sale state function setSaleState(uint256 _saleState) public onlyOwner { saleState = SaleState(_saleState); } /// @notice Generates and returns the token URI for a given token ID /// @param _tokenId An NFT's token ID function tokenURI(uint256 _tokenId) public view override returns (string memory) { if (!_exists(_tokenId)) revert URIQueryForNonexistentToken(); if (!isRevealed) return unrevealedUri; return string(abi.encodePacked(baseUri, _tokenId.toString(), ".json")); } /// @notice Withdraws all ETH from contract to owner's address /// @dev Only owner can call this function function withdraw() public payable onlyOwner { (bool os,) = payable(owner()).call{value: address(this).balance}(""); require(os); } /// @notice All functions below are mandatory add-ons as per the Default Operator Filterer protocol function transferFrom(address from, address to, uint256 tokenId) public payable override onlyAllowedOperator(from) { super.transferFrom(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId) public payable override onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public payable override onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId, data); } }
这是我的配置文件:
import { HardhatUserConfig } from "hardhat/config"; import "@nomicfoundation/hardhat-toolbox"; import "@nomiclabs/hardhat-waffle"; import "dotenv/config"; import "@typechain/hardhat"; const GOERLI_RPC_URL = process.env.GOERLI_RPC_URL; const PRIVATE_KEY = process.env.PRIVATE_KEY; const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; const config: HardhatUserConfig = { solidity: { version: "0.8.17", settings: { optimizer: { enabled: true, runs: 200 } } }, networks: { goerli: { url: GOERLI_RPC_URL, accounts: [PRIVATE_KEY!], chainId: 5, allowUnlimitedContractSize: true, } }, etherscan: { apiKey: { goerli: ETHERSCAN_API_KEY! } } }; export default config;
这是我的部署脚本:
// imports import { ethers, run, network } from 'hardhat'; // async main async function main() { const contractFactory = await ethers.getContractFactory("NFT"); const contract = await contractFactory.deploy(); console.log(`Contract deployed to ${contract.address}`); if (network.config.chainId === 5 && process.env.ETHERSCAN_API_KEY) { console.log("Waiting for block confirmations...") await contract.deployTransaction.wait(6) await verify(contract.address, []) } } // async function verify(contractAddress, args) { const verify = async (contractAddress: string, args: any[]) => { console.log("Verifying contract...") try { await run("verify:verify", { address: contractAddress, constructorArguments: args, }) } catch (e: any) { if (e.message.toLowerCase().includes("already verified")) { console.log("Already Verified!") } else { console.log(e) } } } // main main() .then(() => process.exit(0)) .catch((error) => { console.error(error) process.exit(1) })
我运行命令:
npx hardhat run scripts/deploy.ts --network goerli
通过运行该命令,它会抛出错误。我再次检查了它是否抓取了环境变量和所有内容。实际上,我从我的一个repo中分叉了所有这些代码,当时部署脚本在那里工作,但现在不是了