ERC721:无效的代币 ID — 对于新部署的合约上的第一个代币

问题描述 投票:0回答:1

当我仍在使用一些令牌增量逻辑时,我开始收到此错误,所以我认为这是一个逻辑问题。

ProviderError: Error: VM Exception while processing transaction: reverted with reason string 'ERC721: invalid token ID' 
但经过一系列实验后,即使我在新启动的本地节点上为新部署的合约手动将令牌设置为 1,我仍然收到此错误。

我用

_safeMint(msg.sender, 1);

这是我的完整合同:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/common/ERC2981.sol";


import "./Estonians888Token.sol";
import "./IEstonians888DIDRegistry.sol";

/**
 * @title LoveDoPostNFT
 * @dev Contract for creating NFT posts, supporting superlikes, and tracking given and received superlikes per user with DID.
 * Each post is represented as an NFT and linked to superlike functionality.
 */
contract LoveDoPostNFT is ERC721Enumerable, Ownable, IERC721Receiver, ERC721URIStorage, ERC2981 {
    using SafeERC20 for IERC20;
    using Address for address;

    Estonians888Token public immutable token; // Token used for superlikes
    //IEstonians888DIDRegistry public profileContract;

    uint256 public constant SUPERLIKE_LIMIT = 8; // Monthly superlike limit per user
    uint256 public constant RECOMMENDATION_LIMIT = 8; // Maximum recommendations with superlikes per user

    // NOTE: user=address

    struct Post {
        address author;          // Address of the post author
        address valueProvider;   // Address of the value provider for this post
        uint256 timestamp;       // Timestamp when the post was created
        string contentURI;       // URI pointing to JSON with post content, media and tags
        bool isActive;           // The active status of the post
    }

    // List of addresses that gave superlikes
    mapping(uint256 => address[]) public postSuperlikes;

    mapping(uint256 => Post) public posts;                    // Posts by unique ID (also the NFT tokenId)
    mapping(address => string) public addressToDID;           // Mapping for address to profile DID association
    mapping(string => address) public didToAddress;           // Mapping for DID to address association
    mapping(address => uint256[]) public valueProviderToPosts;   // Mapping valueProvider to all (+other) LoveDo posts about it
    mapping(address => uint256) public userSuperlikeCount;     // Monthly counter for superlikes given by each user
    mapping(address => uint256) public lastSuperlikeReset;     // Timestamp of the last reset of the user's superlike counter
    mapping(address => uint256) public receivedSuperlikes;     // Total superlikes received by each user
    mapping(address => uint256) public givenSuperlikes;        // Total superlikes given by each user
    mapping(address => uint256) public recommendationsCount;    // Tracks the number of superliked recommendations (LoveDoPostNFT with at least one superlike) for each user
    mapping(address => uint256) public pendingWithdrawals;     // Tracks tokens each user can withdraw by DID

    uint256 private tokenIdCounter;

    event PostCreated(uint256 indexed postId, address indexed author, address indexed valueProvider, uint256 timestamp, string contentURI);
    event SuperlikeGiven(uint256 indexed postId, address indexed superlikeGiver, address indexed valueProvider, uint256 timestamp);
    event WithdrawalRequested(address indexed user, uint256 amount);
    event Debug(string message, uint256 value);

    struct Transaction {          // Structure to store transaction data
        uint256 timestamp;
        uint256 amount;
        address user;
    }

    /**
     * @dev Initializes the ERC721 with a name and symbol, and sets the token for superlikes.
     * @param _token Address of the Estonians888Token contract.
     */
    constructor(Estonians888Token _token) ERC721("LoveDoPostNFT", "LDP") {
        require(address(_token).code.length > 0, "Token address must be a contract.");
        token = _token;
        //profileContract = IEstonians888DIDRegistry(_profileContract);
        _setDefaultRoyalty(msg.sender, 888); // Sets default royalty to 8.88%
    }

    /**
     * @notice Sets the address associated with a given DID.
     * @dev This function links a DID to an Ethereum address.
     * @param did The DID of the user.
     * @param userAddress The Ethereum address to associate with the DID.
     */
    function setDIDAddress(string calldata did, address userAddress) private {
        require(userAddress != address(0), "Invalid address");
        // Adding the ability to link DID to an address
        didToAddress[did] = userAddress;
    }

    /**
     * @notice Creates a new post and mints a new NFT representing the post.
     * @param valueProvider Address of the value provider for this post
     * @param contentURI URI pointing to JSON with post content, media and tags
     */
    function createPost(
        address valueProvider,
        string calldata contentURI
    ) external {
        require(valueProvider != address(0), "Value provider address cannot be zero");
        
        tokenIdCounter++;
        uint256 postId = tokenIdCounter;

        require(postId >= 0, "Invalid postId generated");

        posts[postId] = Post({
            author: msg.sender,
            valueProvider: valueProvider,
            timestamp: block.timestamp,
            contentURI: contentURI,
            isActive: true
        });

        require(!_exists(1), "Token ID already exists");
        _safeMint(msg.sender, 1);
        _setTokenURI(1, contentURI);
        require(_exists(1), "Token was not minted");

        emit PostCreated(
            postId, 
            msg.sender, 
            valueProvider, 
            block.timestamp, 
            contentURI
        );
    }

    /**
     * @notice Gives a superlike to a post by address.
     * @param postId ID of the post to superlike.
     */
    function giveSuperlike(uint256 postId) external {
        require(_exists(postId), "Post does not exist.");
        Post storage post = posts[postId];
        require(msg.sender != post.author, "Author cannot superlike own post.");

        _resetSuperlikeCount(msg.sender);

        require(userSuperlikeCount[msg.sender] < SUPERLIKE_LIMIT, "Monthly superlike limit reached.");

        // If this is the first superlike for the post, increase recommendation count for the recommended user
        if (postSuperlikes[postId].length == 0) {
            recommendationsCount[post.valueProvider]++;
        }

        // Update post, user data, and superlike mappings
        postSuperlikes[postId].push(msg.sender);

        // Superlike counter which is monthly reset for the sender user
        userSuperlikeCount[msg.sender]++;

        // Total superlikes received by the value provider
        receivedSuperlikes[post.valueProvider]++;

        // Total superlikes given by the sender user
        givenSuperlikes[msg.sender]++;

        // Update pending withdrawal balance for the value provider user
        pendingWithdrawals[post.valueProvider] += 1 ether; // Assumes 1 ether = 1 token with 18 decimals

        emit SuperlikeGiven(postId, msg.sender, post.valueProvider, block.timestamp);
    }

    /**
     * @notice Allows users to withdraw tokens they have accumulated from received superlikes.
     * Users pay gas fees to withdraw tokens. Ensures they can’t withdraw more than their accumulated balance.
     * @param amount The amount of tokens to withdraw (in smallest token units).
     */
    function withdrawTokens(uint256 amount) external {
        require(pendingWithdrawals[msg.sender] >= amount, "Insufficient balance to withdraw.");
    
        // Reduce the pending balance before transferring
        pendingWithdrawals[msg.sender] -= amount;
    
        // Transfer tokens from the pool in the token contract to the user
        token.transferFromPool(msg.sender, amount);
    
        emit WithdrawalRequested(msg.sender, amount);
    }


    /**
     * @dev Resets the monthly superlike counter for a user if a new month has started.
     * @param userAddress Address of the user.
     */
    function _resetSuperlikeCount(address userAddress) internal {
        uint256 oneMonth = 30 days;
        if (block.timestamp - lastSuperlikeReset[userAddress] >= oneMonth) {
            userSuperlikeCount[userAddress] = 0;
            lastSuperlikeReset[userAddress] = block.timestamp;
        }
    }

    /**
     * @notice Retrieves the list of DIDs that gave superlikes to a post.
     * @param postId ID of the post to retrieve superlikes for.
     * @return Addresses that gave superlikes.
     */
    function getSuperlikes(uint256 postId) external view returns (address[] memory) {
        require(_exists(postId), "Post does not exist.");
        return postSuperlikes[postId];
    }

    /**
     * @notice Returns the total superlikes received by a specific DID.
     * This can be used as a social mining metric for user rating.
     * @param userAddress Address of the user.
     * @return Total superlikes received by the user.
     */
    function getReceivedSuperlikes(address userAddress) external view returns (uint256) {
        return receivedSuperlikes[userAddress];
    }

    /**
     * @notice Returns the total superlikes given by a specific DID.
     * @param userAddress Address of the user.
     * @return Total superlikes given by the user.
     */
    function getGivenSuperlikes(address userAddress) external view returns (uint256) {
        return givenSuperlikes[userAddress];
    }

    /**
     * @notice Returns the amount of tokens the user can withdraw.
     * @param userAddress Address of the user.
     * @return Amount of tokens available for withdrawal.
     */
    function getPendingWithdrawals(address userAddress) external view returns (uint256) {
        return pendingWithdrawals[userAddress];
    }

    /**
    * @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));
    }

    function supportsInterface(bytes4 interfaceId) 
        public view override(ERC721Enumerable, ERC721URIStorage, ERC2981) 
        returns (bool) 
    {
        return super.supportsInterface(interfaceId);
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 firstTokenId,
        uint256 batchSize
    ) internal virtual override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
    }

    function _burn(uint256 tokenId) internal virtual override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId) public view virtual override(ERC721, ERC721URIStorage) returns (string memory) {
        return super.tokenURI(tokenId);
    }

    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external pure override returns (bytes4) {
        return this.onERC721Received.selector;
    }

}

我尝试最大限度地减少 tokenID 生成的错误可能性,在新部署的合约上手动将其设置为 1。

ethereum solidity ether
1个回答
0
投票

我认为问题是由于继承了 ERC721URIStorage 和 ERC721Enumerable 造成的。

您可以在这里查看如何将它们组合在一起:https://forum.openzeppelin.com/t/how-do-inherit-from-erc721-erc721enumerable-and-erc721uristorage-in-v4-of-openzeppelin-合约/6656

© www.soinside.com 2019 - 2024. All rights reserved.