Inscription

Inscribe Data to Solana state.

Introduction

Overview

The Metaplex Inscription Program allows you to write data directly to Solana, using the blockchain as a method of data storage. The Inscription program also allows for this data storage to be optionally linked to an NFT. In this overview, we explain how this program works and how we can leverage its various features at a high level.

Getting Started

Find the language or library of your choice and get started with digital assets on Solana.

API reference

Looking for something specific? Have a peak at our API References and find your answer.

Introduction

NFT JSON data and Images have historically been stored on decentralized storage providers like Arweave or IPFS. The Inscription program introduces Solana as another option for NFT data storage, allowing you to write that data directly to the chain. The Metaplex Inscription program introduces the novel use case of all of an NFT's associated data now being stored on Solana. This enables many new use cases such as Solana programs with trait-based bids, dynamic images that are updated via programs, or even RPG game state on-chain.

There are two different kinds of Inscriptions:

  1. Inscriptions attached to NFT Mints - NFT data is written to the chain instead or in addition to off chain storage
  2. Inscriptions as storage providers - Write arbitrary data to the chain

Inscriptions attached to NFT Mints

Inscriptions can be used in addition to off chain storage like Arweave, where the metadata JSON and the media is stored, or can be completely replace those off chain storage using the Inscription Gateway.

In both cases the same process to create the inscription is used. When using the gateway the only difference is the URI used in the on-chain metadata. Read more on this in the Gateway section.

When storing the NFT Metadata on-chain three inscription accounts are used:

  1. inscriptionAccount which stores the JSON Metadata.
  2. inscriptionMetadata which stores the metadata of the inscription
  3. associatedInscriptionAccount which is storing the media / image.

The below script creates both of these Accounts for you and points the newly minted NFT to the Metaplex gateway. With this your NFT is completely on-chain.

Inscribe Data for new NFT using the Gateway

const umi = await createUmi()
umi.use(mplTokenMetadata())
umi.use(mplInscription())

// Create and mint the NFT to be inscribed.
const mint = generateSigner(umi)
const inscriptionAccount = await findMintInscriptionPda(umi, {
  mint: mint.publicKey,
})
await createV1(umi, {
  mint,
  name: 'My NFT',
  uri: `https://igw.metaplex.com/devnet/${inscriptionAccount[0]}`,
  sellerFeeBasisPoints: percentAmount(5.5),
  tokenStandard: TokenStandard.NonFungible,
}).sendAndConfirm(umi)

await mintV1(umi, {
  mint: mint.publicKey,
  tokenStandard: TokenStandard.NonFungible,
}).sendAndConfirm(umi)

const inscriptionMetadataAccount = await findInscriptionMetadataPda(umi, {
  inscriptionAccount: inscriptionAccount[0],
})

let builder = new TransactionBuilder()

// We initialize the Inscription and create the account where the JSON will be stored.
builder = builder.add(
  initializeFromMint(umi, {
    mintAccount: mint.publicKey,
  })
)

// And then write the JSON data for the NFT to the Inscription account.
builder = builder.add(
  writeData(umi, {
    inscriptionAccount: inscriptionAccount[0],
    inscriptionMetadataAccount,
    value: Buffer.from(
      '{"description": "A bread! But on-chain!", "external_url": "https://breadheads.io"}'
    ),
    associatedTag: null,
    offset: 0,
  })
)

// We then create the associated Inscription that will contain the image.
const associatedInscriptionAccount = findAssociatedInscriptionPda(umi, {
  associated_tag: 'image',
  inscriptionMetadataAccount,
})

builder = builder.add(
  initializeAssociatedInscription(umi, {
    inscriptionMetadataAccount,
    associatedInscriptionAccount,
    associationTag: 'image',
  })
)

await builder.sendAndConfirm(umi, { confirm: { commitment: 'finalized' } })

// Open the image file to fetch the raw bytes.
const imageBytes: Buffer = await fs.promises.readFile('bread.png')

// And write the image.
const chunkSize = 800
for (let i = 0; i < imageBytes.length; i += chunkSize) {
  const chunk = imageBytes.slice(i, i + chunkSize)
  await writeData(umi, {
    inscriptionAccount: associatedInscriptionAccount,
    inscriptionMetadataAccount,
    value: chunk,
    associatedTag: 'image',
    offset: i,
  }).sendAndConfirm(umi)
}

Inscriptions as a Storage Provider

In addition to the usage with NFT Mints Inscriptions can also be used to store arbitrary data up to 10 MB on-chain. An unlimited number of Associated Inscriptions can be created.

This can be useful when writing an on-chain game that needs to store JSON data, storing text on-chain, or storing any program-related data that's not an NFT.

The following example shows how to write NFT JSON data to an Inscription in three different transactions to avoid the 1280 byte transaction size limit.

Find the rank of a specific NFT inscription

const inscriptionAccount = generateSigner(umi)

const inscriptionMetadataAccount = await findInscriptionMetadataPda(umi, {
  inscriptionAccount: inscriptionAccount.publicKey,
})

let builder = new TransactionBuilder()

builder = builder.add(
  initialize(umi, {
    inscriptionAccount,
  })
)

builder = builder.add(
  writeData(umi, {
    inscriptionAccount: inscriptionAccount.publicKey,
    inscriptionMetadataAccount,
    value: Buffer.from('{"description": "A bread! But on-chain!"'),
    associatedTag: null,
    offset: 0,
  })
)

builder = builder.add(
  writeData(umi, {
    inscriptionAccount: inscriptionAccount.publicKey,
    inscriptionMetadataAccount,
    value: Buffer.from(', "external_url":'),
    associatedTag: null,
    offset: '{"description": "A bread! But on-chain!"'.length,
  })
)

builder = builder.add(
  writeData(umi, {
    inscriptionAccount: inscriptionAccount.publicKey,
    inscriptionMetadataAccount,
    value: Buffer.from(' "https://breadheads.io"}'),
    associatedTag: null,
    offset: '{"description": "A bread! But on-chain!", "external_url":'.length,
  })
)

await builder.sendAndConfirm(umi, { confirm: { commitment: 'finalized' } })

Associated Inscription Accounts

The Metaplex JSON standards include the option of linking associated files to a token via the files properties in the JSON schemas. The Inscription program introduces a new method of associating additional data using the power of PDAs! A PDA is derived from the Inscription and an Association Tag, resulting in a programmatic way to derive additional inscribed data, rather that requiring expensive JSON deserialization and parsing.

Inscription Gateway

Together with the Inscription Gateway you can use the normal Token Metadata Standard and just point the URI to the gateway which again reads your data directly from chain without all tools like wallets and explorers reading the data have to read it any differently than NFTs are read usually.

You can either use the gateway that is hosted by Metaplex using the following URL structure: https://igw.metaplex.com/<network>/<account>, e.g. https://igw.metaplex.com/devnet/Fgf4Wn3wjVcLWp5XnMQ4t4Gpaaq2iRbc2cmtXjrQd5hF or host the gateway yourself with a custom URL.

Inscription Rank

The Inscription Rank is the unique number of each inscription. This number represents a sequential, global ranking of all Metaplex Inscriptions in existence based on the total Inscription count at the time of creation. Inscription Rank is managed through a parallelized counter that is explained further in Inscription Sharding.

To find the inscriptionRank of your Inscription you need to fetch the inscriptionMetadata Account and read the inscriptionRank bigint:

Find the rank of a specific NFT inscription

const inscriptionAccount = await findMintInscriptionPda(umi, {
  mint: mint.publicKey,
})
const inscriptionMetadataAccount = await findInscriptionMetadataPda(umi, {
  inscriptionAccount,
})

const { inscriptionRank } = await fetchInscriptionMetadata(
  umi,
  inscriptionMetadataAccount
)

When creating your inscriptions you should always use a random shard to avoid write locks. You can just calculate the random number like this:

Find random shard

const randomShard = Math.floor(Math.random() * 32)

The total amount of Metaplex Inscriptions on Solana can be calculated like this:

Fetch total Inscription amount

import {
  fetchAllInscriptionShard,
  findInscriptionShardPda,
} from '@metaplex-foundation/mpl-inscription'

const shardKeys = []
for (let shardNumber = 0; shardNumber < 32; shardNumber += 1) {
  k.push(findInscriptionShardPda(umi, { shardNumber }))
}

const shards = await fetchAllInscriptionShard(umi, shardKeys)
let numInscriptions = 0
shards.forEach((shard) => {
  const rank = 32 * Number(shard.count) + shard.shardNumber
  numInscriptions = Math.max(numInscriptions, rank)
})

And a lot more

Whilst this provides a good overview of the Inscription program and what it has to offer, there’s still a lot more that can be done with it.

The other pages of this documentation aim to document it further and explain significant features in their own individual pages.