Fatskills
Practice. Master. Repeat.
Study Guide: Blockchain and Web3 Development: NFTs and Gaming - Non-Fungible Tokens, NFTs, Minting, Metadata, Marketplaces
Source: https://www.fatskills.com/cryptocurrency-bitcoin-blockchain-and-more/chapter/blockchain-and-web3-development-blockchain-and-web3-development-nfts-and-gaming-nonfungible-tokens-nfts-minting-metadata-marketplaces

Blockchain and Web3 Development: NFTs and Gaming - Non-Fungible Tokens, NFTs, Minting, Metadata, Marketplaces

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~5 min read

What This Is

Non?Fungible Tokens (NFTs) are unique digital assets stored on a blockchain. Unlike ERC?20 tokens, each NFT has its own identifier and metadata, making it impossible to swap one for another on a one?to?one basis. This uniqueness lets creators issue verifiable ownership of art, collectibles, tickets, or even real?world assets—think of a CryptoPunks portrait that lives forever on Ethereum and can be bought, sold, or displayed in a wallet just like a physical painting.


Key Terms & Code Snippets

  • ERC?721: The original standard for NFTs; defines ownerOf, transferFrom, and tokenURI.
    solidity interface IERC721 { function ownerOf(uint256 tokenId) external view returns (address); function tokenURI(uint256 tokenId) external view returns (string memory); }

  • ERC?1155: A multi?token standard that can hold both fungible and non?fungible items in one contract, saving gas for batch operations.

  • tokenId (uint256): The unique identifier for each NFT; usually generated by an incremental counter or a hash of the metadata.

  • Metadata JSON: A publicly hosted JSON file (often on IPFS) that describes the NFT (name, description, image URL, attributes). Example:
    json { "name": "Pixel Dragon #42", "description": "A rare dragon from the 2024 collection.", "image": "ipfs://QmX.../dragon42.png", "attributes": [{ "trait_type": "Rarity", "value": "Epic" }] }

  • tokenURI(uint256 tokenId): Returns the metadata URI for a given token. In a simple contract you might concatenate a base URI with the tokenId.

  • Minting Function: The public or only?owner method that creates a new NFT and assigns it to a wallet.
    solidity function mint(address to, uint256 tokenId) external onlyOwner { _safeMint(to, tokenId); }

  • _safeMint vs _mint: _safeMint checks that the recipient is either an EOA or a contract that implements IERC721Receiver, preventing tokens from being locked in non?ERC?721?aware contracts.

  • Marketplace Listing (ERC?721): A contract that holds a mapping(uint256 => Listing) where Listing stores seller, price, and status. Buyers call buy(uint256 tokenId) which transfers ETH and then calls safeTransferFrom.

  • Royalty Standard (ERC?2981): Allows creators to earn a percentage on secondary sales without modifying the marketplace code.
    solidity function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver, uint256 royaltyAmount);

  • IPFS CID: Content Identifier (e.g., QmX...) that points to immutable data; used for storing NFT images and metadata off?chain while keeping the reference on?chain.

  • require(msg.sender == ownerOf(tokenId)): A simple access control pattern that ensures only the token owner can perform certain actions (e.g., updating metadata).

  • Gas?Optimized Counter: Using OpenZeppelin’s Counters library or a custom uint256 private _nextId; to avoid storage writes on every mint.


Step?by?Step / Process Flow

  1. Write the contract – Scaffold an ERC?721 contract in Remix or VS?Code using OpenZeppelin’s ERC721Enumerable and ERC2981.
  2. Compile & test – Run npx hardhat compile and write a few Mocha/Chai tests (hardhat test) that mint, query tokenURI, and transfer.
  3. Deploy – Deploy to a testnet (Goerli) with npx hardhat run scripts/deploy.js --network goerli using an Infura/Alchemy RPC and a funded wallet.
  4. Pin metadata – Upload each JSON + image to IPFS via Pinata or npx ipfs add -r ./metadata; record the resulting CID.
  5. Mint via script – Call the mint function from a Node script using Ethers.js:
    js const tx = await nft.mint(wallet.address, nextId); await tx.wait();
  6. List on a marketplace – Approve the marketplace contract (setApprovalForAll) and call its createListing(tokenId, price); buyers can now purchase with buy(tokenId).

Common Mistakes

  • Mistake: Using tx.origin for owner checks.
    Correction: Always use msg.sender (or ownerOf(tokenId)) because tx.origin can be spoofed through a malicious contract, breaking the access control.

  • Mistake: Storing the full image on?chain (e.g., base64?encoded PNG).
    Correction: Keep large assets off?chain (IPFS or Arweave) and store only the CID in tokenURI; this saves gas and keeps the contract size under the limit.

  • Mistake: Forgetting to implement IERC721Receiver when sending NFTs to a contract.
    Correction: Use _safeMint/safeTransferFrom or add onERC721Received to the receiving contract; otherwise the token can become irretrievable.

  • Mistake: Hard?coding the base URI and later needing to change it (e.g., after a migration).
    Correction: Make the base URI mutable via an onlyOwner setter, or store the full URI per token to avoid future upgrades.

  • Mistake: Ignoring royalty enforcement, assuming every marketplace will honor ERC?2981.
    Correction: Include royalty logic in your own marketplace or verify that the target marketplace supports ERC?2981; otherwise creators lose secondary?sale income.


Blockchain Developer Interview / Practical Insights

  1. ERC?721 vs ERC?1155: Interviewers may ask why you’d choose one over the other. Highlight gas savings with batch transfers in ERC?1155 and the simplicity of ERC?721 for pure?NFT projects.
  2. call vs delegatecall: Explain that delegatecall runs the callee’s code in the caller’s context—dangerous for upgradeable proxies if the implementation is untrusted.
  3. Royalty enforcement: Be ready to discuss how ERC?2981 works, its limitations (it’s advisory), and how on?chain marketplaces can enforce it automatically.
  4. Layer?2 considerations: Show awareness of how NFTs are minted on Optimistic Rollups (e.g., using l2Bridge contracts) versus ZK?Rollups where data availability is off?chain but proofs guarantee ownership.

Quick Check Questions

  1. Scenario: A contract uses require(tx.origin == owner) to restrict who can update an NFT’s metadata.
    Answer: Dangerous – tx.origin can be hijacked by a phishing contract, allowing an attacker to bypass the check. Use msg.sender and compare to ownerOf(tokenId).

  2. Scenario: You want to batch?mint 100 NFTs in a single transaction. Which standard gives the lowest gas cost?
    Answer: ERC?1155, because it supports batch mint and safeTransferFrom operations that write fewer storage slots per token.

  3. Scenario: A marketplace lists an NFT but the buyer’s transaction fails with “ERC721: transfer to non?ERC721Receiver implementer”.
    Answer: The buyer’s address is a contract that does not implement onERC721Received; the marketplace should use safeTransferFrom or the buyer must add the ERC721 receiver interface.


Last?Minute Cram Sheet (10 one?liners)

  1. ERC?721 = 1 token = 1 owner; ERC?1155 = many tokens per contract.
  2. tokenURI must return a valid HTTP or IPFS URI; browsers can’t render malformed JSON.
  3. Never use tx.origin for auth – it’s vulnerable to phishing contracts.
  4. _safeMint-“mint + safety check”; always prefer it over _mint.
  5. Gas tip: Use uint256 private _nextId; and increment after minting to avoid extra storage reads.
  6. ERC?2981 royalty = (salePrice * royaltyFraction) / 10_000; denominator is basis points.
  7. OpenZeppelin’s ERC721URIStorage stores a full URI per token – handy for mutable metadata.
  8. IPFS CID version 1 (base32) works better with ENS and browsers than CIDv0 (base58).
  9. When deploying, set the compiler to pragma solidity ^0.8.20; – includes built?in overflow checks.
  10. Approve only the marketplace you trust (setApprovalForAll) – blanket approvals can be abused.