Skip to main content

Mint an NFT

Programmatically mint an NFT on ZORA#

To create new ZORA NFT using the ZDK, you must call the mint function. The mint function on a Zora instance expects two parameters: MediaData and BidShares


const metadata = {  version: 'zora-20210604', // The version of metadata you are generating  name: someName,  description: someDescription,  mimeType: someMimeType, // Media type: audio, image, etc etc  image: "", // IPFS url for a preview image of the media on opensea}

To construct the metadata for a zNFT, use the generateMetadata function defined in metadata.ts. For more info visit the Generate Metadata section.


  • tokenURI: A pointer to where a zNFT's content is hosted
  • metadataURI: A pointer to where a zNFT's metadata is hosted

ZORA strongly recommends hosting both the content and metadata on a decentralized storage provider such as IPFS or arweave.


  • contentHash: A sha256 hash of the content the zNFT's represents
  • metadataHash: A sha256 hash of the zNFT's metadata

It is imperative that this hash is correct, because once it is written to the blockchain it cannot be changed. To generate this hash use any of the sha256 utils defined in the ZDK utils.


import { constructMediaData, sha256FromBuffer, generateMetadata } from '@zoralabs/zdk'
const metadataJSON = generateMetadata('zora-20210604', {  description: '',  mimeType: 'text/plain',  name: '',  // to make token work with opensea previews:  // include {image: "preview_image_ipfs"}  //  and {animation: "preview_video_audio_ipfs"} if there is a video or audio associated  // to show both an audio player and image,   //  include the animation as audio and image as the poster image  version: 'zora-20210604',})
const contentHash = sha256FromBuffer(Buffer.from('Ours Truly,'))const metadataHash = sha256FromBuffer(Buffer.from(metadataJSON))const mediaData = constructMediaData(  '',  '',  contentHash,  metadataHash)


The BidShares type is composed of three fields:

type DecimalValue = { value: BigNumber }
type BidShares = {  owner: DecimalValue  prevOwner: DecimalValue  creator: DecimalValue}

Each field represents the share that each stakeholder of a zNFT has on the next accepted bid. At the time of mint, the indivduals bid shares (creator, owner, prevOwner) must sum to 100.

  • owner: The equity (%) the current owner gets from the next accepted bid
  • prevOwner: The equity (%) the previous owner gets from the next accepted bid
  • creator: The immutable, perpetual equity (%) the creator gets from each accepted bid


The Zora Media Contract allows for 18 decimals of precision. To simplify precision, we created the constructBidShares method with accepts JS numbers and converts them to ethers BigDecimal types rounded to the fourth decimal.

import { constructBidShares } from '@zoralabs/zdk'
const bidShares = constructBidShares(  10, // creator share  90, // owner share  0 // prevOwner share)

Complete Minting Code#

import { constructMediaData, sha256FromBuffer, generateMetadata, isMediaDataVerified, Zora } from '@zoralabs/zdk'
async function uploadToDecentralizedStorage(data: Buffer) {  // function that uploads buffer to decentralized storage  // and returns url of uploaded file from a gateway.  return '';}
async function mintZNFT({  zora: typeof Zora,  content: Buffer,  mimeType: string,  name: string,  description: string,  previewImageUrl?: string  animationUrl?: string}) {
  const metadataJSON = generateMetadata('zora-20210604', {    description,    mimeType: 'text/plain',    image: previewImageUrl,    animation_url: animationUrl,    name,    version: 'zora-20210604',  })
  const contentURI = await uploadToDecentralizedStorage(content);  const metadataURI = await uploadToDecentralizedStorage(Buffer.from(metadataJSON));
  const contentHash = sha256FromBuffer(content);  const metadataHash = sha256FromBuffer(Buffer.from(metadataJSON));  const mediaData = constructMediaData(    contentURI,    metadataURI,    contentHash,    metadataHash  );
  // Verifies hashes of content to ensure the hashes match  const verified = await isMediaDataVerified(mediaData);  if (!verified){    throw new Error("MediaData not valid, do not mint");  }
  // BidShares should sum up to 100%  const bidShares = constructBidShares(    10, // creator share percentage    90, // owner share percentage    0 // prevOwner share percentage  );
  const tx = await, bidShares);  return new Promise((resolve) => {    // This listens for the nft transfer event      "Transfer",      (from: string, to: string, tokenId: BigNumber) => {      if (        from === "0x0000000000000000000000000000000000000000" &&        to === tx.from.address      ) {        promise.resolve(tokenId);      }    });  });}