NFT Collection Development
An NFT collection contract looks simple at first — OpenZeppelin ERC-721 with a mint() function. In practice, most problems arise not in the contract itself, but in the combination of metadata, storage, minting mechanics, and deployment. A collection where metadata is stored on a centralized server (HTTP URL in tokenURI) is vulnerable to "rug pull" in terms of NFT — the server owner can change images at any moment. This is not hypothetical: several collections in 2022–2023 replaced artwork after sale.
Choosing Standard and Minting Mechanics
ERC-721 vs ERC-1155
ERC-721 — one token, one owner. Suitable for PFP collections and unique art. ERC-1155 — one contract, multiple token types, supports fungible and semi-fungible. Right choice for game items where identical items can belong to thousands of players.
For standard PFP collection (10,000 unique tokens) — use ERC-721A (Azuki) instead of standard ERC-721. ERC-721A optimizes batch minting: minting 10 tokens in one transaction costs almost the same gas as minting one in standard ERC-721. Savings for user — 50–80% on gas when minting multiple tokens.
Minting Mechanics
Whitelist / allowlist — addresses from list mint before public. Implementation via Merkle Tree (not via mapping): tree root stored in contract (32 bytes), user provides Merkle proof at minting. Gas savings on deployment and storage — immense compared to mapping(address => bool) for thousands of addresses.
Signature-based allowlist — alternative to Merkle Tree. Backend signs permission for specific address via ECDSA (EIP-712), user provides signature at minting. More convenient for dynamic allowlist (can add addresses without updating Merkle root), but requires backend infrastructure.
Dutch Auction — price starts high and decreases every N minutes to floor price. Lets market find equilibrium price, reduces gas wars at start. Harder to implement — requires correct on-chain price calculation without off-chain data.
Metadata and Storage
tokenURI must return JSON with fields name, description, image, attributes. Critical question — where this JSON and images are stored.
IPFS + Pinata/NFT.Storage — decentralized storage, content-addressable links (ipfs://Qm...). If pinning stops — file is theoretically inaccessible, but can be recovered by any IPFS node with a copy. Standard for most collections.
Arweave — permanent storage, one-time payment for eternal storage. More reliable than IPFS for persistence. Used for valuable collections and PFP.
On-chain SVG — images generated directly in contract as SVG strings. Fully decentralized, can't be changed. Expensive on gas for deployment (if trait data stored on-chain), but perfect for simple geometric art projects.
Reveal mechanic: at deployment all tokenURI point to placeholder. After minting — reveal, contract updates baseURI to final IPFS path. Randomness for trait generation from Chainlink VRF (verifiably random) or blockhash (manipulable but cheap for non-high-value collections).
Stack and Process
Contract — Solidity 0.8.x, ERC-721A or OpenZeppelin ERC-721. Tests in Foundry: coverage >90%, test batch mint gas, test Merkle proof verification.
Generate metadata via script before deployment: take trait layers, generate combinatorics, check rarity distribution, upload to IPFS via Pinata API. Fix final IPFS CID before deployment.
| Stage | Content |
|---|---|
| Contract | ERC-721A + whitelist + public mint + withdraw |
| Metadata | JSON generation, upload to IPFS, CID in contract |
| Tests | Foundry unit + fuzz, gas report |
| Deployment | Sepolia testnet → Ethereum mainnet via Gnosis Safe |
| Verification | Etherscan + OpenSea collection verify |
Timeline Estimates
Basic collection (ERC-721, whitelist via Merkle, public mint, IPFS metadata) — 1–1.5 weeks including tests and deployment. With Dutch auction, custom reveal mechanic, Chainlink VRF — 2 weeks. Cost depends on minting complexity and metadata scope.







