The ERC721 Metadata standard does not enforce uniqueness of the returned tokenURI.
It is allowable for two 721 tokens to return the same metadata. Whether this is appropriate is up to you to decide as you implement the 721 contract. Most likely in the code you run during the minting process you will assign the metadata and decide whether or not you want to enforce it to be unique.
You did not set the TokenURI for NFT, so when NFT gets the URI, BaseURI + TokenID is used.Look at the source code.
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
It returns the URI: https://gateway.pinata.cloud/ipfs/0
This, of course, is not ideal, the ideal URI should be: https://gateway.pinata.cloud/ipfs/Qmf43nycxRXdwdq3GqT7SKU3MvRyBvbpjzufzZR8ASkMGb
I think you see the reason for the problem here.
The next thing you need to do is call _setTokenUri to store the CID from IPFS so that you can get the correct IPFS link when reading the NFT.
/**
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
This function is an internal function, and you should provide an external function to use it, but security must be taken care of, or anyone can modify it!
Best Answer
So
tokenURI()
is the function that will return the address where the metadata of this specific token is stored.The metadata is a .json file (following the ERC721Metadata standard) where all the data associated to the NFT is stored. Such as its name, the address where the image is stored...
https://eips.ethereum.org/EIPS/eip-721
Here is a detailed explanation of ERC721 metadata schemas: https://nftschool.dev/reference/metadata-schemas/#ethereum-and-evm-compatible-chains
If you want your NFT to have a metadata and an image linked to your NFT it is much better (and the standard way used by applications) to store them offChain, since doing it onChain would cost a lot.
Your options to store your metadata as of now are either on a server (centralized) or on IPFS which is a decentralized storage. IPFS stands for Interplanetary File System.
https://en.wikipedia.org/wiki/InterPlanetary_File_System
https://github.com/ipfs/ipfs
The third party services are here to make your life easier. Originally you would have to run an IPFS node yourself to store and access the data you want. Third parties allow you to store on already existing nodes.
PS: All those answers are easily accessible on stackoverflow or the internet. It's always better to search by yourself first ;)