Managing NFT Metadata with IPFS

In this guide, you will rely on an IPFS workflow for ERC721 collections on Somnia.

This guide will walk you through how - You will prepare and upload artwork to IPFS via Pinata. - Generate clean, wallet/marketplace-friendly metadata JSON - Upload the metadata to IPFS via Pinata Cloud - Deploy a Solidity contract that stores the metadata URIs onchain (for each token) - Mint your NFTs.

All metadata resides on IPFS, while only the URIs (pointers) are stored onchain.

Please note that this guide provides a Smart Contract option for fully onchain metadata.

Concepts You Should Know

  • IPFS (InterPlanetary File System) is a “Content Addressed Storage Network”. Files are addressed by their CID (content identifier). If any bit changes, the CID changes, and it is great for integrity and verifiability.

  • Pinata: Pinata pins your files to IPFS and keeps them available. You’ll get CIDs (content identifiers) that never change for the same content.

  • TokenURI: an ERC721 method that returns a URL (often an ipfs:// URI) pointing to a JSON file describing the NFT token (name, description, image, attributes, etc.).

  • Per token URI vs baseURI:

    • Per token URI (this guide): store the full JSON URI onchain for each token with ERC721URIStorage. Easiest and most flexible.

    • baseURI pattern: compute tokenURI = baseURI + tokenId (no per token storage). Cheaper for large drops, but requires sequential naming.

Prerequisites

  • MetaMask is configured for a Somnia RPC and has sufficient funds for gas.

  • Node.js ≥ 18 (only if you’ll run the helper scripts below).

  • Pinata account and a JWT (recommended): Get your JWT on Pinata

    • Pinata Dashboard → API Keys → New Key → choose Admin/Scoped as needed → copy JWT.

Prepare Images

If your art varies wildly in size or format, normalize once for a consistent user experience. Create an assets directory for your images. Run the node script below, targeting the assets directory to give uniformity to your images.

Install the dependencies:

Run the script:

  • images/ contain consistent dimensions and formats (e.g., 1024×1024 PNG).

  • metadata/ contain one JSON per token ID, matching the filename (e.g., 7.json for tokenId 7).

Upload Images to IPFS via Pinata

Install Pinata SDK:

Initialise Pinata using your JWT credentials:

Create a .env file in your project root:

Upload the images directory and get the root CID:

Run the following command to upload the images:

Copy the CID into .env as IMAGES_CID=....

Generate Metadata JSON

Each *.json should follow the de facto standards:

Install fast-glob library:

Run the script below to generate a file per image:

To create the metadata files, run the command:

Upload Metadata to IPFS

Upload the metadata folder to generate the METADATA_CID

Run the command:

Your tokenURI format is now: ipfs://<METADATA_CID>/<tokenId>.json Make sure the script uploads assets/metadata and records the printed Metadata CID; you’ll mint with: ipfs://<METADATA_CID>/<tokenId>.json

NFT Smart Contract

We’ll use ERC721URIStorage Smart Contract and mint with full URIs. Paste this into Remix as NFTTest.sol

NFTTest.sol

Contract Walkthrough

Imports

  • ERC721 is the core NFT logic (ownership, transfers, approvals).

  • ERC721URIStorage adds per-token storage for URIs via _setTokenURI.

  • Ownable simple admin model; exposes onlyOwner for minting control.

ERC721URIStorage is the most straightforward way to store a token’s exact URI. For large drops, consider using a baseURI pattern to save storage gas; otherwise, this is perfect for flexible, explicit URIs.

State (_nextTokenId) is the sequential ID counter starting at 0. First mint → tokenId = 0, then 1, 2, … etc

Constructor

Initializes collection name/symbol and sets the owner to initialOwner (the only account allowed to mint).

_baseURI()

Returns https://ipfs.io. This is only used if you mint with relative paths (e.g., /ipfs/CID/7.json). If you mint with absolute ipfs://… URIs (recommended), this value is ignored.

safeMint(to, uri):

Owner-only mint. Creates a new token ID, mints safely (checking receiver contracts implement IERC721Receiver), and stores the exact uri string for that token via _setTokenURI.

Compile and Deploy using Remix. Follow this guide

Copy the deployed contract address.

How To Mint NFTs (Per Token URIs)

n Remix (Deployed Contracts):

  • Call safeMint(to, uri) where:

    • to is the recipient address (can be yours).

    • uri is ipfs://<METADATA_CID>/<id>.json (e.g., ipfs://bafy.../0.json).

Then call tokenURI(0) to confirm the stored URI.

If you prefer scripts for multiple mints, you can write a script to Batch Mint using Hardhat, for example:

Validation

To validate everything end to end, first do quick gateway smoke tests by opening https://ipfs.io/ipfs/<IMAGES_CID>/0.png and https://ipfs.io/ipfs/<METADATA_CID>/0.json in a browser to confirm the image and JSON are reachable. Next, call tokenURI(0) on your contract and verify it returns ipfs://<METADATA_CID>/0.json. Finally, check in a wallet or marketplace UI that the NFT renders correctly using the ipfs:// image URI embedded in the JSON.

MetadataOptional Fully Onchain Metadata

Smart Contract

If your art is small (SVG) or you only need text metadata, you can encode metadata JSON on-chain:

Trade-offs

  • No external dependencies; URIs are immutable, always available.

  • Higher gas (long strings), limited to compact media (SVG/text). For large images, prefer IPFS.

Last updated