NFT Project Architecture: Key Decisions
Leveraging 10+ years of blockchain development experience and 50+ successful NFT projects, we design scalable and future-proof architecture. Our architecture covers key decisions: ERC-721, ERC-1155, ERC-721A token standards, metadata storage on IPFS or Arweave, EIP-2981 royalties, and gas optimization for mint mechanics. We guarantee marketplace compatibility and proven gas efficiency.
Most NFT project problems surface not during development, but at scale or when hitting marketplaces. The collection is deployed, sold, and only then it emerges: metadata lives on a centralized server—when it goes down, traders see blank images; royalties don't work on Blur and LooksRare; the contract doesn't support batch operations and gas per transfer hits $5 on a congested network. We design architecture to prevent such surprises half a year down the line. Savings from gas optimization can exceed $10,000 for a 10k collection, justifying the architectural design investment.
Which token standard is best for your collection?
The classic ERC-721 is the standard for unique NFTs. Each tokenId is unique, each has its own owner. Widely supported by marketplaces. Weakness: _mint in a loop—every mint is a separate SSTORE operation (~20k gas). Minting 100 NFTs in one transaction costs 2M gas.
ERC-721A (Azuki) solves this: batch mint writes only the entry for the first tokenId; the rest are computed via ownerOf. Savings on batch mint: 60-80% gas. The trade-off: ownerOf and transferFrom cost more due to extra computations. For projects with active post-mint trading, weigh carefully.
ERC-1155 is a multi-token standard: one contract holds multiple IDs, each ID can have multiple copies (fungible or semi-fungible). Ideal for edition NFTs, gaming items, certificates. Marketplaces support ERC-1155, but UX can be worse than ERC-721 (some aggregators display editions incorrectly).
Gas calculation example for batch mint
Minting 10 ERC-721 tokens in a loop: ~200k gas. Minting 10 ERC-721A tokens: ~80k gas. Savings of 60% at 50 gwei gas price = $18 saved for 10 tokens. For a collection of 10,000 NFTs, the total saving exceeds $18,000.
| Standard |
Best For |
Gas per Mint |
Gas per Transfer |
| ERC-721 |
Unique PFP |
High |
Low |
| ERC-721A |
Batch mint PFP |
Very low |
Medium |
| ERC-1155 |
Edition, gaming |
Low |
Very low |
Upgradability: When and How to Use It
For most NFT collections, an immutable contract inspires more trust. A proxy (EIP-1967 transparent or UUPS) adds attack surface: storage slot collision during an upgrade can corrupt the _owners mapping. UUPS avoids selector clashes but still requires careful storage layout. Upgradability is justified for gaming NFTs with evolving mechanics or protocol NFTs within DeFi. In that case—UUPS with a timelock: any upgrade has a 48-72 hour delay.
How to ensure permanent metadata storage?
IPFS is decentralized, but a file exists only while pinned. Solution: a paid pinning service (Pinata, NFT.Storage, Filebase) plus multiple pinners. NFT.Storage stores data permanently through Filecoin deals—closer to real decentralization than plain IPFS pinning.
Arweave—pay-once-store-forever. One payment on upload, data stored permanently (~200 years per protocol estimates). Used by Metaplex and many serious ETH projects. For 10,000 NFT images, Arweave costs around $200-500—cheaper than years of paid pinning.
For fully on-chain NFTs (generative art, fully on-chain games), metadata and images live in the contract. tokenURI returns a base64-encoded JSON with a base64-encoded SVG inside. Expensive to deploy but absolutely permanent. Examples: Loot Project, Nouns DAO.
| Solution |
Cost for 10K NFTs |
Permanence |
Decentralization |
| IPFS + Pinata |
~$99/month |
No (as long as paid) |
Medium |
| NFT.Storage |
Free (with limits) |
Yes (Filecoin deals) |
High |
| Arweave |
$200–500 one-time |
Yes (200+ years) |
High |
| On-chain |
>$10,000 one-time |
Yes |
Full |
Implementing Royalties via EIP-2981 and Operator Filter
The EIP-2981 standard—royaltyInfo(uint256 tokenId, uint256 salePrice) returns (receiver, royaltyAmount). Supported by OpenSea, Rarible, Foundation. Not enforced—advisory. Implemented via ERC2981 from OpenZeppelin:
According to OpenZeppelin documentation, _setDefaultRoyalty sets a default royalty for all tokens, which can be overridden per token.
_setDefaultRoyalty(treasury, 500); // 5% = 500 basis points
For projects wanting enforced royalties: inherit from OperatorFilterer, register in OpenSea Operator Filter Registry. This approach blocks transfers through non-compliant marketplaces and implements an NFT operator filter. However, it restricts transferability and may spark community debate.
Selecting Mint Mechanics
Dutch Auction reduces gas wars and offers fair price discovery with a refund mechanism for overpayment.
Whitelist + Public phases use Merkle proofs for WL and per-wallet rate limits.
Lazy mint keeps NFTs off-chain until first purchase; collector pays mint gas. Reduces creator risk—no need to deploy unsold tokens.
Architectural Design Process
-
Analysis (1-2 days). Project type (PFP / gaming / art / membership), target marketplaces, royalty requirements, planned utility (staking, governance, content access).
-
Architecture document (1-2 days). Standard selection with justification, metadata storage scheme, mint mechanics, royalty approach, upgrade roadmap if needed.
-
Technical architecture audit. Check compatibility with target marketplaces (OpenSea, Blur, LooksRare), potential gas issues, attack surface.
-
Deliverable. Markdown document with architectural decisions, ER-diagram of contracts, storage layout, risk list. Development proceeds based on this document.
What's Included
- Architecture document (standard selection, metadata schema, mint logic)
- ER-diagram of contracts and storage layout
- Gas optimization analysis for batch mint and transfers
- Marketplace compatibility check
- Risk list and security recommendations
- Post-delivery consultation (up to 1 hour)
Timeline and Investment
Architectural design for an NFT project—3-5 days. Typical design fee: $3,000–$5,000, often recouped through gas savings exceeding $10,000 for large collections. Contact us to discuss your project and get an estimate. Get a consultation on NFT token standards and metadata storage.
Why does NFT marketplace development require a comprehensive approach?
We see that at first glance, an NFT contract looks simple: ERC-721, mint(), IPFS for metadata — that's it. In practice, it's this 'simplicity' that hides most problems — from bots buying out the entire mint in the first block to broken royalties on the secondary market. We often hear: Make a collection like others in a week — and a month later it turns out gas has tripled due to an unoptimized for loop, or OpenSea cannot see metadata after reveal. We know each of these pitfalls and build processes to avoid them.
Over 5 years of working with blockchains, we have implemented 40+ NFT projects, including marketplaces with dynamic attributes and cross-chain bridges. We have accumulated a library of proven templates — some of which we break down below.
Which standard to choose: ERC-721 or ERC-1155?
ERC-721 — each token is unique, one owner. Suitable for collections where each NFT has individual attributes and a direct owner → tokenId mapping.
ERC-1155 — multi-token standard: one contract holds both fungible and non-fungible tokens. It uses balanceOf(address, tokenId) instead of ownerOf(tokenId). A single transaction can transfer multiple different tokens via safeBatchTransferFrom. This saves gas on bulk operations — important for game items, tickets, edition collections. ERC-1155 is 2–3× more gas-efficient than ERC-721 for batch transfers.
| Criteria |
ERC-721 |
ERC-1155 |
| Token uniqueness |
Each token is unique |
One tokenId can have multiple copies |
| User balance |
Only ownerOf (one) |
balanceOf(address, tokenId) |
| Gas per transfer |
~25,000 gas |
~18,000 gas (batch even lower) |
| Batch operations |
No native support |
safeBatchTransferFrom |
| Ideal scenario |
Art collections, PFPs |
Games, tickets, editions |
Specific case: a game project with 50 types of items, each with a supply of 10,000. ERC-721 — 500,000 unique tokens, huge overhead on mappings. ERC-1155 — 50 tokenIds, balanceOf per player. Gas per transfer is 2–3 times lower, contract deployment is cheaper. For such tasks, we use OpenZeppelin ERC-1155 with custom modifications.
Metadata: on-chain vs IPFS vs centralized
The standard route is tokenURI() returning a link to a JSON with fields name, description, image, attributes. Three storage options:
- Centralized server — cheapest and most flexible. Risk: server goes down, company closes — NFT loses metadata. Not suitable for collections claiming long-term value.
- IPFS + Pinning — content-addressed storage, the link is bound to the content hash. Pinata or NFT.Storage provide pinning. Important: IPFS does not guarantee availability by itself — an active pinning service is needed. If it shuts down, data may disappear if no one keeps a copy.
- On-chain metadata — base64-encoded SVG or JSON directly in tokenURI. Maximum reliability, but expensive: for a collection of 10,000 tokens, gas costs may exceed $5,000. Suitable for generative art projects where visuals are generated from on-chain attributes (Nouns, Loot).
For most collections, we choose IPFS with Pinata for images + on-chain attributes for traits — a good balance. We validate files against a JSON Schema before upload; a typical mistake is unescaped quotes, causing marketplaces to display a blank screen.
Typical JSON metadata format
{
"name": "Token #1",
"description": "A unique NFT",
"image": "ipfs://QmHash/image.png",
"attributes": [{"trait_type": "Background", "value": "Red"}]
}
Dynamic NFT: metadata that changes
Dynamic NFT updates metadata in response to external events — match results, character levels, real-world data via Chainlink. Architecturally, it's a combination: the smart contract stores state → tokenURI() generates metadata from the state on-chain. Caching problem: OpenSea and other marketplaces aggressively cache. The standard invalidation mechanism is a MetadataUpdate(tokenId) event from ERC-4906. OpenSea listens to this event and clears the cache. Without it, updated metadata may not appear for weeks.
Chainlink Automation (formerly Keepers) for automatically updating state on the contract on a schedule or condition — a standard solution for dynamics.
How to protect mint from bots?
Allowlist via Merkle tree — standard. The list of addresses is hashed into a Merkle root, stored in the contract. During mint, the user provides a Merkle proof — the contract verifies without storing the full list. We use OpenZeppelin MerkleProof library.
Reveal mechanism — on mint, a placeholder is issued; real traits are revealed after the sale ends. Otherwise, bots can scan pending transactions and snipe rare traits via frontrunning. But reveal requires a commitment scheme — the random seed must be fixed before mint or use Chainlink VRF.
Chainlink VRF for fair randomization of traits. VRF request at mint → callback with verifiable random number → assign traits. This adds ~2 transactions and latency but guarantees fairness. Chainlink VRF v2.5.
Rate limiting — require(mintedPerWallet[msg.sender] < maxPerWallet). Does not protect against multi-wallets but raises attack cost. For premium projects, we often add proof-of-work directly in the contract (via EIP-2612 signatures).
Royalties: the real market state
ERC-2981 — on-chain royalty standard. The contract returns (recipient, amount) for any sale price via royaltyInfo(tokenId, salePrice). Marketplaces query this on each sale. Problem: adherence to royalties is voluntary for marketplaces. Blur launched with zero royalties, triggering a wave of other platforms. The situation has partially stabilized: OpenSea supports ERC-2981, Blur added optional ones. Royalty payments can represent 5–10% of secondary sale volume, so getting them right matters.
Attempts to enforce royalties on-chain by restricting transfers only to approved marketplaces (operator filtering) were proposed by OpenSea via OperatorFilterRegistry. This breaks composability — you cannot transfer an NFT through a custom contract. Most serious projects have abandoned this approach. For projects where royalties are critical, we build a custom marketplace within the ecosystem plus an incentive structure for users to trade there.
Lazy minting and gas-free mint
Gas-free mint via signature: the creator signs a voucher (tokenId, tokenURI, price, signature), the buyer provides the voucher in mint() — the contract verifies the signature via ECDSA.recover() and mints. Works on OpenSea via their Seaport protocol. Seaport is an optimized contract with minimal gas usage. Understanding its mechanics is important when integrating custom marketplace logic.
Stack for NFT projects
- Contracts: Solidity 0.8.x, OpenZeppelin ERC721Enumerable or ERC721A (Azuki) for gas-optimized batch mint, ERC1155 from OpenZeppelin
- VRF and automation: Chainlink VRF v2.5, Chainlink Automation
- Storage: Pinata (IPFS pinning), NFT.Storage, Arweave for permanent storage
- Marketplace: OpenSea Seaport protocol, custom integration
- Frontend: wagmi v2 + viem, RainbowKit for wallet connection, React + TypeScript
Development process
-
Mint mechanics design — allowlist, public sale, price curve (Dutch auction or fixed), limits per wallet
-
Contracts — with Foundry fuzz tests on mint limits, Merkle proof verification, royalty calculations
-
IPFS deployment — upload metadata and images before reveal, pin on at least two services
-
Reveal — if using Chainlink VRF, test on testnet mandatory: VRF subscription must be funded with LINK tokens
-
Marketplace integration — verify collection on OpenSea, configure royalties, test MetadataUpdate events
-
Deployment and monitoring — Tenderly for reentrancy detection, Etherscan API for contract verification, set up event alerts
Deliverables
- Source code of smart contracts (Solidity, Rust for Solana) with comments
- Test suite (Foundry/Hardhat) with ≥90% coverage
- Deployment documentation and integration instructions
- Access to pinning services (Pinata/Pinfluence)
- Metadata generation scripts (Python/JS)
- Support during marketplace verification
- 30 days of technical support after deployment
Timeline
| Task type |
Approximate timeline |
| Basic ERC-721 without reveal |
from 2 weeks |
| NFT collection with allowlist, reveal, VRF |
from 5 weeks |
| ERC-1155 with marketplace and royalties |
from 6 weeks |
| Dynamic NFT with external data |
from 8 weeks |
Cost is calculated individually after auditing your task. Send a brief with your project description — we will provide a transparent estimate within 3 business days. For regular clients, there is a flexible discount system on batch orders. If you need a gas-optimized contract, order a free gas analysis. Get a consultation on marketplace architecture — leave a request, and we will evaluate your project in three days.