Unique Schema
Overview
Unique is an NFT-oriented blockchain built on Substrate, featuring both Substrate and EVM (Ethereum Virtual Machine) capabilities. Unique emphasizes its NFT functionality, with collections and NFTs as first-class entities within the blockchain. The latest metadata schema, Unique V2, offers a modern and enhanced way to handle NFT metadata, drawing inspiration from industry standards.
On-Chain Metadata Storage
Unlike other blockchains that rely on off-chain storage for metadata, Unique stores metadata directly on-chain using a key-value storage system called properties.
So, the Collection and NFT metadata are stored directly on the blockchain, not externally on IPFS or elsewhere. Storing metadata on-chain enhances decentralization, which is highly valued in the crypto community. On-chain storage ensures data immutability and greater security, aligning with the ethos of blockchain technology.
Unique Metadata Schema V2
Unique Schema V2 is an evolution from previous binary and compact encoding versions (v0 and v1), adopting a human-readable format similar to the widely accepted Ethereum NFT metadata standards. This new schema is completely compatible with the OpenSea metadata format and includes several enhancements.
Enhancements in Unique V2
- Improved Media Handling: The new schema properly supports media files, eliminating the misuse of fields like
animation_url
. This includes fields for various media types such as images, videos, and audio. - Customizable NFTs: Unique V2 introduces customization capabilities, where one NFT can own and modify another NFT. For example, a character NFT can wear a hat NFT, with detailed instructions on how to overlay images and other content types.
The Unique Metadata Schema V2 offers a robust and flexible way to handle NFT metadata, aligning with industry standards while providing significant enhancements. This schema ensures compatibility and ease of use, making it a powerful tool for developers and users in the NFT space.
NFTs created previously in schemaVersion v0 and v1 are returned in the new format.
Examples how to create collections NFTs in Schema V2
NFT Token Schema V2 Detailed Description
First of all, Schema V2 NFT Example
For using the Unique Schema V2, you may find this official library useful: unique-nft/schemas
Next, let's take a detailed look at all the fields of the schema.
Tip: All fields are optional
Of course, it doesn't make sense to create NFT without, for example, image
field but the schema doesn't require any field to present.
This rule applies only to the top-level fields. Some nested fields may be required. For example, when providing the image details, specifying the image length implies that you also need to provide the image width.
Common fields
name
string
- NFT token name.
Example:{image: "Substrapunk #1234"}
description
string
- NFT token description.
Example:{description: "A unique Substrapunk character with a rare hat."}
image
string
- URL to the main image associated with the NFT.
Example:{image: "https://example.com/artwork.png"}
image_details: IV2ImageDetails - Additional details about the main image.
Example:
{
image_details: {
name: "Artwork",
type: "image",
format: "PNG",
bytes: 1048576,
width: 1000,
height: 1000,
sha256: "0x1234...",
}
}
- attributes: Array of IV2Attribute - Array of attributes associated with the NFT.
Example:
{
attributes: [
{
trait_type: "Color",
value: "Red"
},
{
trait_type: "Size",
value: "Large"
}
]
}
- animation_url:
string
- URL to a multi-media attachment for the item.
Example:{animation_url: "https://example.com/animation.gif"}
Since it's widely used for attaching files of any format (audio, video, etc), we recommend using the media
field instead of animation_url
.
animation_details: IV2ImageDetails - Additional details about the animation from the
animation_url
field. Example is the same as in theimage_details
field.youtube_url:
string
- URL to a YouTube video associated with the NFT.
Example:{youtube_url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}
created_by:
string
- Address of the creator of the NFT.
Example:{created_by: "0x1234..."}
external_url:
string
- URL to an external resource providing more information about the NFT.
Example:{external_url: "https://example.com/nft"}
background_color:
string
- Background color of the NFT.
Example:{background_color: "#00BFFF"}
locale:
string
- Locale of the NFT.
Example:{locale: "en"}
Media field
- media: Dictionary of IV2Media - Media elements associated with the NFT.
The media
property is a dictionary that allows the storage of multiple media elements associated with the NFT. Each media element is represented by a key-value pair, where the key is a string identifier and the value is an instance of the IV2Media interface. This type of storage allows extremely simple control over complex media stacks within the collection. When you need to mint NFTs automatically, you can use any key for media elements, for example, media_1
, media_2
, etc.
Media key naming
Media keys can be any string that fits the JSON key requirements. For example, media_1
, media_2
, cat
, dog
, hat
, etc. The key should be unique within the media
dictionary.
Example:
{
media: {
media_1: {
type: "image",
url: "https://example.com/first_image.png",
},
cat: {
type: "video",
url: "https://example.com/meow.mp4",
// additional media details...
},
}
}
To ensure optimal compatibility and performance, it is important to adhere to certain best practices and formats for both browser and mobile device support. For example, when incorporating video files, please consider using the MP4 format for broad compatibility across platforms. Similarly, when including images, it is advisable to use widely supported formats such as PNG or JPEG.
Royalties field
The field royalties
is an array of royalty recipients and their respective percentages. This field is used to define the royalty structure for the NFT, specifying the recipients who will receive a percentage of the sale price when the NFT is sold. The royalties
field is an array of the IV2Royalty
objects, where each object represents a royalty recipient and their respective percentage. The royalties
field is optional and can be used to define the royalty structure for the NFT.
Royalties item (IV2Royalty
) fields:
- address:
string
- The address of the royalty recipient. May be Substrate SS-58 encoded or Ethereum address. - percent:
number
- The percentage of the sale price that the recipient will receive. Valid values range is 0.00% - 99.99%. - isPrimaryOnly:
boolean
- Indicates whether the royalty should be paid only on the primary sale or on primary and secondary sales. Default value isfalse
which means that the royalty will be paid on both primary and secondary sales.
Example:
{
royalties: [
{
address: "5HTC7UFtTbBkC7dWWFt6ec3db5LEahCHMqw6LBN5hXEeqDHm",
percent: 1.5 // 1.5%
},
{
address: "0xee53Ae81b06Ed39Ac05B2cF2311F4b399E104Ba3",
percent: 10 // 10%
}
]
}
Customizable NFT Fields
NFTs in Unique can contain other NFTs through a process known as nesting. This is achieved by transferring one NFT to the address of another NFT (each NFT in Unique has its own address). In this way, one NFT owns another, and transitively, the owner of the root NFT owns the entire tree.
This allows the creation of an NFT tree where elements can customize each other. The leaves can influence the nodes, and so on, up to the root.
Unique provides this functionality through the customizing
field
This field contains the customization image details of the NFT itself and "slots" for customization, which can be utilized by nested NFTs.
For example, let's say we have an NFT "character" with customization slots like "hat" and "pet".
We can nest an NFT of a hat and an NFT of a pet within the character NFT, thereby modifying the base character image to show the character wearing a hat and accompanied by a pet.
All customizing NFT fields lay down inside the customizing
field. This field consists of 4 subfields:
interface IV2Customizing {
self: IV2CustomizingFileInfo
slots?: IV2CustomizingSlot[]
mutators?: Record<string, IV2CustomizingMutatorReaction>
mutator_reactions?: string[]
}
- The
self
field is used to store and describe this NFT's customization file and settings. - The
slots
field is used to describe the customization slots available for nested NFTs. - The
mutators
field is used to describe the mutators available for the NFT. - The
mutator_reactions
field is used to describe how this NFT should react to descendant mutators.
self
The self
field is used to describe the NFT's own customization file. It is separated from the main NFT image because it may differ significantly. For example, the main NFT image might be a showcase or ad-like image, while the actual overlaying image should be clear and well-suited for overlaying. Additionally, the self
field contains not only the link to the image but also the overlaying specifications, such as the order of overlaying, offsets, and other relevant details.
IV2CustomizingFileInfo
interface IV2CustomizingFileInfo {
type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'
url: string
name?: string
details?: IV2MediaDetails
image_overlay_specs?: IV2CustomizingImageOverlaySpecs
placeholder?: {
url: string
details?: IV2ImageDetails
}
tag?: string;
}
Types used: IV2MediaDetails, IV2ImageDetails and IV2CustomizingImageOverlaySpecs:
IV2CustomizingImageOverlaySpecs
The type IV2CustomizingImageOverlaySpecs
is used to define the overlay specifications for an image. This type includes the following fields:
All fields are optional
type IV2CustomizingImageOverlaySpecs = {
layer?: number
order_in_layer?: number
offset?: { x: number, y: number }
opacity?: number
rotation?: number
scale?: { x: number, y: number, unit?: 'px' | '%' /* default - % */ }
anchor_point?: { x: number, y: number }
parent_anchor_point?: { x: number, y: number }
}
slots
The slots
field is used to describe the customization slots available for nested NFTs. Each slot is described by the IV2CustomizingSlot
type.
interface IV2CustomizingSlot {
type: 'image' // now only 'image' type is supported
collections?: Array<string | number>
name?: string
image_overlay_specs?: IV2CustomizingImageOverlaySpecs
}
image_overlay_specs
has the type IV2CustomizingImageOverlaySpecs which is the same as in the self
field.
mutator_reactions
The mutator_reactions
field is used to describe how this NFT should react to descendant mutators. It is used when the image needs to be modified based on ancestor or descendant NFTs. For example, the image's offset might change if an ancestor has a non-standard size, or the image might be replaced by another if the descendant has a significant power-up.
This field is a dictionary where the key is the mutator name and the value is the reaction to it.
The key should meet the requirements of regular JSON key.
The value is the type IV2CustomizingMutatorReaction
which is an extended type IV2CustomizingImageOverlaySpecs with the url
and details
fields (alike the NFT first-level media
field).
interface IV2CustomizingMutatorReaction extends IV2CustomizingImageOverlaySpecs {
url: string
details?: IV2ImageDetails
}
mutators
Array of strings which are the names of mutators available for the NFT.
Customizing Overrides
This field allows overriding the default customization options for the NFT. It is rarely used and is typically reserved for specific cases where the main customizing
field needs to be locked and set as readonly.
The difference from the IV2Customizing
type is that the requirements for this field are much more flexible.
interface IV2CustomizingOverrides {
self?: Partial<IV2CustomizingFileInfo> & {tag?: string}
slots?: Partial<IV2CustomizingSlot>[]
mutators?: Record<string, Partial<IV2CustomizingMutatorReaction>>
mutator_reactions?: string[]
}
Optional fields
string
- optional
schemaName Default value: unique
.
Should be omitted when encoding tokens via Library @unique-nft/schemas
.
The name of the schema used for the metadata. When creating new NFTs, this should be 'unique'. This property defines the specific set of rules and attributes that the NFT adheres to. For older NFTs, created before the implementation of the 'unique' schema. This ensures backward compatibility and helps in identifying the origin and structure of the metadata associated with the NFT.
string
- optional
schemaVersion: Default value: 2.0.0
.
Should be omitted when encoding tokens via Library @unique-nft/schemas
.
string
- readonly
originalSchemaVersion: Readonly field.
Applicable only for old tokens (v0 an v1) in decoded format.
The original version of the schema. This property indicates the schema under which the NFT was initially created.
NFT Collection Schema V2
Collection in Unique may contain such fields:
cover_image: IV2ImageWithDetailsAndThumbnail - Cover image for the collection
default_token_image: IV2ImageWithDetailsAndThumbnail - Default image for the tokens in the collection
potential_attributes: Array of IV2Attribute - Potential attributes for the collection. An instruction for the NFT generator/creator which attributes can be used for the NFTs in this collection. Just a hint, not a requirement.
The structure of this field is similar to the NFT attributes
field, with the key difference being that it does not include a value
field. Instead, it has an optional values
field, which is an array of potential values.
type IV2PotentialAttributeValues = {
trait_type: string
display_type?: string
values?: string[]
}
royalties: Array of IV2Royalty - Royalties for the collection. The same as for the NFTs.
customizing: Customization options for the collection. Defines the relationships between collections, specifying which NFTs can be customized by the NFTs in this collection.
{
slots?: IV2CustomizingSlot[]
customizes?: Array<string | number>
}
Types
IV2Attribute
type IV2Attribute = {
trait_type: string
value: string | number
display_type?: string
}
IV2ImageDetails
All fields are optional
type IV2ImageDetails = Partial<{
name: string // name of the image (for captions, etc.)
type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other' // type of the
bytes: number // size of the image file in bytes
format: string // format of the image file (e.g., PNG, JPEG)
sha256: string // SHA-256 hash of the image file
width: number // width of the image in pixels
height: number // height of the image in pixels
order: number // order of the image
}>
IV2MediaDetails
All fields from the IV2ImageDetails and additional fields: duration
, codecs
, loop
.
All fields are optional as well
type IV2MediaDetails = Partial<{
name: string // name of the image (for captions, etc.)
type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other' // type of the
bytes: number // size of the image file in bytes
format: string // format of the image file (e.g., PNG, JPEG)
sha256: string // SHA-256 hash of the image file
width: number // width of the image in pixels
height: number // height of the image in pixels
order: number // order of the image
duration: number // duration in seconds
codecs: string[] // codecs used in the media file
loop: boolean // whether the media should loop
}>
IV2Media
type IV2Media = {
type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'
url: string
name?: string // name of the media (for captions, etc.)
details?: IV2MediaDetails
thumbnail?: { // thumbnail image for the media
url: string,
details?: IV2ImageDetails
}
poster?: { // poster image for the media (for videos - where applicable)
url: string,
details?: IV2ImageDetails
}
}
Tips:
- The
type
andurl
fields are required. - The
details
field extends the base type IV2ImageDetails and provides additional information about the media, such as duration, codecs, and whether the animation/video/audio should be looped in the player. - The
thumbnail
field may be used for the thumbnail image of the media or as a cover image for an audio file. - The
poster
field is used for the poster image of a video.
IV2ImageWithDetailsAndThumbnail
type IV2ImageWithDetailsAndThumbnail = {
url: string
details?: IV2ImageDetails
thumbnail?: {
url: string
details?: IV2ImageDetails
}
}
IV2Royalty
type IV2Royalty = {
address: string
percent: number
isPrimaryOnly?: boolean
}