For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 20+ (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