ERC721无效Token ID问题:手动指定ID仍触发合约报错
问题分析与解决方案
根源问题
1. 错误的_exists函数实现
你自定义的_exists函数通过调用ownerOf(tokenId)判断代币是否存在,但OpenZeppelin的ownerOf方法在代币不存在时会直接抛出ERC721: invalid token ID异常,而非返回地址0。这导致你在第一次铸造前调用require(!_exists(1), ...)时就触发了异常。
2. 硬编码Token ID
在createPost函数中,你硬编码使用Token ID为1进行铸造,这会导致第二次调用时尝试重复铸造已存在的代币ID,引发错误。正确的做法应该使用递增的postId作为Token ID。
修复步骤
步骤1:移除错误的_exists重写函数
删除以下代码块:
/** * @dev Returns whether `tokenId` exists. * Tokens exist if they have an owner. */ function _exists(uint256 tokenId) internal view override returns (bool) { return (ownerOf(tokenId) != address(0)); }
父合约ERC721Enumerable已经提供了正确的_exists实现,它会直接检查代币所有者映射而不会抛出异常。
步骤2:使用递增的Token ID进行铸造
修改createPost函数中的铸造逻辑,将硬编码的1替换为postId:
// 替换之前的硬编码逻辑 // require(!_exists(1), "Token ID already exists"); // _safeMint(msg.sender, 1); // _setTokenURI(1, contentURI); // require(_exists(1), "Token was not minted"); // 使用递增的postId require(!_exists(postId), "Token ID already exists"); _safeMint(msg.sender, postId); _setTokenURI(postId, contentURI); require(_exists(postId), "Token was not minted");
验证修复
- 重新部署合约到本地节点。
- 调用
createPost函数,此时会使用递增的Token ID(从1开始)进行铸造,不会触发异常。 - 多次调用
createPost,每次都会生成新的Token ID,避免重复铸造问题。
内容的提问来源于stack exchange,提问作者Zeya888
相关产品推荐
相关产品推荐

