Skip to main content

Non-Fungible Assets

Overview

The Non-Fungible Token (NFT) pallet extends the Asset pallet, allowing users to create NFT collections, issue unique tokens, and redeem (i.e. burn) existing non-fungible tokens. Documentation for the NFT crate can be found here.

NFTs on Polymesh are always managed as collections, each associated with a unique asset. Before you can register local metadata keys or create an NFT collection with local keys, you must first create the asset itself as described in the Asset Creation section. The asset must be of type NonFungible. Once the asset exists, you can register any required local metadata keys, and only then proceed to create the NFT collection. See Asset Creation for a step-by-step overview.

NFT Collection

All non-fungible tokens are linked to a unique NFT collection, which is tied to an Asset and user-defined metadata.

Collection Metadata Keys

Before creating an NFT collection, you must define metadata keys that specify the properties each NFT in the collection must have. Registering local metadata keys requires that the asset has already been created. These collection keys serve several purposes:

  1. Required Properties: Each NFT in the collection must provide values for all defined collection keys.
  2. On-chain Data: Values are stored directly on-chain for transparency and smart contract usage.
  3. Immutable Structure: Collection keys and their values cannot be modified after creation of the collection or individual NFT.

To set up collection keys:

  1. Register metadata keys after creating the asset but before creating the NFT collection, or identify appropriate standardized global metadata keys.
  2. All required collection keys must be registered before collection creation.
  3. The number of keys must not exceed MaxNumberOfCollectionKeys.
  4. Keys can be registered individually or in batches.

Creating an NFT Collection

To successfully create an NFT collection, the create_nft_collection function must be called, and the following conditions must be met:

  • If the asset already exists, it must be of type NonFungible, and the caller must have the appropriate permission for the asset. If the asset does not exist, create_nft_collection will also create one non-fungible asset using the values passed as nft_type.
  • This must be the first collection associated with the given AssetId (i.e. only one collection per Asset ID is allowed).
  • The number of metadata keys associated with the collection must be less than or equal to MaxNumberOfCollectionKeys.
  • All metadata keys must be registered before the collection is created. When using local metadata keys, you must call create_asset and register_asset_metadata_local_type before create_nft_collection.

Once the transaction succeeds, a unique ID is tied to the NFT collection, in addition to the Asset ID, which identifies the underlying asset details.

Issuing an NFT

After creating the collection, tokens can be issued with the issue_nft function. For an NFT to be issued successfully, the following conditions must hold:

  • An NFT collection associated with an AssetId must exist.
  • The caller must have the appropriate permission for the Asset.
  • The portfolio_kind of the caller must be valid.
  • All metadata keys specified in the NFT collection must have a value set when issuing the token. Issuing a token with metadata keys not defined in the collection will fail.

Once the transaction succeeds, a unique non-fungible token is linked to the specified portfolio.

Redeeming an NFT

A non-fungible token can be redeemed by calling the redeem_nft function. To successfully redeem an NFT, the following conditions must hold:

  • An NFT collection associated with AssetId must exist.
  • The caller must have the appropriate permission for the Asset.
  • The nft_id of the token must exist in the caller's portfolio.

Once the transaction succeeds, the non-fungible token will no longer exist on chain and be removed from the caller's portfolio.

NFTs and the Settlement Pallet

Polymesh's Settlement engine fully supports NFTs, and transfers of non-fungible tokens follow the same process as fungible assets. This means that all compliance rules defined for the underlying asset must be respected for a successful transfer of an NFT.

Metadata Specifications

NFTs support both collection-level and individual NFT metadata. Polymesh provides standardized approaches for handling metadata:

On-chain vs Off-chain Storage

Unlike other chains and standards (e.g., EIP-721), Polymesh encourages metadata to be stored directly on-chain for:

  • Greater transparency
  • Reduced reliance on external systems
  • Direct reference by smart contract business logic

However, off-chain storage options are also supported through standard URI specifications.

Standardized Global Metadata Keys

Polymesh defines the following global metadata keys to enable standardized processing by third-party dApps. Additional standardized keys can be added through the onchain governance process:

  1. Token URI: Points to off-chain JSON metadata
  2. Base Token URI: Collection-wide base URI for token metadata
  3. Image URI: Points to off-chain NFT image
  4. Base Image URI: Collection-wide base URI for NFT images

Token URI Specification

The Token URI of an NFT points to an off-chain JSON blob containing metadata. It can be specified through:

  • Individual NFT's tokenUri global metadata key
  • Collection's baseTokenUri global metadata key with optional {tokenId} substitution

Resolution process:

  1. Use individual tokenUri if set
  2. Otherwise, use baseTokenUri with {tokenId} substitution
  3. If baseTokenUri lacks {tokenId}, append /{tokenId}

The referenced JSON should follow this schema:

{
"title": "Token Metadata",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Identifies the asset this token represents"
},
"description": {
"type": "string",
"description": "Describes the asset this token represents"
},
"properties": {
"type": "object",
"description": "Arbitrary properties. Values may be strings, numbers, objects, or arrays."
}
}
}

URI Resolution Process

Both Token and Image URIs follow a consistent resolution process:

  1. Individual URI

    • Check token-specific URI first (tokenUri/imageUri)
    • Use if explicitly set for the individual NFT

    Example Individual URIs:

    tokenUri: 'https://metadata.polymesh.network/tokens/abcdefg123';
    imageUri: 'https://images.polymesh.network/tokens/abcdefg123.png';
  2. Collection Base URI

    • Fall back to collection's base URI (baseTokenUri/baseImageUri)
    • Substitute {tokenId} placeholder if present
    • Append /{tokenId} if no placeholder exists

    Example Base URIs:

    baseTokenUri: "https://metadata.polymesh.network/collection/{tokenId}"
    baseImageUri: "https://images.polymesh.network/collection/v1/{tokenId}.png"
    baseImageUri: "https://images.polymesh.network/collection/v1" # expects the token id to be appended
Notes
  • Collection metadata keys are immutable after collection creation
  • Values must be set for all the collection's metadata keys
  • Image and token URI resolution follows the same priority order
  • When using an image URI it is recommended to include the applicable file extension

Further Reading