From 37d7f85adb3762b78b3d0665ac365c91b284b33f Mon Sep 17 00:00:00 2001 From: Alex Saft Date: Thu, 25 Jul 2024 21:57:57 +0300 Subject: [PATCH 1/2] wip schemas v2 docs --- docs/.vuepress/configs/sidebar.ts | 3 +- docs/about/tech-concepts/addresses/index.md | 12 +- docs/reference/schemas/2.0.0.md | 391 -------------------- docs/reference/schemas/index.md | 354 ++++++++++++++++++ docs/tutorials/createCollectionV2/index.md | 12 +- 5 files changed, 367 insertions(+), 405 deletions(-) delete mode 100644 docs/reference/schemas/2.0.0.md create mode 100644 docs/reference/schemas/index.md diff --git a/docs/.vuepress/configs/sidebar.ts b/docs/.vuepress/configs/sidebar.ts index f28cc67..93449fd 100644 --- a/docs/.vuepress/configs/sidebar.ts +++ b/docs/.vuepress/configs/sidebar.ts @@ -45,7 +45,6 @@ export const sidebar: Record = { '/about/network-features/staking.md', '/about/network-features/sdk-indexer', '/about/network-features/marketplace.md', - ] }, { @@ -233,7 +232,7 @@ export const sidebar: Record = { { text: 'Schemas', children: [ - '/reference/schemas/2.0.0', + '/reference/schemas', ] } ], diff --git a/docs/about/tech-concepts/addresses/index.md b/docs/about/tech-concepts/addresses/index.md index 13d904c..75bee2d 100644 --- a/docs/about/tech-concepts/addresses/index.md +++ b/docs/about/tech-concepts/addresses/index.md @@ -25,16 +25,16 @@ demonstrates this in detail. @@ -104,7 +104,7 @@ There are two types of addresses in Ethereum: Externally Owned Address (EOA) and Externally Owned Address refers to an account with a public and private key pair that holds your funds. -An Ethereum address is a 42-character hexadecimal address derived from the last 20 bytes of the public key controlling the address with 0x appended in front. e.g. `0x71C7656EC7ab88b098defB751B7401B5f6d8976F`. +An Ethereum address is a 42-character hexadecimal address derived from the last 20 bytes of the public key controlling the address with 0x appended in front. e.g. `0xee53Ae81b06Ed39Ac05B2cF2311F4b399E104Ba3`. The Ethereum address is the "public" address you would need to receive funds from another party. To access funds in the address, you must have its private key. Kindly exercise a duty of care when handling your private key, as it can be used to access all the funds in an address. diff --git a/docs/reference/schemas/2.0.0.md b/docs/reference/schemas/2.0.0.md deleted file mode 100644 index 9c74622..0000000 --- a/docs/reference/schemas/2.0.0.md +++ /dev/null @@ -1,391 +0,0 @@ -# Unique Schema - -Unique schema is a schema where all links and metadata are stored on-chain. It allows for flexible data and attribute storage with separate access settings for every NFT field. This enables, for example, the creation of live NFTs where some parts of the NFT can be securely changed with guarantees from the blockchain that other parts won't be changed. - -NFTs created previously in schemaVersion 1.0.0 are returned in the new format. - -[Examples how to create collections NFTs 2.0.0](/tutorials/createCollectionV2) - -- **Schema 2.0.0** - -This metadata standard describes the data structure used to describe NFTs in JSON format. -[Example NFT 2.0.0](https://rest.unique.network/unique/v1/collections/v2?collectionId=654) -The main interface that describes the metadata for a NFT. - - -[[toc]] - -## schemaName `string` - 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, the schemaName might have values such as "old" or "ERC721Metadata". This ensures backward compatibility and helps in identifying the origin and structure of the metadata associated with the NFT. - -## schemaVersion: `string` - - **You need it:** To ensure your NFT metadata is compatible with the current standards and features of the Unique Network. - - **Code example:** `"schemaVersion": "2.0.0"` - - **Tips and best practices:** Always check for the latest version before creating your NFTs to ensure compatibility and access to new features. - -## originalSchemaVersion: `string` - The original version of the schema. This property indicates the schema under which the NFT was initially created. - -## name: `string` - - **You need it:** To provide a unique and identifiable title for your NFT. - - **Simplest use case:** Naming a basic digital artwork. - - **Code example:** `"name": "Sunset Painting"` - - **More advanced use case:** Naming NFTs in a series where each name follows a pattern or storyline. - - **Code example:** `"name": "Mystic Forest: Chapter 1 - The Awakening"` - - **Tips and best practices:** Choose names that are memorable and descriptive of the NFT's content or theme. Avoid overly generic names. - - -## description: `string` - - - **You need it:** To provide a narrative or context about the NFT, enhancing its appeal and storytelling. - - **Simplest use case:** Describing a standalone digital artwork. - - **Code example:** `"description": "A beautiful landscape painting depicting a serene sunset."` - - **More advanced use case:** Story-driven collections where each NFT's description adds to an overarching narrative. - - **Code example:** `"description": "In the heart of the Mystic Forest, the Abyssal Dragon awakens, bringing ancient magic back to the land."` - - **Tips and best practices:** Keep the description engaging and relevant to the NFT. - -## image: `string` - URL to the main image associated with the NFT. - - **Avail:** jpeg, jpg, gif, png. - - **You need it:** To visually represent your NFT. It's the primary image that users see in the interfaces of wallets, marketplaces, and other applications. - - **Simplest use case:** A single image representing a digital artwork. - - **Code example:** `"image": "https://example.com/artwork.jpg"` - - **More advanced use case:** NFTs where the image is a composite or dynamically generated based on certain criteria. - - **Code example:** `"image": "https://example.com/generate?params=dynamic"` - - **Tips and best practices:** Use high-quality images that are appropriately sized. Ensure the image URL is reliable and persistent. - -## image_details: `ImageDetails` - Additional details about the main image. - - **You need it if you want:** To provide the applications with specific details about the primary image, like its format, dimensions, and file size. - - **Simplest use case:** Providing basic information about a static artwork image. - - **Code example:** `{ "format": "jpg", "width": 1920, "height": 1080, "bytes": 204800 }` - - **More advanced use case:** Secure the source image changes by using a sha256 image hash. - - **Code example:** `{ "format": "png", "width": 960, "height": 720, "bytes": 512000, "type": "image", "sha256": "..." }` - - **Tips and best practices:** Provide accurate and detailed information. This aids in ensuring that images are displayed correctly across various platforms and devices. - - An interface describing additional details about an image. - - ```typescript - interface ImageDetails { - name?: string; - type?: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; - bytes?: number; - format?: string; - sha256?: string; - width?: number; - height?: number; - order?: number; - } - ``` - -## attributes: `Attribute[]` - Array of attributes associated with the NFT. - - **You need it if you want:** To provide additional information about the NFT in the form of key-value pairs, like color, size, or unique traits. - - **Simplest use case:** Basic traits for a digital artwork, like color and theme. - - **Code example:** `[ { "trait_type": "Color", "value": "Blue" }, { "trait_type": "Theme", "value": "Ocean" } ]` - - **More advanced use case:** Detailed attributes for collectible items, each with a set of distinct traits. - - **Code example:** `[ { "trait_type": "Strength", "value": 10 }, { "trait_type": "Magic", "value": 7 }, { "trait_type": "Agility", "value": 5 } ]` - - **Tips and best practices:** Choose attributes that add value and interest to the NFT. Avoid overloading with irrelevant details. - - ```typescript - interface Attribute { - /** @example Color */ - trait_type: string; - /** @example red */ - value: string | number; - /** @example color */ - display_type?: string; - } - ``` - -## media: `Record` - - 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 `Media` interface. This type of storage enables the specification of a clear order and priority for media files during loading. - - An interface describing media content associated with the NFT. - - ```typescript - interface Media { - type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; - url: string; - name?: string; - details?: MediaDetails; - thumbnail?: ImageWithDetailsAndThumbnail; - poster?: ImageWithDetailsAndThumbnail; - } - - interface ImageWithDetails { - url: string; - details?: ImageDetails; - } - - interface ImageWithDetailsAndThumbnail { - url: string; - details?: ImageDetails; - thumbnail?: ImageWithDetails; - } - ``` - - For instance, when incorporating media elements into the `media` property, you can use keys in the format `media_{N}`, where `N` is a numerical value representing the order or priority of the media file. Here's how this convention can be employed: - - ```typescript - { - // other NFT properties... - media: { - "media_1": { - type: "image", - url: "https://example.com/first_image.png", - // additional media details... - }, - "media_2": { - type: "video", - url: "https://example.com/second_video.mp4", - // additional media details... - }, - // additional media elements... - } - } - ``` - -### Media Formats and Best Practices - To ensure optimal compatibility and performance, it's important to adhere to certain best practices and formats when specifying media elements for NFTs. - -#### Image - - **Formats**: JPEG, PNG, GIF, SVG, WebP - - **Best Practices**: Use high-resolution images with appropriate compression to balance quality and file size. Provide alternative text descriptions for accessibility. - -#### Animation - - **Formats**: GIF, SVG, APNG, WebP - - **Best Practices**: Use lightweight formats for animations to ensure quick loading times. Optimize frame rates to reduce file sizes without compromising quality. - -#### Video - - **Formats**: MP4, WebM - - **Best Practices**: Use H.264 encoding for MP4 files for broad compatibility. Include a poster image to be displayed before the video loads. Ensure videos are not excessively long or large in file size. - -#### Audio - - **Formats**: MP3, WAV - - **Best Practices**: Use MP3 for compatibility and smaller file sizes. Ensure the audio quality is high enough to avoid distortion but optimized for size. - -#### Spatial - - **Formats**: GLTF, GLB, USDZ - - **Best Practices**: Use compressed 3D models to reduce loading times. Provide low-poly versions if possible for performance on less powerful devices. - -#### PDF/Document - - **Formats**: PDF - - **Best Practices**: Ensure documents are optimized for web viewing. Include metadata within the PDF for accessibility and searchability. - -#### Other - - **Formats**: Depends on the content type. - - **Best Practices**: Clearly define the content type and ensure it is supported by the platforms where the NFT will be viewed or interacted with. - -## animation_url: `string` - A URL to a multi-media attachment for the item. The file extensions GLTF, GLB, WEBM, MP4, M4V, OGV, and OGG are supported, along with the audio-only extensions MP3, WAV, and OGA. - - Animation_url also supports HTML pages, allowing you to build rich experiences and interactive NFTs using JavaScript canvas, WebGL, and more. Scripts and relative paths within the HTML page are now supported. However, access to browser extensions is not supported - -## animation_details: `MediaDetails` - Additional details about the animation. - - - ```typescript - interface MediaDetails { - name?: string; - type?: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; - bytes?: number; - format?: string; - sha256?: string; - width?: number; - height?: number; - order?: number; - duration?: number; - codecs?: string[]; - loop?: boolean; - } - ``` - -## youtube_url: `string` - URL to a YouTube video associated with the NFT. - -## created_by: `string` - Address of the creator of the NFT. - -## external_url: `string` - URL to an external resource providing more information about the NFT. It will appear below the asset's image and will allow users to leave Unique Marketplace and view the item on your site. - -## customizing: `Customizing` - Customizing options for the NFT. - - **You need it if you want:** To allow users to visually enhance and modify base NFTs by equipping other NFTs, creating dynamic and interactive digital assets. - - - **Use case:** - The process involves two types of collections: - - **Base Collection:** This is the primary collection. - - **Equip Collections:** These collections (one or more) are designed to interact with and modify the Base Collection. - - There are endless options for how Equip NFTs can customize a Base NFT: - - **Overlay Placement for the images:** Placing the equip image over a specific part of the base image while: - - **Layering:** Ordering and reordering layers. - - **Precise Placement:** Placing at the desired point or area (Offset). - - **Scaling:** Adjusting the size of both Base and Equip NFTs. - - **Rotation:** Rotating both Base and Equip NFTs. - - **Opacity Adjustment:** Adjusting the transparency of both Base and Equip NFTs. - - **Customizable Backgrounds:** Setting specific backgrounds for the NFTs. - - **Pattern or Texture Application:** Applying various patterns or textures. - - **Morphing or Transformation:** Changing the shape or form of the images and other media types. Examples include: - - Changing the character's hairstyle. - - Morphing facial features. - - Slowing the audio. - - **Audio/Video/3D Integration:** Adding multimedia elements to images and other media types. Examples include: - - Jingle sound with jewelry. - - Theme music with a heroic cape. - - Mixing audio tracks. - - Adding subtitles to videos. - - - **Tips and best practices:** When using equip collections to enhance base NFTs, consider how each modification adds value and interest. Ensure that modifications are meaningful and enhance the overall user experience without overwhelming the base NFT. - - Interfaces for customizing: - ```typescript - interface Customizing { - self: CustomizingFileInfo; - slots?: CustomizingSlot[]; - /** @example ["mutator1","mutator2"] */ - mutators?: any[][]; - mutator_reactions?: MutatorReaction[]; - } - - interface CustomizingFileInfo { - type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; - url: string; - name?: string; - details?: MediaDetails; - image_overlay_specs?: CustomizingImageOverlaySpecs; - placeholder?: ImageWithDetails; - tag?: string; - } - - interface CustomizingSlot { - type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; - collections?: (string | number)[]; - name?: string; - image_overlay_specs?: CustomizingImageOverlaySpecs; - } - - interface MutatorReaction { - /** @example 1 */ - layer?: number; - /** @example 1 */ - order_in_layer?: number; - offset?: Coordinates; - opacity?: number; - rotation?: number; - scale?: Scale; - mount_point?: Coordinates; - parent_mount_point?: Coordinates; - url: string; - details?: ImageDetails; - } - - interface Coordinates { - x: number; - y: number; - } - - interface Scale { - x: number; - y: number; - unit?: 'px' | '%'; - } - - interface CustomizingImageOverlaySpecs { - /** @example 1 */ - layer?: number; - /** @example 1 */ - order_in_layer?: number; - offset?: Coordinates; - opacity?: number; - rotation?: number; - scale?: Scale; - mount_point?: Coordinates; - parent_mount_point?: Coordinates; - } - ``` - -## customizing_overrides: `CustomizingOverrides` - Overrides for customizing options. - - ```typescript - interface CustomizingOverrides { - self?: CustomizingFileInfo; - slots?: CustomizingSlot[]; - mutators?: string[]; - mutator_reactions?: MutatorReaction[]; - } - ``` - -## background_color: `string` - Background color of the NFT. Must be a six-character hexadecimal without a pre-pended #. - -## royalties: `Royalty[]` - Array of royalty information associated with the NFT. - - **You need it if you want:** To define how creators or other stakeholders will receive a percentage of sales from secondary market sales of the particular NFT. - - **Simplest use case:** A single creator receiving royalties. - - **Code example:** `[ { "address": "0x1234...", "percent": 5 } ]` - - **More advanced use case:** Splitting royalties between multiple parties, such as the artist, a charity, and a collaborator. - - **Code example:** `[ { "address": "0x1234...", "percent": 3 }, { "address": "0x5678...", "percent": 2 } ]` - - **Most advanced use case:** Using a smart contract address to achieve more complicated distribution models. - - **Code example:** `[ { "address": "0x1234..." } ]` - - **Tips and best practices:** Clearly define royalty percentages and ensure they are fair. Keep the royalty structure transparent to buyers and other stakeholders. - An interface representing royalty information associated with the NFT. - - ```typescript - interface Royalty { - /** - * The ss-58 encoded or Ethereum address - * @example yGCyN3eydMkze4EPtz59Tn7obwbUbYNZCz48dp8FRdemTaLwm - */ - address: string; - /** @example 5 */ - percent?: number; - isPrimaryOnly?: boolean; - } - ``` - -## locale: `string` (Optional) - - **You need it if you want:** To tailor your NFT's language setting to specific regional or linguistic audiences, enhancing accessibility and user experience. - - **Simplest use case:** A collection primarily targeting a specific language-speaking audience. - - **Code Example:** `{"locale": "en" }` - -## collectionId: `number` - The ID of the collection to which the NFT belongs. - -## tokenId: `number` - The ID of the NFT. - -## owner: `string` - SS-58 encoded address of the owner of the NFT. - -## parsingError: `string | null` - Error message if there was a parsing error, otherwise null. - -## properties: `PropertyWithHexValue[]` - Array of properties with their hexadecimal values. Used for internal purposes and ensuring backward compatibility for tokens created under schema version 1.0.0. Generated automatically when creating a token based on the provided schemaName, schemaVersion, and attributes. - - ```typescript - interface PropertyWithHexValue { - key: string; - value: string; - /** @example 0x01 */ - valueHex: string; - } - ``` - -## nestingParentToken: `NestingParentId` - ID of the parent token in nesting. - - ```typescript - interface NestingParentId { - collectionId: number; - tokenId: number; - } - ``` - ---- diff --git a/docs/reference/schemas/index.md b/docs/reference/schemas/index.md new file mode 100644 index 0000000..904b202 --- /dev/null +++ b/docs/reference/schemas/index.md @@ -0,0 +1,354 @@ +# 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. + +[[toc]] + +#### 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 + +1. **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. +2. **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](/tutorials/createCollectionV2) + +## Schema V2 Detailed Description + +First of all, [Schema V2 NFT Example](https://rest.unique.network/unique/v1/tokens/v2?collectionId=654&tokenId=1) + +For using the Unique Schema V2, you may find this official library useful: [unique-nft/schemas](https://www.npmjs.com/package/@unique-nft/schemas/v/2.1.6) + +Next, let's take a detailed look at all the fields of the schema. + +::: tip 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](#iv2imagedetails) - Additional details about the main image. +Example: +```json5:no-line-numbers +{ + image_details: { + name: "Artwork", + type: "image", + format: "PNG", + bytes: 1048576, + width: 1000, + height: 1000, + sha256: "0x1234...", + } +} +``` + +- **attributes**: Array of [IV2Attribute](#iv2attribute) - Array of attributes associated with the NFT. +Example: +```json5:no-line-numbers +{ + 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](#iv2imagedetails) - Additional details about the animation from the `animation_url` field. +Example is the same as in the `image_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](#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](#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. + + +::: tip 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: +```json5:no-line-numbers +{ + 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 + +Royalty 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 is `false` which means that the royalty will be paid on both primary and secondary sales. + +Example: +```json5 +{ + royalties: [ + { + address: "5HTC7UFtTbBkC7dWWFt6ec3db5LEahCHMqw6LBN5hXEeqDHm", + percent: 1.5 // 1.5% + }, + { + address: "0xee53Ae81b06Ed39Ac05B2cF2311F4b399E104Ba3", + percent: 10 // 10% + } + ] +} +``` + +### Customizable NFT Fields + +#### customizing: `Customizing` + Customizing options for the NFT. + - **You need it if you want:** To allow users to visually enhance and modify base NFTs by equipping other NFTs, creating dynamic and interactive digital assets. + + - **Use case:** + The process involves two types of collections: + - **Base Collection:** This is the primary collection. + - **Equip Collections:** These collections (one or more) are designed to interact with and modify the Base Collection. + + There are endless options for how Equip NFTs can customize a Base NFT: + - **Overlay Placement for the images:** Placing the equip image over a specific part of the base image while: + - **Layering:** Ordering and reordering layers. + - **Precise Placement:** Placing at the desired point or area (Offset). + - **Scaling:** Adjusting the size of both Base and Equip NFTs. + - **Rotation:** Rotating both Base and Equip NFTs. + - **Opacity Adjustment:** Adjusting the transparency of both Base and Equip NFTs. + - **Customizable Backgrounds:** Setting specific backgrounds for the NFTs. + - **Pattern or Texture Application:** Applying various patterns or textures. + - **Morphing or Transformation:** Changing the shape or form of the images and other media types. Examples include: + - Changing the character's hairstyle. + - Morphing facial features. + - Slowing the audio. + - **Audio/Video/3D Integration:** Adding multimedia elements to images and other media types. Examples include: + - Jingle sound with jewelry. + - Theme music with a heroic cape. + - Mixing audio tracks. + - Adding subtitles to videos. + + - **Tips and best practices:** When using equip collections to enhance base NFTs, consider how each modification adds value and interest. Ensure that modifications are meaningful and enhance the overall user experience without overwhelming the base NFT. + + Interfaces for customizing: + ```typescript + interface Customizing { + self: CustomizingFileInfo; + slots?: CustomizingSlot[]; + /** @example ["mutator1","mutator2"] */ + mutators?: any[][]; + mutator_reactions?: MutatorReaction[]; + } + + interface CustomizingFileInfo { + type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; + url: string; + name?: string; + details?: MediaDetails; + image_overlay_specs?: CustomizingImageOverlaySpecs; + placeholder?: ImageWithDetails; + tag?: string; + } + + interface CustomizingSlot { + type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; + collections?: (string | number)[]; + name?: string; + image_overlay_specs?: CustomizingImageOverlaySpecs; + } + + interface MutatorReaction { + /** @example 1 */ + layer?: number; + /** @example 1 */ + order_in_layer?: number; + offset?: Coordinates; + opacity?: number; + rotation?: number; + scale?: Scale; + mount_point?: Coordinates; + parent_mount_point?: Coordinates; + url: string; + details?: ImageDetails; + } + + interface Coordinates { + x: number; + y: number; + } + + interface Scale { + x: number; + y: number; + unit?: 'px' | '%'; + } + + interface CustomizingImageOverlaySpecs { + /** @example 1 */ + layer?: number; + /** @example 1 */ + order_in_layer?: number; + offset?: Coordinates; + opacity?: number; + rotation?: number; + scale?: Scale; + mount_point?: Coordinates; + parent_mount_point?: Coordinates; + } + ``` + +#### customizing_overrides: `CustomizingOverrides` + Overrides for customizing options. + + ```typescript + interface CustomizingOverrides { + self?: CustomizingFileInfo; + slots?: CustomizingSlot[]; + mutators?: string[]; + mutator_reactions?: MutatorReaction[]; + } + ``` + +### Optional fields + +#### schemaName `string` - optional +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. + +#### schemaVersion: `string` - optional +Default value: `2.0.0`. +*Should be omitted when encoding tokens via Library `@unique-nft/schemas`.* + +#### originalSchemaVersion: `string` - readonly +**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. + + +## Types + +#### IV2Attribute + +```typescript:no-line-numbers +type IV2Attribute = { + trait_type: string + value: string | number + display_type?: string +} +``` + +#### IV2ImageDetails + +*All fields are optional* + +```typescript:no-line-numbers +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 +}> +``` + + +#### IV2Media + +```typescript:no-line-numbers +type IV2Media = { + type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other' + url: string + name?: string // name of the media (for captions, etc.) + details?: IV2ImageDetails & Partial<{ + duration: number // duration of the media in seconds (for videos, audio, etc - where applicable) + codecs: string[] // codecs used in the media file + loop: boolean // whether the media should loop + }> + 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` and `url` fields are required. +- The `details` field extends the base type [IV2ImageDetails](#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. + +--- diff --git a/docs/tutorials/createCollectionV2/index.md b/docs/tutorials/createCollectionV2/index.md index e8cf42d..76d597e 100644 --- a/docs/tutorials/createCollectionV2/index.md +++ b/docs/tutorials/createCollectionV2/index.md @@ -1,16 +1,16 @@ -# Mint an NFT using schema 2.0.0 +# Mint an NFT using Unique Schema v2 Below is an example of creating a collection and generating a token. Upon executing this script, you will obtain a collection and token similar to the following: [Collection](https://unqnft.io/collection/654?filterState=) [NFT1](https://unqnft.io/unique/token/654/1). [NFT2](https://unqnft.io/unique/token/654/4). - [Metadata description](/reference/schemas/2.0.0.html) + [Metadata description](/reference/schemas) -```typescript -import { Sr25519Account } from '@unique-nft/utils/sr25519'; -import Sdk from '@unique-nft/sdk'; -import * as dotenv from 'dotenv'; +```typescript:no-line-numbers +import { Sr25519Account } from '@unique-nft/utils/sr25519' +import Sdk from '@unique-nft/sdk' +import * as dotenv from 'dotenv' const SUBSTRATE_MNEMONIC = PUT_YOUR_MNEMONIC_HERE; From 048df56502ed85a3a87c17a16230aa69a9f061eb Mon Sep 17 00:00:00 2001 From: Alex Saft Date: Wed, 31 Jul 2024 14:04:02 +0300 Subject: [PATCH 2/2] schemas v2 docs --- docs/reference/schemas/index.md | 313 +++++++++++++++++++++----------- 1 file changed, 205 insertions(+), 108 deletions(-) diff --git a/docs/reference/schemas/index.md b/docs/reference/schemas/index.md index 904b202..4fd4231 100644 --- a/docs/reference/schemas/index.md +++ b/docs/reference/schemas/index.md @@ -27,7 +27,7 @@ NFTs created previously in schemaVersion v0 and v1 are returned in the new forma [Examples how to create collections NFTs in Schema V2](/tutorials/createCollectionV2) -## Schema V2 Detailed Description +## NFT Token Schema V2 Detailed Description First of all, [Schema V2 NFT Example](https://rest.unique.network/unique/v1/tokens/v2?collectionId=654&tokenId=1) @@ -142,13 +142,15 @@ To ensure optimal compatibility and performance, it is important to adhere to ce ### Royalties field -Royalty fields: +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 is `false` which means that the royalty will be paid on both primary and secondary sales. Example: -```json5 +```json5:no-line-numbers { royalties: [ { @@ -165,114 +167,139 @@ Example: ### Customizable NFT Fields -#### customizing: `Customizing` - Customizing options for the NFT. - - **You need it if you want:** To allow users to visually enhance and modify base NFTs by equipping other NFTs, creating dynamic and interactive digital assets. - - - **Use case:** - The process involves two types of collections: - - **Base Collection:** This is the primary collection. - - **Equip Collections:** These collections (one or more) are designed to interact with and modify the Base Collection. - - There are endless options for how Equip NFTs can customize a Base NFT: - - **Overlay Placement for the images:** Placing the equip image over a specific part of the base image while: - - **Layering:** Ordering and reordering layers. - - **Precise Placement:** Placing at the desired point or area (Offset). - - **Scaling:** Adjusting the size of both Base and Equip NFTs. - - **Rotation:** Rotating both Base and Equip NFTs. - - **Opacity Adjustment:** Adjusting the transparency of both Base and Equip NFTs. - - **Customizable Backgrounds:** Setting specific backgrounds for the NFTs. - - **Pattern or Texture Application:** Applying various patterns or textures. - - **Morphing or Transformation:** Changing the shape or form of the images and other media types. Examples include: - - Changing the character's hairstyle. - - Morphing facial features. - - Slowing the audio. - - **Audio/Video/3D Integration:** Adding multimedia elements to images and other media types. Examples include: - - Jingle sound with jewelry. - - Theme music with a heroic cape. - - Mixing audio tracks. - - Adding subtitles to videos. - - - **Tips and best practices:** When using equip collections to enhance base NFTs, consider how each modification adds value and interest. Ensure that modifications are meaningful and enhance the overall user experience without overwhelming the base NFT. - - Interfaces for customizing: - ```typescript - interface Customizing { - self: CustomizingFileInfo; - slots?: CustomizingSlot[]; - /** @example ["mutator1","mutator2"] */ - mutators?: any[][]; - mutator_reactions?: MutatorReaction[]; - } +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. - interface CustomizingFileInfo { - type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; - url: string; - name?: string; - details?: MediaDetails; - image_overlay_specs?: CustomizingImageOverlaySpecs; - placeholder?: ImageWithDetails; - tag?: string; - } +**Unique provides this functionality through the `customizing` field** - interface CustomizingSlot { - type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other'; - collections?: (string | number)[]; - name?: string; - image_overlay_specs?: CustomizingImageOverlaySpecs; - } +This field contains the customization image details of the NFT itself and "slots" for customization, which can be utilized by nested NFTs. - interface MutatorReaction { - /** @example 1 */ - layer?: number; - /** @example 1 */ - order_in_layer?: number; - offset?: Coordinates; - opacity?: number; - rotation?: number; - scale?: Scale; - mount_point?: Coordinates; - parent_mount_point?: Coordinates; - url: string; - details?: ImageDetails; - } +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. - interface Coordinates { - x: number; - y: number; - } - interface Scale { - x: number; - y: number; - unit?: 'px' | '%'; - } - interface CustomizingImageOverlaySpecs { - /** @example 1 */ - layer?: number; - /** @example 1 */ - order_in_layer?: number; - offset?: Coordinates; - opacity?: number; - rotation?: number; - scale?: Scale; - mount_point?: Coordinates; - parent_mount_point?: Coordinates; - } - ``` +All customizing NFT fields lay down inside the `customizing` field. This field consists of 4 subfields: + +```typescript:no-line-numbers +interface IV2Customizing { + self: IV2CustomizingFileInfo + slots?: IV2CustomizingSlot[] + mutators?: Record + mutator_reactions?: string[] +} +``` -#### customizing_overrides: `CustomizingOverrides` - Overrides for customizing options. +- 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. - ```typescript - interface CustomizingOverrides { - self?: CustomizingFileInfo; - slots?: CustomizingSlot[]; - mutators?: string[]; - mutator_reactions?: MutatorReaction[]; +##### 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 + +```typescript:no-line-numbers +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](#iv2mediadetails), [IV2ImageDetails](#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* + +```typescript:no-line-numbers +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. + +```typescript:no-line-numbers +interface IV2CustomizingSlot { + type: 'image' // now only 'image' type is supported + collections?: Array + name?: string + image_overlay_specs?: IV2CustomizingImageOverlaySpecs +} +``` + +`image_overlay_specs` has the type [IV2CustomizingImageOverlaySpecs](#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](#iv2customizingimageoverlayspecs) with the `url` and `details` fields (alike the NFT first-level `media` field). + +```typescript:no-line-numbers +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. + +```typescript:no-line-numbers +interface IV2CustomizingOverrides { + self?: Partial & {tag?: string} + slots?: Partial[] + mutators?: Record> + mutator_reactions?: string[] +} +``` ### Optional fields @@ -291,6 +318,38 @@ Default value: `2.0.0`. 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](#iv2imagewithdetailsandthumbnail) - Cover image for the collection + +- **default_token_image**: [IV2ImageWithDetailsAndThumbnail](#iv2imagewithdetailsandthumbnail) - Default image for the tokens in the collection + +- **potential_attributes**: Array of [IV2Attribute](#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. + +```typescript:no-line-numbers +type IV2PotentialAttributeValues = { + trait_type: string + display_type?: string + values?: string[] +} +``` + +- **royalties**: Array of [IV2Royalty](#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. + +```typescript:no-line-numbers +{ + slots?: IV2CustomizingSlot[] + customizes?: Array +} +``` ## Types @@ -321,6 +380,27 @@ type IV2ImageDetails = Partial<{ }> ``` +#### IV2MediaDetails + +All fields from the [IV2ImageDetails](#iv2imagedetails) and additional fields: `duration`, `codecs`, `loop`. + +*All fields are optional as well* + +```typescript:no-line-numbers +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 @@ -329,11 +409,7 @@ type IV2Media = { type: 'image' | 'animation' | 'video' | 'audio' | 'spatial' | 'pdf' | 'document' | 'other' url: string name?: string // name of the media (for captions, etc.) - details?: IV2ImageDetails & Partial<{ - duration: number // duration of the media in seconds (for videos, audio, etc - where applicable) - codecs: string[] // codecs used in the media file - loop: boolean // whether the media should loop - }> + details?: IV2MediaDetails thumbnail?: { // thumbnail image for the media url: string, details?: IV2ImageDetails @@ -351,4 +427,25 @@ Tips: - 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 + +```typescript:no-line-numbers +type IV2ImageWithDetailsAndThumbnail = { + url: string + details?: IV2ImageDetails + thumbnail?: { + url: string + details?: IV2ImageDetails + } +} +``` + +#### IV2Royalty + +```typescript:no-line-numbers +type IV2Royalty = { + address: string + percent: number + isPrimaryOnly?: boolean +} +``` \ No newline at end of file