我正在尝试将我的合约部署到 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
通过运行该命令,它会抛出错误。我仔细检查了它是否正在获取环境变量和所有内容。实际上,我从我的一个存储库中分叉了所有这些代码,当时部署脚本可以在该存储库中工作,但现在不是了
我在使用hardhat为goerli和主网部署时遇到了一些类似的问题。对我来说解决这个问题的方法是手动添加 Gas 限制和 Gas 价格,这是你的 goerli 配置对象应该是什么样子的,只是要注意你需要调整限制和价格以考虑当前的 Gas 价格。
goerli: {
url: GOERLI_RPC_URL,
accounts: [PRIVATE_KEY!],
chainId: 5,
allowUnlimitedContractSize: true,
gas: 5000000, //units of gas you are willing to pay, aka gas limit
gasPrice: 50000000000, //gas is typically in units of gwei, but you must enter it as wei here
}
阅读安全帽配置文档也值得一看这里
检查 ETH Gas 追踪器以查看最新的 Gas 价格预估
在安全帽配置文件中 将此属性与您的 api 密钥一起提供。
etherscan: {
apiKey: "",
},
您可以通过登录etherscan(如果是主网)或basescan(如果是basenet)来获取API密钥。