Polygon测试网NFT铸造报错:ERC721无效Token ID问题求助
问题:Polygon测试网NFT mint交易成功,但未收到NFT,调用ownerOf返回无效token ID错误
在Polygon测试网开发NFT合约,通过Remix IDE的「Injected Provider - Metamask」部署。用户支付MATIC后交易显示成功,但MetaMask账户未收到NFT,调用ownerOf函数时返回错误:
call to ERC721.ownerOf errored: Returned error: {"jsonrpc":"2.0","error":"execution reverted: ERC721: invalid token ID"
尝试了两个合约均出现相同问题,代码如下:
合约1:Example_credential1
pragma solidity ^0.8.9; //define version import "@openzeppelin/contracts@4.8.3/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts@4.8.3/token/ERC721/extensions/ERC721Enumerable.sol"; import "@openzeppelin/contracts@4.8.3/security/Pausable.sol"; import "@openzeppelin/contracts@4.8.3/access/Ownable.sol"; import "@openzeppelin/contracts@4.8.3/utils/Counters.sol"; contract Example_credential1 is ERC721, ERC721Enumerable, Pausable, Ownable { event Log(string message); // Propoerty variables using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; //uint256 private _tokenIdCounter; uint256 public MINT_PRICE = 0.005 ether; uint256 public MAX_SUPPLY = 10000; //we define the max amount of NFTs that can be created // Lifecycle Methods constructor() ERC721("Example_credential1", "EC1") { _tokenIdCounter.increment(); } //Function withdraw function withdraw() public onlyOwner() { require(address(this).balance > 0, "Balance is zero"); payable(owner()).transfer(address(this).balance); } // Pausable Functions function pause() public onlyOwner { _pause(); } function unpause() public onlyOwner { _unpause(); } //Minting functions function safeMint(address to) public payable { require(msg.value >= MINT_PRICE, "not enough ether sent"); require(totalSupply() <= MAX_SUPPLY, "No more tokens can be minted"); uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to,tokenId); } // Other functions function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) internal whenNotPaused override(ERC721, ERC721Enumerable) { super._beforeTokenTransfer(from, to, tokenId, batchSize); } // The following functions are overrides required by Solidity. function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721Enumerable) returns (bool) { return super.supportsInterface(interfaceId); } }
合约2:Example_credential2
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "@openzeppelin/contracts@4.8.3/token/ERC721/ERC721.sol"; contract Example_credential2 is ERC721 { uint256 public MINT_PRICE = 0.005 ether; uint256 public tokenCounter; constructor() public ERC721("Example_credential2", "EC2") { tokenCounter = 0; } function createCollectible(string memory tokenURI) public returns (uint256){ uint256 newItemId = tokenCounter; _safeMint(msg.sender, newItemId); tokenCounter = tokenCounter + 1; return newItemId; } }
原因分析与解决方案
核心问题
两个合约的mint逻辑存在关键缺陷:
- 合约1:虽有金额校验,但你可能未正确触发
safeMint函数(比如只转了MATIC但没调用mint方法),且totalSupply判断逻辑有误 - 合约2:
createCollectible完全未处理MATIC支付校验,导致该函数可被无成本调用,而你转的MATIC只是单纯转账到合约,未触发mint
以上情况都会导致没有实际生成NFT,自然调用ownerOf会提示无效token ID。
针对合约1的修复
- 修正
safeMint的供应量判断:totalSupply()返回已 mint 数量,<= MAX_SUPPLY会允许超量 mint,改为< MAX_SUPPLY - 确保调用
safeMint时,在Remix的「Value」输入框填写至少0.005 ether(对应5000000000000000 wei),并传入你的MetaMask地址作为to参数
修改后的safeMint函数:
function safeMint(address to) public payable { require(msg.value >= MINT_PRICE, "not enough ether sent"); require(totalSupply() < MAX_SUPPLY, "No more tokens can be minted"); uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); }
针对合约2的修复
给createCollectible添加支付校验,确保只有支付足够MATIC才能mint:
function createCollectible(string memory tokenURI) public payable returns (uint256){ require(msg.value >= MINT_PRICE, "Insufficient MATIC sent"); uint256 newItemId = tokenCounter; _safeMint(msg.sender, newItemId); tokenCounter += 1; return newItemId; }
调用该函数时,必须在Remix的「Value」栏填入0.005 ether以上的金额。
额外检查项
- 查交易详情:在Polygon测试网扫描器上查看你的交易,确认「Input Data」对应合约的mint函数调用,若只是单纯转MATIC到合约,不会触发mint
- 手动添加NFT:即使mint成功,MetaMask可能需要手动添加NFT(输入合约地址和对应的token ID)才能显示
- 确认token ID:调用合约的
tokenCounter(合约2)或给合约1添加getCurrentTokenId()函数读取_tokenIdCounter.current(),确保调用ownerOf时传入的是已存在的ID
内容的提问来源于stack exchange,提问作者Frenom
相关产品推荐
相关产品推荐

