What data does an NFT actually store?

I’m working on an NFT marketplace using Solidity. I’ve built a custom smart contract on top of OpenZeppelin’s ERC-721. My NFTs have 5 attributes: id, image (IPFS hash), description, collection, and creator address.

I’m confused about where to store these attributes. Right now, I’m saving them in a struct outside the ERC-721 contract. This makes me wonder: what exactly is an NFT? Are the attributes not part of the NFT itself?

Am I doing this right? Is the ERC-721 standard just for basic NFT functions, or should I be storing the data differently?

Here’s a simplified version of my code:

contract NFTMarket is ERC721Full {
  struct TokenData {
    uint256 id;
    string ipfsHash;
    string desc;
    string collection;
    address creator;
  }

  TokenData[] public tokens;

  function mintToken(string memory _hash, string memory _desc, string memory _collection) public {
    uint256 newTokenId = tokens.length;
    tokens.push(TokenData(newTokenId, _hash, _desc, _collection, msg.sender));
    _mint(msg.sender, newTokenId);
  }
}

Any tips on improving this setup? I’m new to Ethereum development, so I appreciate any advice. Thanks!

hey there, fellow nft enthusiast! :blush: your question got me thinking… have you considered using a token URI instead of storing all that data on-chain? it might save you some gas fees in the long run.

i’m curious, what kind of nfts are you planning to create? are they art pieces, collectibles, or something else entirely? it’d be cool to hear more about your project!

also, have you looked into using IPFS for storing your metadata? it could be a neat way to keep things decentralized. what do you think about that approach?

keep us posted on how your marketplace develops! it’s always exciting to see new projects in the nft space. good luck with your coding adventures! :rocket:

Your approach is actually quite common and sensible. The ERC-721 standard primarily deals with ownership and transfer of unique tokens, not their metadata. Storing additional attributes in a separate struct is a valid method.

However, consider using a mapping instead of an array for better gas efficiency:

mapping(uint256 => TokenData) public tokens;

Also, it’s generally recommended to store minimal data on-chain due to high costs. You might want to keep only essential info on-chain (like the IPFS hash) and store the rest off-chain, retrievable via the hash.

Remember to implement access controls for minting and consider adding events to track important actions. Lastly, ensure your contract is upgradeable if you plan future improvements. Keep learning and iterating!