NFT Metadata Development (On-Chain/Off-Chain)

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
NFT Metadata Development (On-Chain/Off-Chain)
Simple
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Development of NFT Metadata (On-Chain/Off-Chain)

tokenURI() returns a string — URL or base64-encoded JSON. Behind this simplicity lies an architectural decision that will determine the collection's fate for years. Metadata on centralized IPFS gateway is not "decentralized metadata", it's a link to Pinata server, which can disappear. NFT with metadata on contract (on-chain) will outlive any hosting.

On-Chain vs. Off-Chain: Real Trade-Offs

Fully On-Chain

Metadata stored right in smart contract. tokenURI() generates JSON and SVG at runtime via string concatenation:

function tokenURI(uint256 tokenId) public view override returns (string memory) {
    string memory json = Base64.encode(bytes(string(abi.encodePacked(
        '{"name":"Token #', Strings.toString(tokenId),
        '","description":"On-chain NFT","image":"data:image/svg+xml;base64,',
        Base64.encode(bytes(_generateSVG(tokenId))),
        '"}'
    ))));
    return string(abi.encodePacked("data:application/json;base64,", json));
}

Advantage: complete permanence, no dependency on external services. Disadvantage: gas on deploy grows with SVG size. For simple generative collection (Loot, Nouns-style) this works. For photos — no.

Storing attributes in storage: mapping tokenId → struct with trait values. Each attribute — uint8 or bytes32 for slot economy. uint8 attributes packed 32 per storage slot.

IPFS Off-Chain

Standard approach for most collections. Metadata uploaded to IPFS, tokenURI() returns ipfs://CID/tokenId.json. Critical requirement: don't use HTTP gateway in URI.

Correct: ipfs://QmHash/1.json Incorrect: https://ipfs.io/ipfs/QmHash/1.json

The second is a link to specific HTTP server. It can disappear. The first is content address, works with any IPFS gateway.

For pinning — Pinata + Web3.Storage as backup. For most important collections — Filecoin via NFT.Storage for long-term storage with cryptographic guarantee.

Reveal Mechanism

Pre-reveal: all tokens show placeholder metadata. Post-reveal: real metadata revealed. Naive implementation — owner just changes baseURI. This is centralized and trust-requiring.

Commit-reveal scheme on VRF: before mint owner commits seed hash, after mint is complete — publishes seed and calls Chainlink VRF for random offset. Metadata shuffled deterministically via (tokenId + offset) % totalSupply. Nobody can know beforehand which traits go to which token.

function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override {
    revealOffset = randomWords[0] % maxSupply;
    revealed = true;
}

function tokenURI(uint256 tokenId) public view override returns (string memory) {
    require(revealed, "Not revealed yet");
    uint256 metadataId = (tokenId + revealOffset) % maxSupply;
    return string(abi.encodePacked(baseURI, metadataId.toString(), ".json"));
}

JSON Metadata Structure

OpenSea ERC-721 metadata standard:

{
  "name": "Token #1",
  "description": "Description text",
  "image": "ipfs://CID/1.png",
  "external_url": "https://project.xyz/token/1",
  "attributes": [
    {"trait_type": "Background", "value": "Blue"},
    {"trait_type": "Rarity", "value": "Legendary", "display_type": "boost_percentage", "max_value": 100}
  ]
}

display_type controls OpenSea display. Numeric attributes: "number" (just number), "boost_percentage" (progress bar), "boost_number" (modifier), "date" (unix timestamp → date).

For ERC-1155 structure is similar, but tokenURI takes uint256 id and can use {id} placeholder in URI.

Tools and Process

Generating metadata for large collection — off-chain TypeScript script: loads layers, generates combinations with rarity weights, creates JSON and images, batch uploads to IPFS via Pinata API. Then IPFS CID is fixed in contract.

For on-chain SVG — TypeScript script generates Solidity libraries with string constants for each trait. Contract size checked: 24KB limit.

Timeline Reference

IPFS off-chain metadata with reveal mechanism — 2-3 days. On-chain SVG generation for generative collection — 3-5 days depending on art complexity.