diff --git a/tips/TIP-0027/irc27.schema.json b/tips/TIP-0027/irc27.schema.json new file mode 100644 index 000000000..08bf0578a --- /dev/null +++ b/tips/TIP-0027/irc27.schema.json @@ -0,0 +1,53 @@ + +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/iotaledger/tips/main/tips/TIP-0027/irc27.schema.json", + "title": "IRC27 IOTA NFT Metadata Schema", + "description": "A JSON schema for validating IRC27 compliant NFT metadata", + "type": "object", + "properties": { + "standard": { + "type": "string", + "description": "identifier for the metadata standard used", + "pattern": "^IRC27$" + }, + "version": { + "type": "string", + "description": "version of the metadata standard used", + "pattern": "^v\\d+.\\d+$" + }, + "name": { + "type": "string", + "description": "A descriptive name of the token" + }, + "description": { + "type": "string", + "description": "some information about the NFT project" + }, + "type": { + "type": "string", + "description": "MIME type of the asset" + }, + "uri": { + "type": "string", + "description": "URI pointing to the resource where the file with `type` MIME type is hosted" + }, + "collectionName": { + "type": "string", + "description": "A human readable name for the NFT collection" + }, + "royalties": { + "type": "object", + "description": "object containing the payout addresses mapped to their percentage share" + }, + "issuerName": { + "type": "string", + "description": "name of the artist" + }, + "attributes": { + "type": "array", + "description": "array containing any additional data as objects." + } + }, + "required": [ "standard", "type", "version", "uri", "name" ] +} diff --git a/tips/TIP-0027/tip-0027.md b/tips/TIP-0027/tip-0027.md new file mode 100644 index 000000000..e0c3495fc --- /dev/null +++ b/tips/TIP-0027/tip-0027.md @@ -0,0 +1,364 @@ +--- +tip: 27 +title: IOTA NFT Standard IRC27 +description: Define NFT standard and creator royalties +author: Adam Eunson (@AdamCroply) , Merul Dhiman (@coodos) +discussions-to: https://github.com/iotaledger/tips/discussions/59 +status: Proposed +type: Standards +layer: IRC +created: 2022-03-04 +--- + +# IOTA NFT Standard - IRC27 + +## Abstract + +**IRC27** is a series of standards to support interoperable and universal NFT systems throughout the IOTA ecosystem, to provide a more robust and secure system for creators and buyers. + +### Introduction + +This document aims to support a universal system that can provide dApp developers and creators with an interoperable foundation of standards to support ease-of-adoption and a connected NFT ecosystem. To bring value, security, trust, and interoperability. + +Focusing on the primary use case for digital assets as NFTs, this defined standard supports key aspects in the creation, trade, and exchange of different assets with a focus on image, video, audio, and 3d asset file types. + +To support an easy-to-implement system the IOTA NFT Standard supports: + +- **Collection ID** system should define NFT origins by issuerId and collectionId for authenticity and verification within the IOTA NFT space. +- **Creator Royalty** System that can support universal creator royalties throughout the ecosystem. +- **NFT Schema Standard** allowing for easily definable keys and values for ease-of-integration. +- **Version Modelling** to allow for easy updates as well as backwards compatibility. +- **Modular System Design** to provide developers freedom to utilise only required elements, as well as for future standards expansion beyond the existing standard model. + +The standard provides the foundation for future expansion, supporting a modular design to provide an opportunity for selective integration, as well as further use case expansion through additional modules as time goes by. + +## Motivation + +### Why IOTA NFT Standards? + +Non-Standardised NFT systems have caused concerns and issues across a number of areas in other ecosystems. The lack of interoperable standards present numerous awkward and complicated experiences and in some ecosystems has resulted in countless verification and API issues that segment the NFT community. + +Early safeguards are possible to put in place to support a more secure and interoperable ecosystem that puts creators and buyer security at the forefront, providing developers and dApp makers the grounds to build a more connected and consistent ecosystem. + +With the IOTA Tokenization Framework in its infancy, the early adoption of an IOTA NFT Standard can support a safer, more secure environment for creators and dApp providers, to allow an easily interoperable experience through-out the IOTA ecosystem. + +In this document we will present the IOTA NFT Standard - IRC27. + +## Specification + + +### Collection ID + +The IOTA Tokenization Framework allows for a unique and robust solution when defining the identity of a collection. The integration of such a system can support verification of the origins of the creation of an NFT. For example, an artist creating a collection of works that will be labelled under a single collection. This allows for ease of verification for buyers and 3rd party application developers to provide an easily observable system of authenticity for users navigating the IOTA NFT space. + +The standard is defined utilising the creation mechanism for NFTs. + +`issuerId` (referred to as [Issuer Block in TIP-18](https://github.com/iotaledger/tips/pull/38)) is already defined in the framework, allowing every NFT created from the same source to be easily defined. + +Each NFT in the IOTA Tokenization Framework has its own unique address, that allows the ability to define a **collection UTXO** that can subsequently mint each unique NFT within that collection. + +The `nftId` of a collection NFT is defined as the `collectionId`. + +The `collectionId` will act as a unique identifier for the collection and would allow the `collectionNft` to control NFT creation in a collection. This allows for unprecedented amounts of control where you can lock NFT creation in a collection for some time. It also allows for the ability to transfer the `collectionNft` (parent NFT of all the NFTs minted within a collection) on transfer of which the new holder will be able to add NFTs to the collection, gaining control over ownership of the collection brand, but also with the ability to lock the collection by the destruction of the collection NFT. + +A creator should define the UTXO of their collection NFT as the sole minting source of an entire collection that is the `collectionId`. + +A creator may choose to burn the collection NFT on completion of minting or retain the collection NFT to add further NFTs to the collection over time. + +The UTXO of the collection NFT, `nftId`, acts as the `collectionId` for the collection and can be used in dApps to define the verified origin of an NFT. + +To call the defined `collectionId` you should request the `collectionId` UTXO of the collection NFT and resolve to the Collection Registry for human identifiable verification. + +To better serve the ecosystem with a single point of record for registered collections one possible Public Token Registry is defined in [IOTA Public Token Registry - TIP 33](https://github.com/iotaledger/tips/pull/72) where further reading can be found. + +It is important to note that several token registries may coexist in the future, TIP-27 only defines the data structure of NFT metadata.It is up to the registry to decide what criteria and method to use to verify and accept submissions. + +### Creator Royalties + +A system to support interoperable royalty payments across dApps. Allowing universal secondary market reward systems to be integrated through-out the ecosystem. Integration of such systems is at the choosing of the dApp developer but is encouraged to support creator royalties. + +Royalty addresses may be defined under the `royalties` key within the NFT metadata. + - The key inside the royalties object must be a valid iota1/smr1 address where royalties will be sent to. + - The value must be a numeric decimal representative of the percentage required ie. 0.05=5% +```json +{ + ... + "royalties": { + "iota1...a": 0.05 + } +} +``` + +In the event there are further royalty, multiple royalty addresses could be used in the form of an object where the address and percentage are mapped in a key value format inside the `royalties` field. + +```json +{ + ... + "royalties": { + "iota1...a": 0.025, + "iota1...b": 0.025, + "iota1...c": 0.025, + "iota1...d": 0.025 + } +} +``` + +The total decimal sum of all `royaltyPercentage` must never exceed 1 and is recommended not to exceed 0.5. + +If `royalties` exists, it is iterated over the keys and then all the royalties are paid out till there are no keys left to iterate over. + +### NFT Schema + +For ease of development and interoperability between applications within the ecosystem an extendable schema standard is defined to support ease of integration of NFTs for developers. + +Each schema is defined by three main keys: + +- `standard` – the standard model +- `schema` – the defined schema type +- `version` – the version of the standard + + +**Universal schema** +Each NFT schema should consist of a collection of universal keys to define key properties of the NFT + +The standard defined is: + +- `IRC27` + +The schema type is defined as a [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types), for example: + +- Image files: `image/jpeg`, `image/png`, `image/gif`, etc. +- Video files: `video/x-msvideo` (avi), `video/mp4`, `video/mpeg`, etc. +- Audio files: `audio/mpeg`, `audio/wav`, etc. +- 3D Assets: `model/obj`, `model/u3d`, etc. +- Documents: `application/pdf`, `text/plain`, etc. + +You may find all common MIME types in [IANA's registry](https://www.iana.org/assignments/media-types/media-types.xhtml). +Custom file types might define their own MIME types. + +The version is defined by the version number used preceded with the letter v, current version: + +- `v1.0` + +Define the standard, the type, and the version: +```json + +{ + "standard": "IRC27", + "type": "image/jpeg", + "version": "v1.0" +} +``` + +Additional keys that must be included in every NFT schema: +- `uri` – url pointing to the NFT file location with MIME type defined in `type`. +- `name` - alphanumeric text string defining the human identifiable name for the NFT + +```json +{ + "standard": "IRC27", + "version": "v1.0", + "type": "image/jpeg", + "uri": "https://mywebsite.com/my-nft-files-1.jpeg", + "name": "My NFT #0001" +} +``` + +Optional, but recommended keys, that may be included in NFT schema include: +- `collectionName` – alphanumeric text string defining the human identifiable collection name +- `royalties` - Object containing key value pair where payment address mapped to the payout percentage +- `issuerName` – alphanumeric text string to define the human identifiable name of the creator +- `description` – alphanumeric text string to define a basic description of the NFT +- `attributes` - Array objects defining additional attributes of the NFT + + +```json +{ + "standard": "IRC27", + "version": "v1.0", + "type": "image/jpeg", + "uri": "https://mywebsite.com/my-nft-files-1.jpeg", + "name": "My NFT #0001", + "collectionName": "My Collection of Art", + "royalties": { + "iota1...a": 0.025, + "iota1...b": 0.025 + }, + "issuerName": "My Artist Name", + "description": "A little information about my NFT collection" +} +``` + +In addition to the required and recommended schema, the inclusion of `attributes` allows for versatile expansion for NFT metadata. + +`attributes` are the attributes for the item, which will show up on dApps like NFT Marketplaces. + +IRC27 NFT metadata follows the [OpenSea metadata standards](https://docs.opensea.io/docs/metadata-standards). + +```json +{ + "standard": "IRC27", + "version": "v1.0", + "type": "image/jpeg", + "uri": "https://mywebsite.com/my-nft-files-1.jpeg", + "name": "My NFT #0001", + "collectionName": "My Collection of Art", + "royalties": { + "iota1...a": 0.025, + "iota1...b": 0.025 + }, + "issuerName": "My Artist Name", + "description": "A little information about my NFT collection" + "attributes": [ + { + "trait_type": "Background", + "value": "Purple" + }, + { + "trait_type": "Element", + "value": "Water" + }, + { + "trait_type": "Attack", + "value": "150" + }, + { + "trait_type": "Health", + "value": "500" + } + ] +} +``` + +## Practical example + +How does this all work for L1 NFT collections in the IOTA Tangle? Best to explain with an example. + +In Stardust L1 NFTs are represented as outputs in the ledger. Each NFT output has the following properties: + - (mandatory) `nftId`: a unique identifier of the NFT output assigned by the protocol upon minting + - (mandatory) `owner`: address in _Address Unlock Condition_ that is allowed to unlock the NFT output + - (optional) `immutableIssuer`: an address (can be alias/nft address too) that minted the NFT output + - (optional) `immutableMetadata`: binary blob of data defined upon minting by the issuer + - (optional) `sender`: defines an address that transferred the nft to the current owner + - (optional) `mutableMetadata`: binary blob defined by the last sender + +To host metadata about the NFT, the `immutableMetadata` field of the output should be used, as the mutable one may be changed by the current owner. +Storing metadata in an output increases the storage deposit requirement of the output, but then no additional off-chain metadata storage solution is required. + +NFTs may be standalone assets, but often they are part of a collection. In EVM based NFT platforms collections are represented as a single contract that manages the collection and keeps the ownership record of the NFTs within collection. Since L1 NFTs in IOTA are represented as UTXOs in the ledger rather than smart contracts, minting a collection is conceptually different. + +### Minting L1 NFT Collections + +The idea is to tie NFTs (individual UTXOs) within one collection together via the `immutableIssuer` property. +The collection itself is represented by a special NFT output, the `Collection NFT`. It holds information about the properties of the collection and when included in transactions, it can mint the NFTs within the collection where `immutableIssuer` becomes the `nftId` of the `Collection NFT`. + +It is possible to timelock the `Collection NFT` on protocol level to prevent minting of the NFTs for a certain time period. It is also possible send the `Collection NFT` to the zero address, or burn it al together, which essentially means that the collection is locked forever, no more collection items can ever be minted. +It is not possible to define on protocol level how many items can the `Collection NFT` can mint, unless it is deposited into a L2 smart contract that manages issuer rights. + +Let's look at a practical example on how the process looks like. + +### 1. Minting the Collection NFT + +The issuer mints an NFT output on L1 with the following properties: + - `nftId`: a unique identifier of the NFT output assigned by the protocol upon minting. This will become the `collectionId`. + - `immutableIssuer`: L1 address of the minting artist. Can be used to prove authenticity of the collection. + - `immutableMetadata`: binary blob of data defined upon minting by the issuer. This will become the `collectionMetadata`. + +The issuer of the `Collection NFT` defines the collection metadata according to IRC-27 standard. In our example, `collectionMetadata` is a JSON object: +```json +{ + "standard": "IRC27", + "version": "v1.0", + "type": "text/html", + "uri": "https://my-awesome-nft-project.com", + "name": "My Awesome NFT Collection", + "issuerName": "Me" +} +``` + - The binary blob of this JSON object is put in the `immutableMetadata` field of the NFT output. + +### 2. Minting NFTs within the collection + +The issuer includes the `Collection NFT` in a transaction that mints NFTs within the collection. The number of mintable NFTs in one transaction is defined by the protocol as the [Max Outputs Count defined in TIP-22](https://github.com/Wollac/protocol-rfcs/blob/protocol-parameters/tips/TIP-0022/tip-0022.md#detailed-design) - 1 (the `Collection NFT` is also part of the outputs). + +The minted NFTs will have the following properties: + - `nftId`: the unique identifier of the NFT output assigned by the protocol upon minting. + - `immutableIssuer`: is set as `collectionId` of the `Collection NFT`. This unique value identifies which collection the NFT belongs to. + - `immutableMetadata`: metadata for the individual NFT. Binary blob of an IRC-27 compliant JSON object. + +For item 4 for example, the metadata is: +```json +{ + "standard": "IRC27", + "version": "v1.0", + "type": "image/gif", + "uri": "https://my-awesome-nft-project.com/item-4.gif", + "name": "#4 My Awesome NFT", + "issuerName": "Me", + "royalties": { + "smr1q5948....": 0.05 + }, + "collectionName": "My Awesome NFT Collection", + "attributes": [ + { + "trait_type": "awesomeness", + "value": 60 + } +] +} +``` + + +In case the metadata is not stored in the NFT output but is hosted off-chain, it is still recommended to include information in the `immutableMetadata` field such that clients are able to locate it. The `immutableMetadata` json object would look something like: + +```json +{ +"standard": "IRC27", +"version": "v1.0", +"type": "application/json", +"uri": "https://my-awesome-nft-project.com/{id}.json", +"name": "#4 My Awesome NFT" +} +``` + + - Note the {id} substitution in the `uri` field. Clients should replace `id` with the `nftId` property of the NFT output, without the `0x` prefix. + +### 3. Fetching content of a collection + +Since each NFT within the collection has the `immutableIssuer` set as the `collectionId`, it is possible to query the +UTXO Indexer API ([TIP-26](https://github.com/iotaledger/tips/blob/indexer-api/tips/TIP-0026/tip-0026.md)) to get the outputIds for all +NFTs that belong to the collection. Then the output objects can be fetched via the Core API ([TIP-25](https://github.com/iotaledger/tips/blob/stardust-api/tips/TIP-0025/tip-0025.md)): + - GET `/api/indexer/v1/outputs/nft?issuer=collectionId` returns the outputIds that have `collectionId` in the `issuer` field. + - GET `/api/core/v2/outputs/{outputId}` returns the NFT output itself. + +The Collection NFT can be fetched via: + - GET `/api/indexer/v1/outputs/nfts/{collectionId}` returns the outputId of the Collection NFT (if not burnt). + - GET `/api/core/v2/outputs/{outputId}` returns the Collection NFT output itself. + +## Rationale + +### Interoperable Standards + +For a unified IOTA NFT ecosystem the standards have been designed to support ease of integration and cross-compatibility of NFTs throughout the IOTA network. Observations of undefined standards in other ecosystems has illustrated the importance of such developments in the early stages of the technology. Simple defined keys such as `uri`, instead of `nftUrl` or `fileLocation` can support a much more interoperable experience for creators and dApp developers with everyone working from the same foundations. + +Supporting creators is also a key element in driving adoption for the technology, royalty integrations vary substantially in other blockchain ecosystems which remains a challenge for both 3rd party applications and creators in sustaining a consistent and reliable ecosystem across different applications. + +This standard also supports expansion, backwards compatibility, and a universal guideline for the ecosystem to develop with. Allowing an immediate interoperable environment that can support ease-of-adoption in the early stages of IOTA NFTs, whilst continually supporting feature expansion and future development. + +## Backwards Compatibility + +### Versioning + +Expanding use-cases in the NFT space will present multiple requirements for different standards and schemas to be developed and over time alterations and updates will be presented to support an evolving technology and future developments. + +Version is introduced from the start to allow dApp developers and creators to maintain backwards compatibility with differing versions of the standard, which can be defined as a numeric value proceeded with the letter v. All future versions will be submitted as separate TIPs. + +Current version `v1.0` + +### Modular Structure Expansion + +A modular structure to the standard has been created to support use case expansion, file type extension, standards catalogue. Allowing creators to utilise minimalist implementations as well as the more advanced expanded standards. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).