Skip to main content
LIVE
BTC $—| ETH $—| BNB $—| SOL $—| XRP $— · · · BITAIGEN · · · | | | | · · · BITAIGEN · · ·

Step‑by‑Step Guide to Mint NFTs on Flow with IPFS

Bitaigen Research Bitaigen Research 4 min read

Learn to create, deploy, and mint NFT contracts on Flow, then store assets on IPFS. This guide covers tool setup, contract coding, deployment, and minting.

The Bitaigen editorial team believes that this article systematically outlines the complete steps for writing, deploying, and minting NFTs within the Flow and IPFS environments. From tool installation to Cadence contract implementation, each step is explained in detail to help developers get started quickly. The upcoming hands‑on examples are especially promising, so a careful read is recommended.
Step‑by‑Step Guide to Mint NFTs on Flow with IPFS flowchart

Tutorial for Creating NFT Contracts and Minting Tokens with Flow and IPFS

When Flow is used together with IPFS, you can write a Cadence contract, deploy it to the emulator or a testnet, upload assets to Pinata to obtain a CID, and then mint an NFT in a transaction while binding its metadata to the token. This yields a full end‑to‑end creation and minting workflow.

Environment Setup

  1. Install the Flow CLI
  • macOS

```bash

brew install flow-cli

```

  • Linux

```bash

sh -ci "$(curl -fsSL https://storage.googleapis.com/flow-cli/install.sh)"

```

  • Windows

```powershell

iex "& { $(irm 'https://storage.googleapis.com/flow-cli/install.ps1') }"

```

  1. Prepare IPFS Asset Storage
  • Register a Pinata free account and obtain an API Key. The second part of this series will use the Pinata API; this article only covers manual uploads through the Pinata website.
  1. Install Node.js and a Code Editor
  • We recommend Visual Studio Code together with the Cadence syntax extension, which provides proper highlighting for smart‑contract code.
  1. Create a Project Directory

```bash

mkdir pinata-party

cd pinata-party

flow project init

```

After the initialization, a `flow.json` file is generated; it will be edited later.

  1. Organize the Code Structure

```

pinata-party/

├─ cadence/

│   └─ contracts/

│       └─ PinataPartyContract.cdc

├─ transactions/

└─ scripts/

```

All Flow‑related source files belong in `cadence/contracts`. Transaction scripts and read‑only query scripts will be placed in `transactions` and `scripts`, respectively.

Configuring `flow.json` for the Emulator

Add the contract path to `flow.json`:

```json

"contracts": {

"PinataPartyContract": "./cadence/contracts/PinataPartyContract.cdc"

},

"deployments": {

"emulator": {

"emulator-account": ["PinataPartyContract"]

}

}

```

This configuration tells the Flow CLI to deploy `PinataPartyContract` to the local emulator.

Writing the NFT Contract

Core Contract Structure

```cadence

pub contract PinataPartyContract {

// NFT resource definition

pub resource NFT {

pub let id: UInt64

init(initID: UInt64) {

self.id = initID

}

}

// Interface for receiving NFTs

pub resource interface NFTReceiver {

pub fun deposit(token: @NFT, metadata: {String: String})

pub fun getIDs(): [UInt64]

pub fun idExists(id: UInt64): Bool

pub fun getMetadata(id: UInt64): {String: String}

}

// Implementation of an NFT Collection

pub resource Collection: NFTReceiver {

// Storage for NFTs

pub var ownedNFT: @{UInt64: NFT}

// Mapping for corresponding metadata

pub var metadataObjs: {UInt64: {String: String}}

init() {

self.ownedNFT <- {}

self.metadataObjs = {}

}

// Withdraw an NFT

pub fun withdraw(withdrawID: UInt64): @NFT {

let token <- self.ownedNFT.remove(key: withdrawID)!

return <-token

}

// Deposit an NFT and bind metadata

pub fun deposit(token: @NFT, metadata: {String: String}) {

self.ownedNFT[token.id] <-! token

self.metadataObjs[token.id] = metadata

}

pub fun idExists(id: UInt64): Bool {

return self.ownedNFT[id] != nil

}

pub fun getIDs(): [UInt64] {

return self.ownedNFT.keys

}

pub fun getMetadata(id: UInt64): {String: String} {

return self.metadataObjs[id]!

}

destroy() {

destroy self.ownedNFT

}

}

// Factory function that creates an empty Collection

pub fun createEmptyCollection(): @Collection {

return <-create Collection()

}

// Minter resource

pub resource NFTMinter {

pub var idCount: UInt64

init() {

self.idCount = 1

}

pub fun mintNFT(): @NFT {

let newNFT <- create NFT(initID: self.idCount)

self.idCount = self.idCount + 1

return <-newNFT

}

}

// Contract initializer: deploy the Collection, expose the interface, store the minter

init() {

// Create and store an empty Collection for the contract deployer

self.account.save(<-self.createEmptyCollection(), to: /storage/NFTCollection)

// Publish the Collection as an NFTReceiver capability

self.account.link<&Collection{NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection)

// Store the NFTMinter resource; only the contract creator can access it

self.account.save(<-create NFTMinter(), to: /storage/NFTMinter)

}

}

```

Key Points Explained

  • The NFT resource contains only a unique identifier `id`.
  • The NFTReceiver interface defines four externally callable methods: `deposit`, `getIDs`, `idExists`, and `getMetadata`.
  • The Collection resource implements this interface and additionally maintains `metadataObjs` to store each NFT’s metadata.
  • NFTMinter handles the incremental `idCount` and creates new NFTs.
  • The `init()` function automatically creates an empty Collection, publishes the capability, and stores the minter resource when the contract is deployed.

Deploying the Contract

  1. Deploy via the Flow Playground or the local emulator.
  2. Using the local emulator, run:

```bash

flow project start-emulator   # Launch the local emulator

flow project deploy           # Deploy the contract

```

A successful deployment prints a log similar to:

```

Deploying 1 contracts for accounts: emulator-account.PinataPartyContract -> 0xf8d6e0586b0a20c7

```

Minting an NFT (Transaction Script)

Create the Transaction File

Add a new file `MintPinataParty.cdc` inside the `transactions` folder with the following content:

```cadence

import PinataPartyContract from 0xf8d6e0586b0a20c7

transaction {

// References to the receiver and the minter

let receiverRef: &{PinataPartyContract.NFTReceiver}

let minterRef: &PinataPartyContract.NFTMinter

prepare(acct: AuthAccount) {

// Obtain the public NFTReceiver capability

self.receiverRef = acct.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver)

.borrow()

?? panic("Could not borrow receiver reference")

// Borrow the private NFTMinter resource

self.minterRef = acct.borrow<&PinataPartyContract.NFTMinter>(from: /storage/NFTMinter)

?? panic("Could not borrow minter reference")

}

execute {

// Example metadata (replace the IPFS CID with your own)

let metadata: {String: String} = {

"name": "The Big Swing",

"swing_velocity": "29",

"swing_angle": "45",

"rating": "5",

"uri": "ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"

}

// Mint the NFT and deposit it into the Collection

let newNFT <- self.minterRef.mintNFT()

self.receiverRef.deposit(token: <-newNFT, metadata: metadata)

log("NFT minted and deposited")

}

}

```

Explanation

  • Replace the address in the `import` line with the actual address where the contract was deployed.
  • The `uri` field in `metadata` uses the `ipfs://` scheme and should point to the CID you obtained after uploading the asset to Pinata.
  • The `deposit` call writes both the NFT and its metadata into the Collection.

Generate an Account Key and Update `flow.json`

```bash

flow keys generate

```

Copy the returned `privateKey` and `publicKey` into `flow.json` as shown:

```json

"accounts": {

"emulator-account": {

"address": "0xf8d6e0586b0a20c7",

"privateKey": "YOUR_PRIVATE_KEY",

"chain": "flow-emulator",

"sigAlgorithm": "ECDSA_P256",

"hashAlgorithm": "SHA3_256"

}

}

```

Security Note: Never commit private keys to a public repository. It is advisable to add `flow.json` to `.gitignore`.

Send the Transaction

```bash

flow transactions send --code ./transactions/MintPinataParty.cdc --signer emulator-account

```

If the command succeeds, a transaction ID is returned, confirming that the NFT has been written to the account’s Collection.

Querying NFT Metadata (Read‑Only Script)

Create a script called `CheckTokenMetadata.cdc` inside the `scripts` folder:

```cadence

import PinataPartyContract from 0xf8d6e0586b0a20c7

pub fun main(): {String: String} {

let nftOwner = getAccount(0xf8d6e0586b0a20c7)

let capability = nftOwner.getCapability<&{PinataPartyContract.NFTReceiver}>(/public/NFTReceiver)

let receiverRef = capability.borrow()

?? panic("Could not borrow the receiver reference")

return receiverRef.getMetadata(id: 1)

}

```

Execute the script:

```bash

flow scripts execute ./scripts/CheckTokenMetadata.cdc

```

The expected output resembles:

```

{"name":"The Big Swing","swing_velocity":"29","swing_angle":"45","rating":"5","uri":"ipfs://QmRZdc3mAMXpv6Akz9Ekp1y4vDSjazTx2dCQRkxVy1yUj6"}

```

At this point, the complete workflow—creating an NFT on Flow, storing its associated media on IPFS, minting the token, and retrieving its metadata—has been fully demonstrated.

---

Summary

  • Write an NFT contract that complies with Flow standards using Cadence.
  • Upload media files to IPFS through Pinata to obtain a CID.
  • Insert the CID into the `uri` field of the metadata within a transaction script, thereby linking the on‑chain NFT to off‑chain assets.
  • This tutorial covers environment setup, contract development, deployment, minting, and querying—four essential stages that lay the groundwork for building front‑end displays or secondary‑market integrations.
Tutorial for Creating NFT Contracts and Minting Tokens with Flow and IPFS

---

*This translation was sponsored by Cell Network. Original article link: https://medium.com/pinata/how-to-create-nfts-like-nba-top-shot-with-flow-and-ipfs-701296944bf*

Related Reading

💡 Register on Binance with referral code B2345 for the maximum trading fee discount. See Binance complete guide.

Sign Up on Binance Now

The world's largest crypto exchange. Use our exclusive code to unlock the maximum trading fee discount.

  • 0.075% spot fees (industry low)
  • 350+ cryptocurrencies · 24/7 trading
  • $1B+ SAFU user protection fund
Referral Code B2345

⚠️ Crypto investing carries risk. We have an affiliate partnership with Binance.

📖 View full Binance guide →
Sign up on Binance – Maximum Fee Discount邀请码 B2345 · Spot fee from 0.075%
Bitaigen Research
About the Author
Bitaigen Research

Bitaigen's editorial team covers blockchain news, market analysis and exchange tutorials.

Join our Telegram Discuss this article
Telegram →

Subscribe to Bitaigen

Weekly crypto news, Bitcoin price analysis delivered to your inbox

🔒 We respect your privacy. No spam, ever.

⚠️ Risk disclaimer: Crypto prices are highly volatile. This article is not investment advice. Invest responsibly at your own risk.