NFT Marketplace Development
OpenSea processes billions of dollars in trades, but that doesn't mean "building a marketplace" is simple. Most custom marketplaces die from one of three problems: vulnerability in listing logic (allows buying NFT at old price after transfer), lack of royalty enforcement, or inability to scale offers without on-chain transaction for each. These problems are technically solved in modern protocols — you just need to apply them correctly.
Listing Protocols: Don't Reinvent the Wheel
Seaport — De Facto Standard
OpenSea's Seaport Protocol (v1.5, audited by Spearbit + Trail of Bits) — most proven foundation for marketplace. Key advantage: orders exist off-chain as signed EIP-712 structures. Listing costs no gas. Only order fulfillment — on-chain transaction.
Seaport architecture: offerer signs Order with offer[] (what I offer) and consideration[] (what I want). This is generalized swap primitive: NFT for ETH, NFT for ERC-20, NFT for NFT, batch orders. conduit — authorized contract that can transfer tokens on offerer's behalf when condition met. Removes need for direct marketplace approve — approve goes to Conduit Controller, which marketplace registers.
Why important: if marketplace implements custom listing storing orders on-chain — each listing costs ~50–80k gas. At 10,000 listings daily that's $50k/day just on listings (at 20 gwei gas, $2500 ETH). Off-chain signatures — only scalable approach.
Critical Vulnerability: Replay After Transfer
Classic attack on custom marketplaces: user lists NFT for 1 ETH, then transfers it to another wallet. Listing formally not cancelled (no on-chain cancel). New owner lists for 100 ETH. Attacker executes old listing (if signature valid) — buys for 1 ETH, NFT leaves from old listing, but owner already changed.
Seaport solves this via offererConduitKey + counter. incrementCounter() — one transaction, invalidates all previously signed orders from this address. Also each order can have startTime / endTime — automatic expiration without cancellation.
Royalty Enforcement: EIP-2981 Not Enough
EIP-2981 defines royaltyInfo(uint256 tokenId, uint256 salePrice) — standard for storing royalty info in NFT contract. But it's advisory mechanism — marketplace can ignore it. Most aggregators in 2022–2023 did exactly that, bypassing creator royalties.
Enforced royalty possible through approaches:
Operator Filter Registry (OpenSea approach) — NFT contract checks in setApprovalForAll and transferFrom that operator is in Operator Filter Registry whitelist. Marketplaces without royalty enforcement blocked. Downside: creator must support, users lose transferability if marketplace refuses.
ERC-721C (LimitBreak) — modified transfer with built-in policy engine. More flexible: different policies for different collections. But non-standard contract.
Soulbound + secondary market through own contract — NFT with limited transfer, all secondary sales only through protocol with royalty enforcement. Radical, but works for gaming assets.
For our marketplace: implement EIP-2981 as minimum, offer optional Operator Filter for collections wanting enforced royalty.
Auctions: Technical Complexities
English Auction and Bid Snipping
Classic English auction: each bid extends auction by N minutes. Standard protection against bid snipping (last-second bids). In Solidity: if (block.timestamp > endTime - extensionWindow) endTime += extensionWindow.
Critical: storing all bids on-chain is expensive. Better to store only highestBid and highestBidder. Previous highest bidder gets refund automatically on each new bid via pull payment pattern.
Dutch Auction for Mints
Price starts high, falls every N minutes/blocks. Math:
currentPrice = startPrice - (startPrice - endPrice) * elapsed / duration
Danger: if using block.number instead of block.timestamp on L2 (Optimism, Arbitrum) — block time unstable, price falls unevenly. Always block.timestamp for time-based logic on L2.
Refund mechanism in dutch auction — if user minted at $100 price, final price $60, difference must be returned. This is settlementPrice refund pattern: record each transaction, after auction ends users claim refund.
Architecture and Stack
| Layer | Technology | Comment |
|---|---|---|
| Order protocol | Seaport v1.5 | Off-chain signatures, audited |
| NFT standard | ERC-721 + ERC-2981 | Royalty info |
| Batch transfers | ERC-1155 | For gaming/edition NFT |
| Metadata | IPFS (Pinata/NFT.Storage) | Decentralized storage |
| Indexing | The Graph (subgraph) | Trade history, listings |
| Frontend | wagmi + viem | WalletConnect v2, MetaMask |
| Search/filter | PostgreSQL + Elasticsearch | Off-chain index for speed |
Subgraph as Data Foundation
Marketplace without subgraph — marketplace without history. The Graph indexes contract events: OrderFulfilled, OrderCancelled, Transfer, RoyaltyPaid. Schema entities: Token, Collection, Order, Sale, Account. GraphQL queries give history, trending, floor price — all without on-chain RPC calls.
Floor price calculated off-chain from active listings, stored in PostgreSQL, updated on each new listing/cancellation via subgraph webhook.
Development Process
Analysis (3–5 days). Define: asset types (ERC-721 / ERC-1155 / mixed), need for primary mint through marketplace, royalty enforcement requirements, target chains.
Design (1 week). Contract architecture, subgraph schema, database schema, API endpoints.
Development (4–8 weeks). Smart contracts + subgraph + backend API + frontend. Parallel tracks with integration at stitch points.
Testing. Fork tests with real Seaport orders. E2E tests on Sepolia.
Audit and Deployment. Audit focus: replay attacks, royalty bypass, bid manipulation in auctions. Deployment with multisig owner.
Timeline Estimates
MVP marketplace with fixed-price listings on Seaport — 3–4 weeks. Full platform with auctions, offers, royalty enforcement and subgraph — 2–3 months.
Cost calculated after analyzing requirements.







