P11: NFT Marketplace

P11: NFT Marketplace

Project Overview

Attribute Value
Main Language Solidity + TypeScript
Alternative Languages Vyper + Python, Rust (Solana)
Difficulty Advanced
Coolness Level Level 4: Hardcore Tech Flex
Business Potential The โ€œOpen Coreโ€ Infrastructure
Knowledge Area NFTs / Marketplaces
Software or Tool OpenSea Clone
Main Book โ€œMastering Ethereumโ€ by Andreas M. Antonopoulos & Gavin Wood

Learning Objectives

By completing this project, you will:

  1. Master the ERC-721 token standard understanding how non-fungible tokens differ fundamentally from ERC-20 tokens and why unique token IDs enable digital ownership
  2. Implement marketplace listing mechanics including both escrow-based and approval-based patterns, understanding the security trade-offs of each approach
  3. Build multiple auction types including English (ascending), Dutch (descending), and sealed-bid auctions, understanding the game theory behind each
  4. Integrate the ERC-2981 royalty standard enabling automatic creator compensation on secondary sales, solving the attribution problem in digital art
  5. Handle offer mechanics and escrow patterns safely managing funds during the negotiation and settlement process
  6. Design gas-efficient marketplace contracts using packed structs, assembly optimization, and batch operations
  7. Build a complete frontend that interacts with IPFS for metadata and blockchain for ownership, creating a seamless user experience

Real World Outcome

When your NFT marketplace is complete, users will experience a fully functional decentralized trading platform. Here is exactly what they will see and interact with:

Web Interface - Home Page

+------------------------------------------------------------------+
|  [Logo] NFT Marketplace          [Connect Wallet]  [0x7a2...3f4] |
+------------------------------------------------------------------+
|  [Explore]  [Create]  [My NFTs]  [Activity]                      |
+------------------------------------------------------------------+
|                                                                   |
|  FEATURED COLLECTIONS                                             |
|  +----------------+  +----------------+  +----------------+       |
|  | [Bored Ape #42]|  | [CryptoPunk #7]|  | [Azuki #1337] |       |
|  | Floor: 2.5 ETH |  | Floor: 15 ETH  |  | Floor: 3.2 ETH|       |
|  | 24h Vol: 45 ETH|  | 24h Vol: 120ETH|  | 24h Vol: 28ETH|       |
|  +----------------+  +----------------+  +----------------+       |
|                                                                   |
|  LIVE AUCTIONS                               TIME REMAINING       |
|  +--------------------------------------------------+            |
|  | [NFT Image]  "Genesis #001"      Current: 5.2 ETH | 2h 34m   |
|  | [NFT Image]  "Pixel Dragon #88"  Current: 1.8 ETH | 45m 12s  |
|  | [NFT Image]  "Meta House #5"     Current: 12.1 ETH| 5h 02m   |
|  +--------------------------------------------------+            |
|                                                                   |
|  RECENT SALES                                                     |
|  +--------------------------------------------------+            |
|  | CryptoKitty #234    Sold for 0.5 ETH    2 min ago |            |
|  | Bored Ape #8821     Sold for 85 ETH     15 min ago|            |
|  | Doodle #4420        Sold for 3.2 ETH    32 min ago|            |
|  +--------------------------------------------------+            |
+------------------------------------------------------------------+

NFT Detail Page

+------------------------------------------------------------------+
|  [Back to Collection]                                             |
+------------------------------------------------------------------+
|  +------------------+                                             |
|  |                  |   BORED APE YACHT CLUB #4821               |
|  |   [NFT Image]    |   Owned by 0x742d...f44e                   |
|  |                  |                                             |
|  |                  |   Created by: 0xBC4C...a891 (2.5% royalty) |
|  +------------------+                                             |
|                                                                   |
|  +--------------------------------------------------+            |
|  |  CURRENT PRICE                                    |            |
|  |  42.5 ETH ($127,500)                             |            |
|  |                                                   |            |
|  |  [  BUY NOW  ]    [  MAKE OFFER  ]               |            |
|  +--------------------------------------------------+            |
|                                                                   |
|  PRICE HISTORY                                                    |
|  [Chart showing price over time with sale points marked]          |
|                                                                   |
|  OFFERS                                                           |
|  +--------------------------------------------------+            |
|  | 0x123...abc    38.0 ETH    Expires: 2 days       | [Accept]  |
|  | 0x456...def    35.5 ETH    Expires: 5 days       | [Accept]  |
|  | 0x789...ghi    30.0 ETH    Expires: 7 days       | [Accept]  |
|  +--------------------------------------------------+            |
|                                                                   |
|  ACTIVITY                                                         |
|  +--------------------------------------------------+            |
|  | Listed     42.5 ETH    by 0x742...f44e    1 day ago          |
|  | Transfer   --          0xabc -> 0x742     3 days ago          |
|  | Sale       35.0 ETH    0xdef -> 0xabc     2 weeks ago         |
|  | Minted     --          by 0xBC4...a891    6 months ago        |
|  +--------------------------------------------------+            |
|                                                                   |
|  PROPERTIES                                                       |
|  +------------+  +------------+  +------------+                   |
|  | Background |  | Fur        |  | Eyes       |                   |
|  | Aquamarine |  | Robot      |  | Laser      |                   |
|  | 12% rare   |  | 2% rare    |  | 0.5% rare  |                   |
|  +------------+  +------------+  +------------+                   |
+------------------------------------------------------------------+

Example Marketplace Interactions

Scenario 1: Listing an NFT for Fixed Price Sale

User clicks โ€œSellโ€ on their NFT:

+--------------------------------------------------+
|  LIST YOUR NFT FOR SALE                          |
+--------------------------------------------------+
|  NFT: CryptoPunk #7234                           |
|                                                   |
|  Listing Type:  (x) Fixed Price  ( ) Auction     |
|                                                   |
|  Price: [____15.5____] ETH                       |
|                                                   |
|  Duration: [7 days      v]                       |
|                                                   |
|  Marketplace Fee: 2.5%                           |
|  Creator Royalty: 5.0%                           |
|  You Receive: 92.5% = 14.3375 ETH               |
|                                                   |
|  [ APPROVE NFT ]  โ†’  [ CREATE LISTING ]          |
+--------------------------------------------------+

Transaction Output (Listing):

Transaction Hash: 0x7a8b...4f2e
Status: Success
Block: 18,432,109
Gas Used: 124,532 (0.0031 ETH)

Event Logs:
  Approval(owner: 0x742d...f44e, approved: 0xMarketplace, tokenId: 7234)
  Listed(listingId: 0xa8f3...2e1b, seller: 0x742d...f44e, nft: 0xPunks, tokenId: 7234, price: 15500000000000000000)

Scenario 2: Buying an NFT

User clicks โ€œBuy Nowโ€ on a listed NFT:

+--------------------------------------------------+
|  CONFIRM PURCHASE                                |
+--------------------------------------------------+
|  You are buying:                                 |
|  [NFT Thumbnail] Bored Ape #4821                 |
|                                                   |
|  Price:                        42.50 ETH         |
|  + Marketplace Fee (2.5%):      1.0625 ETH       |
|  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€           |
|  Total:                        43.5625 ETH       |
|                                                   |
|  Your Balance: 52.34 ETH                         |
|                                                   |
|  Distribution:                                   |
|  โ†’ Seller (0x742d...f44e):    41.4375 ETH       |
|  โ†’ Creator Royalty (2.5%):     1.0625 ETH       |
|  โ†’ Marketplace Fee (2.5%):     1.0625 ETH       |
|                                                   |
|  [  CONFIRM PURCHASE  ]                          |
+--------------------------------------------------+

Transaction Output (Purchase with Royalty Distribution):

Transaction Hash: 0x9c3d...8e7f
Status: Success
Block: 18,432,215
Gas Used: 187,234 (0.0047 ETH)

Event Logs:
  Transfer(from: 0x742d...f44e, to: 0x5aAe...3ced, tokenId: 4821)
  RoyaltyPaid(receiver: 0xBC4C...a891, amount: 1062500000000000000)
  Sale(listingId: 0xa8f3...2e1b, buyer: 0x5aAe...3ced, price: 42500000000000000000)

Internal Transactions:
  0x742d...f44e received 41.4375 ETH (seller proceeds)
  0xBC4C...a891 received 1.0625 ETH (creator royalty)
  0xFee...Treasury received 1.0625 ETH (marketplace fee)

Scenario 3: English Auction (Ascending Price)

+--------------------------------------------------+
|  LIVE AUCTION                                    |
+--------------------------------------------------+
|  [Large NFT Image]                               |
|                                                   |
|  "Celestial Genesis #001"                        |
|  by ArtistDAO                                    |
|                                                   |
|  CURRENT BID: 12.5 ETH                          |
|  by 0x789...ghi                                  |
|                                                   |
|  TIME REMAINING: 02:34:17                        |
|  [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘] 65%                      |
|                                                   |
|  Minimum Next Bid: 13.125 ETH (+5%)             |
|                                                   |
|  Your Bid: [________] ETH                        |
|                                                   |
|  [  PLACE BID  ]                                 |
|                                                   |
|  BID HISTORY:                                    |
|  12.5 ETH  0x789...ghi   2 hours ago            |
|  11.0 ETH  0x456...def   4 hours ago            |
|  10.0 ETH  0x123...abc   6 hours ago            |
|   8.5 ETH  0x789...ghi   8 hours ago (start)    |
+--------------------------------------------------+

Transaction Output (Auction Bid):

Transaction Hash: 0x2b5e...9d1a
Status: Success
Block: 18,432,301

Event Logs:
  BidPlaced(auctionId: 0x7f2a...3e8b, bidder: 0xnew...addr, amount: 13500000000000000000)
  BidRefunded(previousBidder: 0x789...ghi, amount: 12500000000000000000)

Complete User Journey: Creator to Collector

CREATOR FLOW:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 1. CREATE COLLECTION                                           โ”‚
โ”‚    - Deploy ERC-721 contract (or use lazy minting)            โ”‚
โ”‚    - Set royalty percentage (e.g., 5%)                        โ”‚
โ”‚    - Upload metadata to IPFS                                  โ”‚
โ”‚                                                                โ”‚
โ”‚ 2. MINT NFT                                                    โ”‚
โ”‚    - Upload artwork to IPFS โ†’ returns CID                     โ”‚
โ”‚    - Create metadata JSON โ†’ upload to IPFS                    โ”‚
โ”‚    - Call mint(tokenURI) on contract                          โ”‚
โ”‚                                                                โ”‚
โ”‚ 3. LIST ON MARKETPLACE                                         โ”‚
โ”‚    - Approve marketplace contract                              โ”‚
โ”‚    - Create listing with price/auction parameters             โ”‚
โ”‚                                                                โ”‚
โ”‚ 4. RECEIVE PAYMENTS                                            โ”‚
โ”‚    - Primary sale: 100% - marketplace fee                     โ”‚
โ”‚    - All secondary sales: Automatic royalty via ERC-2981      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

COLLECTOR FLOW:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 1. BROWSE & DISCOVER                                           โ”‚
โ”‚    - Search by collection, traits, price range                โ”‚
โ”‚    - View rarity rankings and price history                   โ”‚
โ”‚                                                                โ”‚
โ”‚ 2. ACQUIRE                                                     โ”‚
โ”‚    - Buy Now: Instant purchase at listed price                โ”‚
โ”‚    - Make Offer: Propose lower price (seller can accept)      โ”‚
โ”‚    - Bid in Auction: Compete for unique pieces                โ”‚
โ”‚                                                                โ”‚
โ”‚ 3. MANAGE PORTFOLIO                                            โ”‚
โ”‚    - View all owned NFTs in "My NFTs" page                    โ”‚
โ”‚    - Track floor prices and portfolio value                   โ”‚
โ”‚    - List for resale when desired                             โ”‚
โ”‚                                                                โ”‚
โ”‚ 4. RESELL                                                      โ”‚
โ”‚    - List owned NFT on marketplace                            โ”‚
โ”‚    - Creator automatically receives royalty on sale           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Example Royalty Distribution Flow

SECONDARY SALE: "CryptoArt #42" sells for 10 ETH

Original Creator:     0xCreator...001 (set 5% royalty)
Previous Owner:       0xSeller...002
New Owner:            0xBuyer...003
Marketplace:          0xMarket...004 (2.5% fee)

DISTRIBUTION:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                                                โ”‚
โ”‚  Buyer Pays:        10.00 ETH                                 โ”‚
โ”‚                     โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                               โ”‚
โ”‚  Creator Royalty:    0.50 ETH (5.0%) โ†’ 0xCreator...001        โ”‚
โ”‚  Marketplace Fee:    0.25 ETH (2.5%) โ†’ 0xMarket...004         โ”‚
โ”‚  Seller Receives:    9.25 ETH (92.5%) โ†’ 0xSeller...002        โ”‚
โ”‚                     โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                               โ”‚
โ”‚  Total:             10.00 ETH                                 โ”‚
โ”‚                                                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

On-Chain Verification:
> cast call $NFT "royaltyInfo(uint256,uint256)" 42 10000000000000000000
> Returns: (0xCreator...001, 500000000000000000) // 0.5 ETH to creator

The Core Question Youโ€™re Answering

How does digital ownership work when thereโ€™s no physical object to possess?

This project answers one of the most profound questions in the digital age: What does it mean to โ€œownโ€ something that can be infinitely copied? The traditional answer was: you canโ€™t. Digital files have no scarcity.

NFTs invert this by shifting from copy-protection to origin-verification:

Traditional Digital Ownership:        NFT Ownership:
"I have the only copy"               "I have the verified original"
     โ†“                                    โ†“
Enforced by DRM                      Enforced by cryptography
Can be cracked                       Mathematically impossible to forge
Requires central authority           Self-verifying on blockchain

The ERC-721 standard creates provable uniqueness. Each token ID maps to exactly one owner at any time:

// The fundamental truth of NFT ownership
mapping(uint256 => address) private _owners;

// tokenId 42 belongs to exactly one address
// This is enforced by consensus of thousands of nodes
// No one can claim otherwise without rewriting the blockchain

Understanding this project means understanding:

  • Why ownership is a social construct that blockchains formalize mathematically
  • How marketplaces coordinate the exchange of unique digital assets
  • Why creator royalties represent a new economic model for digital art
  • How the same principles apply beyond art to any unique digital asset (domains, tickets, credentials, real estate deeds)

Concepts You Must Understand First

1. ERC-721 Token Standard

The ERC-721 standard defines the interface for non-fungible tokens. Unlike ERC-20 where all tokens are identical, each ERC-721 token has a unique tokenId.

Core Interface:

interface IERC721 {
    function balanceOf(address owner) external view returns (uint256);
    function ownerOf(uint256 tokenId) external view returns (address);
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function setApprovalForAll(address operator, bool approved) external;
    function getApproved(uint256 tokenId) external view returns (address);
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

Book Reference: โ€œMastering Ethereumโ€ Chapter 10: Tokens - discusses token standards and NFT fundamentals.

Questions to Test Understanding:

  • Why does ownerOf() return an address, while ERC-20โ€™s balanceOf() returns a uint256?
  • Whatโ€™s the difference between approve() and setApprovalForAll()?
  • Why does safeTransferFrom() exist alongside transferFrom()?
  • How does an NFT collection create multiple tokens while remaining ERC-721 compliant?

2. The Approval Pattern

Before a marketplace can transfer your NFT, you must give it permission. There are two types of approval:

Single Token Approval:              Operator Approval:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ approve(market, 42) โ”‚            โ”‚ setApprovalForAll(  โ”‚
โ”‚                     โ”‚            โ”‚   market, true)     โ”‚
โ”‚ Only token #42      โ”‚            โ”‚                     โ”‚
โ”‚ can be transferred  โ”‚            โ”‚ ALL your tokens in  โ”‚
โ”‚ by marketplace      โ”‚            โ”‚ this collection can โ”‚
โ”‚                     โ”‚            โ”‚ be transferred      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Book Reference: โ€œMastering Ethereumโ€ Chapter 10 covers approval mechanics in the context of token standards.

Questions to Test Understanding:

  • Why would you ever use single-token approval instead of operator approval?
  • What security risks does setApprovalForAll(true) introduce?
  • How do malicious sites exploit approval patterns to drain wallets?
  • How does revocation work, and why should users periodically audit their approvals?

3. Escrow vs. Approval-Based Marketplace Design

There are two fundamental patterns for marketplace contracts:

Escrow Pattern:

User lists NFT โ†’ NFT transfers to marketplace contract โ†’ stays there until sold
Pros: Simpler logic, clear ownership during listing
Cons: User loses possession, gas for transfer in/out

Approval Pattern:

User lists NFT โ†’ NFT stays in user's wallet โ†’ transfers directly to buyer on sale
Pros: User retains NFT until sold, more gas efficient
Cons: User can transfer NFT elsewhere (breaking listing), more complex checks

Book Reference: โ€œDesigning Data-Intensive Applicationsโ€ Chapter 7 discusses transaction isolation levels and consistency guarantees that parallel these trade-offs.

Questions to Test Understanding:

  • What happens in the approval pattern if the user transfers the NFT elsewhere after listing?
  • Why do most production marketplaces (like OpenSea) use the approval pattern?
  • How does the escrow pattern handle a seller who wants to cancel their listing?
  • What reentrancy risks exist in each pattern?

4. ERC-2981 Royalty Standard

ERC-2981 defines a standard way for NFT contracts to signal royalty information:

interface IERC2981 {
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external view returns (address receiver, uint256 royaltyAmount);
}

Key Insight: ERC-2981 is informational only. It tells marketplaces โ€œthis creator wants X% royalty,โ€ but marketplaces must choose to honor it. Thereโ€™s no on-chain enforcement.

Book Reference: โ€œMastering Ethereumโ€ discussions of token standards and the composability of smart contracts.

Questions to Test Understanding:

  • Why canโ€™t ERC-2981 enforce royalties on-chain?
  • How do platforms like OpenSea, Blur, and LooksRare differ in royalty enforcement?
  • What are the economic arguments for and against mandatory royalties?
  • How might future standards (like bound transfer hooks) enable enforced royalties?

5. Auction Theory and Game Theory

Different auction types optimize for different goals:

English Auction (Ascending):

Start low โ†’ Bidders compete โ†’ Highest bid wins
- Maximizes price discovery
- Creates FOMO and competition
- Most common for valuable NFTs

Dutch Auction (Descending):

Start high โ†’ Price drops over time โ†’ First bidder wins
- Faster conclusion
- Buyer sets their maximum willingness-to-pay
- Good for initial drops (avoids gas wars)

Sealed-Bid (Vickrey):

All bids hidden โ†’ Highest bidder wins โ†’ Pays second-highest price
- Incentivizes truthful bidding
- More complex to implement on-chain

Book Reference: โ€œComputer Systems: A Programmerโ€™s Perspectiveโ€ Chapter 12 discusses concurrency which relates to handling simultaneous bids.

Questions to Test Understanding:

  • Why does a Dutch auction prevent gas wars during NFT drops?
  • In a Vickrey auction, why is paying the second-highest price strategically important?
  • How do you handle bid sniping (last-second bids) in English auctions?
  • What prevents bid manipulation in on-chain auctions where all data is public?

6. IPFS and Metadata Storage

NFTs donโ€™t store images on-chain (too expensive). Instead, they store a tokenURI pointing to metadata:

On-Chain:                      Off-Chain (IPFS):
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ tokenId: 42            โ”‚     โ”‚ {                          โ”‚
โ”‚ owner: 0x742...f44e    โ”‚     โ”‚   "name": "Bored Ape #42", โ”‚
โ”‚ tokenURI:              โ”‚โ”€โ”€โ”€โ”€>โ”‚   "image": "ipfs://Qm...", โ”‚
โ”‚   "ipfs://QmXyz..."    โ”‚     โ”‚   "attributes": [...]      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚ }                          โ”‚
                               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

IPFS Addressing: Content-addressed storage means the hash of the content IS the address. Change the file, change the address. This provides immutability.

Book Reference: โ€œDesigning Data-Intensive Applicationsโ€ Chapter 3 covers storage engines and content-addressable storage concepts.

Questions to Test Understanding:

  • Why do many NFT projects use IPFS instead of regular HTTP URLs?
  • What happens to an NFT if the IPFS gateway goes down?
  • How does pinning work, and who is responsible for keeping NFT metadata available?
  • What are the trade-offs of storing metadata fully on-chain (like Loot)?

7. Reentrancy and Marketplace Security

Marketplaces handle ETH transfers, making them prime targets for reentrancy attacks:

Malicious Contract:
1. Calls buyNFT() with ETH
2. Marketplace sends NFT to attacker
3. Marketplace refunds excess ETH to attacker
4. Attacker's receive() function calls buyNFT() again
5. Repeat until marketplace drained

Book Reference: โ€œSerious Cryptographyโ€ discusses protocol security, and โ€œMastering Ethereumโ€ Chapter 9 covers smart contract security patterns.

Questions to Test Understanding:

  • At what points in a marketplace transaction is reentrancy possible?
  • How does the Checks-Effects-Interactions pattern prevent reentrancy?
  • When is transfer() vs call() for sending ETH more appropriate?
  • How do reentrancy guards (locks) work internally?

8. Gas Optimization for Marketplace Operations

Marketplace operations are gas-intensive. Understanding optimization is crucial:

// Unoptimized: 6 storage reads
struct Listing {
    address seller;      // 20 bytes, slot 0
    bool active;         // 1 byte, slot 1
    uint256 price;       // 32 bytes, slot 2
    address nftContract; // 20 bytes, slot 3
    uint256 tokenId;     // 32 bytes, slot 4
}

// Optimized: Pack into fewer slots
struct Listing {
    address seller;      // 20 bytes  โ”
    uint96 price;        // 12 bytes  โ”ดโ”€ slot 0 (32 bytes)
    address nftContract; // 20 bytes  โ”
    uint80 tokenId;      // 10 bytes  โ”‚
    bool active;         // 1 byte    โ”ดโ”€ slot 1 (31 bytes)
}

Book Reference: โ€œComputer Systems: A Programmerโ€™s Perspectiveโ€ Chapter 6 covers memory hierarchy and cache optimization principles that apply to EVM storage.

Questions to Test Understanding:

  • Why does reading from the same storage slot cost less gas than reading from different slots?
  • How do you balance uint96/uint80 constraints with real-world price/tokenId ranges?
  • When does it make sense to use memory vs. storage vs. calldata?
  • How do batch operations (listing multiple NFTs) save gas?

Questions to Guide Your Design

NFT Contract Design

  1. How will you handle the relationship between your marketplace and external NFT contracts?
  2. Should your marketplace work with any ERC-721, or only specific collections?
  3. How will you verify that an NFT contract implements ERC-721 before listing?
  4. How will you handle NFT contracts that donโ€™t implement ERC-2981 for royalties?

Listing Mechanics

  1. Will you use escrow or approval pattern? What are the trade-offs in your specific case?
  2. How will you handle listing expiration? On-chain timer or off-chain with on-chain verification?
  3. What happens if a seller transfers their NFT elsewhere while itโ€™s listed?
  4. How will you prevent users from listing the same NFT multiple times?

Pricing and Auctions

  1. Will you support multiple currencies (ETH, WETH, other ERC-20s)?
  2. For auctions, how will you handle the minimum bid increment?
  3. How will you prevent bid sniping in English auctions? (Extend time on late bids?)
  4. For Dutch auctions, how frequently should the price decrease?

Fees and Royalties

  1. How will you calculate and distribute multiple fee recipients (marketplace, creator, seller)?
  2. Will you honor ERC-2981 royalties, or allow sellers to bypass them?
  3. How will you handle royalties for NFTs that donโ€™t implement ERC-2981?
  4. What happens if royalty info returns an invalid address (address(0))?

Security and Edge Cases

  1. How will you prevent reentrancy during purchase/settlement?
  2. What happens if a buyer sends more ETH than the listing price?
  3. How will you handle failed ETH transfers (seller is a contract that rejects ETH)?
  4. What emergency functions will you include (pause, withdraw)?

Thinking Exercise

Before writing any code, trace through this complete marketplace scenario on paper:

Scenario: Secondary Sale with Auction and Royalties

Setup:

  • Creator Alice minted โ€œGenesis Art #1โ€ with 5% royalty (ERC-2981)
  • Alice sold it to Bob for 10 ETH (primary sale, no royalty)
  • Bob lists it for English auction, starting at 5 ETH, 24-hour duration

Events:

  1. T+0h: Bob calls createAuction(nftContract, tokenId, startPrice=5ETH, duration=24h)
  2. T+2h: Carol bids 6 ETH
  3. T+8h: Dave bids 8 ETH
  4. T+23h: Eve bids 12 ETH
  5. T+23h59m: Dave bids 15 ETH (triggers time extension)
  6. T+24h29m: Eve bids 16 ETH
  7. T+24h44m: Auction ends, Eve wins

Trace on paper:

STEP 1: createAuction()
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ State Before:                                                  โ”‚
โ”‚   _owners[1] = Bob                                            โ”‚
โ”‚   _approvals[1] = Marketplace (or isApprovedForAll = true)    โ”‚
โ”‚   auctions = empty                                             โ”‚
โ”‚                                                                โ”‚
โ”‚ Actions:                                                       โ”‚
โ”‚   1. Verify Bob owns token #1                                 โ”‚
โ”‚   2. Verify marketplace has approval                          โ”‚
โ”‚   3. Store auction: { seller: Bob, start: 5 ETH, end: T+24h } โ”‚
โ”‚   4. Emit AuctionCreated event                                โ”‚
โ”‚                                                                โ”‚
โ”‚ State After:                                                   โ”‚
โ”‚   auctions[auctionId] = { seller: Bob, ... }                  โ”‚
โ”‚   NFT still with Bob (approval pattern)                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

STEP 2: Carol bids 6 ETH
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ State Before:                                                  โ”‚
โ”‚   highestBidder = address(0)                                  โ”‚
โ”‚   highestBid = 0                                              โ”‚
โ”‚                                                                โ”‚
โ”‚ Actions:                                                       โ”‚
โ”‚   1. Verify auction active                                    โ”‚
โ”‚   2. Verify 6 ETH > 5 ETH (start price)                       โ”‚
โ”‚   3. Store Carol's bid                                        โ”‚
โ”‚   4. Hold 6 ETH in escrow                                     โ”‚
โ”‚                                                                โ”‚
โ”‚ State After:                                                   โ”‚
โ”‚   highestBidder = Carol                                       โ”‚
โ”‚   highestBid = 6 ETH                                          โ”‚
โ”‚   escrowedFunds[Carol] = 6 ETH                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

[Continue tracing each step...]

STEP 6: Eve bids 16 ETH (final)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Actions:                                                       โ”‚
โ”‚   1. Verify 16 > 15 ETH (previous bid)                        โ”‚
โ”‚   2. Refund Dave's 15 ETH                                     โ”‚
โ”‚   3. Store Eve's bid                                          โ”‚
โ”‚   4. Check time remaining (15 min < 10 min extension threshold)โ”‚
โ”‚   5. Extend auction by 10 minutes                             โ”‚
โ”‚                                                                โ”‚
โ”‚ State After:                                                   โ”‚
โ”‚   highestBidder = Eve                                         โ”‚
โ”‚   highestBid = 16 ETH                                         โ”‚
โ”‚   auctionEndTime = T+24h44m                                   โ”‚
โ”‚   Dave's 15 ETH returned                                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

STEP 7: Auction Settlement
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Preconditions:                                                 โ”‚
โ”‚   - Current time > auction end time                           โ”‚
โ”‚   - Auction not already settled                               โ”‚
โ”‚                                                                โ”‚
โ”‚ Fee Calculation (16 ETH sale):                                โ”‚
โ”‚   Creator Royalty (5%): 0.8 ETH โ†’ Alice (0xCreator)          โ”‚
โ”‚   Marketplace Fee (2.5%): 0.4 ETH โ†’ Treasury                 โ”‚
โ”‚   Seller Proceeds: 14.8 ETH โ†’ Bob                            โ”‚
โ”‚   Total: 16.0 ETH โœ“                                          โ”‚
โ”‚                                                                โ”‚
โ”‚ Actions:                                                       โ”‚
โ”‚   1. Transfer NFT from Bob to Eve                             โ”‚
โ”‚   2. Send 0.8 ETH to Alice (creator)                         โ”‚
โ”‚   3. Send 0.4 ETH to Treasury                                โ”‚
โ”‚   4. Send 14.8 ETH to Bob (seller)                           โ”‚
โ”‚   5. Mark auction as settled                                  โ”‚
โ”‚                                                                โ”‚
โ”‚ Final State:                                                   โ”‚
โ”‚   _owners[1] = Eve                                            โ”‚
โ”‚   Bob: +14.8 ETH                                              โ”‚
โ”‚   Alice: +0.8 ETH (royalty from secondary sale!)             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Questions to answer after tracing:

  1. What if Bob revokes marketplace approval between listing and settlement?
  2. What if Daveโ€™s refund fails (his address is a contract that reverts)?
  3. How do you prevent Eve from calling settleAuction before time expires?
  4. What happens if the ERC-2981 royaltyInfo call returns address(0)?
  5. If thereโ€™s a 10-minute extension rule, can an auction run forever with constant sniping?

The Interview Questions Theyโ€™ll Ask

ERC-721 Standard Deep Dive

  1. What is the difference between transferFrom and safeTransferFrom?
    • safeTransferFrom checks if the recipient is a contract, and if so, calls onERC721Received() to verify the contract can handle NFTs. Prevents tokens from being locked forever in incompatible contracts.
  2. Explain the purpose of tokenURI() and how metadata is typically stored.
    • Returns a URI pointing to JSON metadata (name, description, image, attributes). Typically stored on IPFS for immutability and decentralization.
  3. How do you implement a custom ERC-721 collection with rarity traits?
    • Trait data in metadata JSON, provenance hash for fairness, on-chain vs off-chain trait storage trade-offs.
  4. Whatโ€™s the difference between approve() and setApprovalForAll()?
    • approve() grants permission for a single tokenId. setApprovalForAll() grants operator status for all tokens owned by the sender in that collection.
  5. How does ERC-721A differ from standard ERC-721, and when would you use it?
    • ERC-721A optimizes batch minting by not updating balances per-token. Saves gas for minting many sequential tokens.

Marketplace Design Patterns

  1. Explain the trade-offs between escrow and approval-based marketplace designs.
    • Escrow: Simpler, NFT in custody, user loses possession, gas for transfer in/out
    • Approval: User retains NFT, more efficient, but seller can break listing by transferring elsewhere
  2. How do you prevent the seller from transferring an NFT after listing it (in approval pattern)?
    • Check ownerOf() and approval status at purchase time. Accept that listings can become invalid.
  3. How would you implement a collection-wide floor price feature?
    • Track lowest active listing per collection, update on list/delist/purchase, use heap or sorted set for efficiency.
  4. Describe how youโ€™d implement lazy minting.
    • Creator signs a message containing tokenURI and parameters. First buyer submits signature to mint-and-buy atomically. Creator never pays gas until sale.
  5. How do you handle offers on unlisted NFTs?
    • Store offers mapping (nft, tokenId) => offers[]. Seller can accept by transferring to marketplace with offer reference. Or use signature-based offers.

Auction Mechanics

  1. How do you implement anti-sniping measures in an English auction?
    • Time extension: if bid placed within last N minutes, extend auction by M minutes. Cap total extensions to prevent infinite auctions.
  2. Explain how a Dutch auction prevents gas wars during NFT drops.
    • Price starts high and decreases. No competition for the same price point. First buyer at each price level wins without racing.
  3. How would you implement a sealed-bid auction on a public blockchain?
    • Commit-reveal scheme: Bidders submit hash(bid, salt), then reveal bid+salt after commitment period. Prevents front-running.
  4. What happens if the highest bidderโ€™s transaction fails during settlement?
    • Store bids as escrowed ETH (sent with bid). Settlement uses stored funds, canโ€™t fail on buyer side. Handle seller-side failures with pull-over-push pattern.

Security Considerations

  1. How do you prevent reentrancy in a marketplace contract?
    • Checks-Effects-Interactions pattern: Update all state before external calls. Use reentrancy guards (mutex). Prefer pull-over-push for payments.
  2. What are the risks of using transfer() vs call() for ETH transfers?
    • transfer() has 2300 gas limit, can fail with receiving contracts. call() forwards all gas but requires reentrancy protection. Use call() with guards.
  3. How would you implement emergency pause functionality?
    • Pausable pattern with onlyOwner/multisig. Pause listing, buying; allow withdrawals. Consider timelock for unpause.
  4. What attacks are possible if royaltyInfo returns malicious data?
    • Returns 100% royalty to drain funds. Returns contract that reverts to DoS. Validate royalty <= max percentage, handle failed transfers gracefully.

Gas Optimization

  1. How can you optimize storage for listing data?
    • Pack structs into fewer storage slots (address + uint96 = 32 bytes). Use uint96 for prices (up to ~79 billion ETH). Batch operations with bitmap for status.
  2. What are the gas implications of checking ERC-2981 support on every sale?
    • supportsInterface() costs gas. Cache results per contract. Use try/catch for royaltyInfo() call. Consider off-chain registry.

Royalties and Economics

  1. Why canโ€™t ERC-2981 enforce royalties on-chain?
    • ERC-2981 is read-only (view function). Nothing prevents direct transfer bypassing marketplace. Enforcement requires transfer hooks (ERC-6150) or wrapper contracts.
  2. How has the royalty enforcement debate evolved? What are different approaches?
    • OpenSea: Optional, then enforced via blocklist. Blur: Minimum 0.5%. Creator coins: Royalties as exit fee. Bound NFTs: Blacklist non-royalty marketplaces.

Hints in Layers

Hint 1: Starting Point

Begin with a minimal ERC-721 contract that you fully understand. Donโ€™t use OpenZeppelin as a black box - study their implementation and write your own. Focus on _owners, _balances, and _tokenApprovals mappings. Get minting and transferring working before touching the marketplace.

Hint 2: Listing Structure

Your listing needs to capture: seller address, NFT contract address, token ID, price, listing type (fixed/auction), and status. Consider whether you need the listing ID to be deterministic (hash of nft+tokenId) or random. Deterministic makes lookups easier but prevents relisting the same NFT with different terms without canceling first.

Hint 3: The Purchase Flow

Think about purchase as a multi-step atomic operation: (1) verify listing active, (2) verify caller sent enough ETH, (3) calculate fee splits, (4) mark listing inactive, (5) transfer NFT, (6) distribute ETH. The order matters for security. What should happen before external calls vs. after?

Hint 4: Handling Royalties Gracefully

Not all NFTs implement ERC-2981. Use try/catch when calling royaltyInfo(). If it fails or returns address(0), proceed without royalty. If royalty percentage is unreasonable (>50%), cap it or reject. Document your policy clearly.

Hint 5: Auction State Machine

An auction moves through states: Created -> Active -> Ended -> Settled. Each state transition should be guarded. Only the highest bidder (or anyone after timeout) can trigger settlement. Store endTime rather than duration to avoid block.timestamp manipulation concerns across function calls.

Hint 6: Refunding Previous Bidders

When a new higher bid comes in, you must refund the previous bidder. Use pull-over-push: donโ€™t send ETH in the bid function, store pending refunds. Provide a withdraw() function for users to claim. This prevents griefing where a malicious bidder blocks new bids.

Hint 7: Testing Strategy

Test the unhappy paths as thoroughly as happy paths. Test: listing NFT you donโ€™t own, buying your own listing, bidding less than current bid, settling auction before end, double-settling, approval revoked between list and buy. Each should revert with clear error.

Hint 8: Frontend Integration

Your frontend needs to: (1) read listings from events or indexer, (2) fetch metadata from IPFS/HTTP, (3) prompt wallet approval before listing, (4) show transaction status during purchase. Consider using The Graph for indexing, wagmi/viem for wallet integration, and IPFS gateways with fallbacks.


Books That Will Help

Book Relevant Chapters What Youโ€™ll Learn
โ€œMastering Ethereumโ€ by Antonopoulos & Wood Ch. 10: Tokens; Ch. 11: Oracles; Ch. 9: Smart Contract Security ERC-721 fundamentals, how contracts interact, security patterns
โ€œDesigning Data-Intensive Applicationsโ€ by Kleppmann Ch. 7: Transactions; Ch. 8: Distributed Systems; Ch. 3: Storage Atomicity of marketplace operations, consistency guarantees, indexing for queries
โ€œComputer Systems: A Programmerโ€™s Perspectiveโ€ by Bryant & Oโ€™Hallaron Ch. 6: Memory Hierarchy; Ch. 12: Concurrent Programming Storage slot packing, understanding gas costs, handling concurrent bids
โ€œSerious Cryptographyโ€ by Aumasson Ch. 1: Encryption; Ch. 11: Authentication Understanding signatures for lazy minting, commit-reveal for sealed bids

Additional Resources

EIPs and Standards

Production Marketplace Contracts

  • OpenSea Seaport: GitHub - Advanced order-based marketplace protocol
  • LooksRare: GitHub - Production marketplace implementation
  • Blur: Study their aggregator and marketplace contracts on Etherscan

Development Tools

  • Foundry: Book - Modern Solidity testing framework
  • Hardhat: Docs - JavaScript-based development environment
  • IPFS: Docs - Decentralized storage for NFT metadata
  • The Graph: Docs - Indexing protocol for querying blockchain data

NFT Metadata Standards

  • OpenSea Metadata Standards: Docs - How to structure metadata for marketplace compatibility
  • ERC-721 Metadata JSON Schema: IPFS Example

Security Resources


Self-Assessment Checklist

Before moving to the next project, verify:

  • I can explain the difference between ERC-20 and ERC-721 at a fundamental level
  • I understand why safeTransferFrom exists and when itโ€™s necessary
  • I can implement both escrow and approval-based marketplace patterns
  • I understand the trade-offs of each pattern and can justify my choice
  • My marketplace correctly handles ERC-2981 royalties (including contracts that donโ€™t implement it)
  • I can implement at least two auction types (English, Dutch, or sealed-bid)
  • I understand how to prevent reentrancy in fund distribution
  • My tests cover listing, buying, auction bidding, and edge cases
  • I can explain why ERC-2981 canโ€™t enforce royalties and the marketplace ecosystem implications
  • I understand how NFT metadata works with IPFS and tokenURI
  • I can optimize storage layout for gas efficiency
  • Iโ€™ve considered emergency functions (pause, withdraw, upgrade path)

Whatโ€™s Next?

With NFT marketplace mechanics mastered, you now understand how digital ownership and commerce work on-chain. Youโ€™ve seen how unique tokens enable marketplaces, and how royalties create new creator economics.

In Project 12: DAO Governance System, youโ€™ll build the organizational primitive of Web3: decentralized decision-making. Youโ€™ll implement proposal creation, voting mechanisms, delegation, and timelocks. This is where token ownership transforms into governance power, and where code becomes the constitution of digital organizations.