From 459090e3325d3ac7de3b162096bf43c589d82aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 14:44:51 +0100 Subject: [PATCH 1/7] initial draft --- sips/sip-012-nft-metadata.md | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 sips/sip-012-nft-metadata.md diff --git a/sips/sip-012-nft-metadata.md b/sips/sip-012-nft-metadata.md new file mode 100644 index 00000000..02fd3b25 --- /dev/null +++ b/sips/sip-012-nft-metadata.md @@ -0,0 +1,105 @@ +# Preamble + +SIP Number: 012 + +Title: Standard Trait Definition for Metadata for Non-Fungible Tokens + +Author: Friedger Müffke (mail@friedger.de) + +Consideration: Technical + +Type: Standard + +Status: Draft + +Created: 17 March 2021 + +License: CC0-1.0 + +Sign-off: + +# Abstract + +Non-fungible tokens or NFTs are digital assets registered on blockchain with unique identifiers and properties that distinguish them from each other. SIP-009 defines the trait how ownership of an NFT is managed. This SIP aims to provide a flexible and easy-to-implement standard that can be used by developers mainly of wallet when managing NFTs. + +# License and Copyright + +This SIP is made available under the terms of the Creative Commons CC0 1.0 Universal license, available at https://creativecommons.org/publicdomain/zero/1.0/ +This SIP’s copyright is held by the Stacks Open Internet Foundation. + +# Introduction + +Tokens are digital assets registered on blockchain through a smart contract. A non-fungible token (NFT) is a token that is globally unique and can be identified through its unique identifier. In blockchains with smart contracts, including the Stacks blockchain, developers and users can use smart contracts to register and interact with non-fungible tokens. + +Some use cases of NFTs are name registration, digital art, certification, media and enternainment, real-estate. They all require that users associate certain content with an NFT. In general, it is helpful for the users to have a name, sound, image that represents this content. + +# Specification + +Every SIP-012 compliant smart contract in Stacks blockchain must implement the trait, `nft-meta`, defined in the [Trait](#trait) section and must meet the requirements for the following functions: + +### Metadata of Defining Smart Contract + +`(get-nft-meta () (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint))` + +Takes no arguments and returns a response containing the name and URL of an image representing the class of NFTs defined by this contract. + +This function must never return an error response. It can be defined as read-only, i.e. `define-read-only`. + +### Metadata of NFT + +`(get-meta (uint) (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint))` + +Takes no arguments and returns a response containing the name and URL of an image representing the class of NFTs defined by this contract. The data uple must be wrapped in an `optional`. If the corresponding NFT doesn't exist or the contract doesn't maintain metadata, the response must be `(ok none)`. If a valid URI exists for the NFT, the response must be `(ok (some metadata))`. + +This function must never return an error response. It can be defined as read-only, i.e. `define-read-only`. + +## Trait + +``` +(define-trait nft-meta + ( + ;; Metadata of NFT class + (get-meta (uint) (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint)) + + + ;; Metadata of individual NFT + (get-meta (uint) (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint)) + ) +) +``` + +# Using NFTs in applications + +Developers who wish to represent non-fungible tokens in an application should first be provided, or keep track of, various different non-fungible token implementations. When validating metadata of a non-fungible token contract, they should fetch the interface and/or source code for that contract. If the contract implements the trait, then the application can use this standard's contract interface for making transfers and getting other details defined in this standard. Furthermore, the received metadata should be verified whether they are compliant with the applications guidelines. + +All of the functions in this trait return the `response` type, which is a requirement of trait definitions in Clarity. All of these functions should be "fail-proof", in the sense that they should never return an error. These "fail-proof" functions are those that have been recommended as read-only. If a contract that implements this trait returns an error for these functions, it may be an indication of a non-compliant contract, and consumers of those contracts should proceed with caution. + +We remind implementation authors that the empty string is a valid response to name and image if you don't want to supply parts of the metadata. We also remind everyone that any smart contract can use the same name and image as your contract. How a client may determine which smart contracts are well-known (canonical) is outside the scope of this standard. + +# Related Work + +NFTs are an established asset class on blockchains. Read for example [here](https://www.ledger.com/academy/what-are-nft). + +## BNS +The Blockchain Naming System uses native non-fungible tokens. It does define metadata for a name through attachements. The schema for names owned by a person follows the definition of (schema.org/Person)[https://schema.org/Person]. This could be an alternative to token URIs. + +## SIP 9 +An NFT is defined in SIP-009. + +## EIP 721 +Metadata for NFTs on Ethereum are defined in [EIP 721](https://eips.ethereum.org/EIPS/eip-721). Compliant smart contracts have to implement a `name` and `symbol` function as human readable identifiers, as well as `tokenURI` for access to the actual metadat. The schema of the metadata contains name, description and image. For NFTs on the Stacks blockchain, a name for the nft is already defined by the underlying native asset. Therefore, `name` and `symbol` is not needed. + +# Backwards Compatibility + +## Boom +The NFT contract for Boom implements a variation of this trait using similar naming, but returning other types than response types: https://explorer.stacks.co/txid/0x423d113e14791f5d60f5efbee19bbb05cf5e68d84bcec4e611f2c783b08d05aa?chain=mainnet + +# Activation + +This SIP is activated if 5 contracts are deployed that use the same trait that follows this specification. This must happen before Bitcoin tip #700,000. + +# Reference Implementations + +## Source code + +// TODO \ No newline at end of file From 30d05a69bf04bbf611050d9deee32410b2f419b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 14:49:18 +0100 Subject: [PATCH 2/7] fix trait --- sips/sip-012-nft-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/sip-012-nft-metadata.md b/sips/sip-012-nft-metadata.md index 02fd3b25..3032fd40 100644 --- a/sips/sip-012-nft-metadata.md +++ b/sips/sip-012-nft-metadata.md @@ -59,7 +59,7 @@ This function must never return an error response. It can be defined as read-onl (define-trait nft-meta ( ;; Metadata of NFT class - (get-meta (uint) (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint)) + (get-nft-meta (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint)) ;; Metadata of individual NFT From ff9e0d5e8c79f99fc38ac89d0d85d793385af05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 14:50:05 +0100 Subject: [PATCH 3/7] use power of 2 for length --- sips/sip-012-nft-metadata.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sips/sip-012-nft-metadata.md b/sips/sip-012-nft-metadata.md index 3032fd40..9df4eb69 100644 --- a/sips/sip-012-nft-metadata.md +++ b/sips/sip-012-nft-metadata.md @@ -59,11 +59,11 @@ This function must never return an error response. It can be defined as read-onl (define-trait nft-meta ( ;; Metadata of NFT class - (get-nft-meta (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint)) + (get-nft-meta (response (optional {name: (string-uft8 32), image: (string-ascii 256)}) uint)) ;; Metadata of individual NFT - (get-meta (uint) (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint)) + (get-meta (uint) (response (optional {name: (string-uft8 32), image: (string-ascii 256)}) uint)) ) ) ``` From a1a2f3899117d2681d87e114193922276a5184b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 14:56:17 +0100 Subject: [PATCH 4/7] add boom details --- sips/sip-012-nft-metadata.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sips/sip-012-nft-metadata.md b/sips/sip-012-nft-metadata.md index 9df4eb69..125a290c 100644 --- a/sips/sip-012-nft-metadata.md +++ b/sips/sip-012-nft-metadata.md @@ -94,6 +94,10 @@ Metadata for NFTs on Ethereum are defined in [EIP 721](https://eips.ethereum.org ## Boom The NFT contract for Boom implements a variation of this trait using similar naming, but returning other types than response types: https://explorer.stacks.co/txid/0x423d113e14791f5d60f5efbee19bbb05cf5e68d84bcec4e611f2c783b08d05aa?chain=mainnet +The function signatures for metadata are: +* `(get-boom-meta () {uri: (string-ascii 35), name: (string-ascii 16), mime-type: (string-ascii 9)})` and +* `(get-meta? uint {series-id: uint, number: uint, name: (string-utf8 80), uri: (string-ascii 2048), mime-type: (string-ascii 129), hash: (buff 64)})` + # Activation This SIP is activated if 5 contracts are deployed that use the same trait that follows this specification. This must happen before Bitcoin tip #700,000. From c93ca00ff549c3fd27eab2807e50cfd137e29739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 15:12:08 +0100 Subject: [PATCH 5/7] add alternative get-meta, add badges contract --- sips/sip-012-nft-metadata.md | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/sips/sip-012-nft-metadata.md b/sips/sip-012-nft-metadata.md index 125a290c..f31764c3 100644 --- a/sips/sip-012-nft-metadata.md +++ b/sips/sip-012-nft-metadata.md @@ -49,6 +49,8 @@ This function must never return an error response. It can be defined as read-onl `(get-meta (uint) (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint))` +`(get-meta-ext (uint) (response (optional {name: (string-uft8 32), uri: (string-ascii 256), mime-type: (string-ascii 129)}) uint))` + Takes no arguments and returns a response containing the name and URL of an image representing the class of NFTs defined by this contract. The data uple must be wrapped in an `optional`. If the corresponding NFT doesn't exist or the contract doesn't maintain metadata, the response must be `(ok none)`. If a valid URI exists for the NFT, the response must be `(ok (some metadata))`. This function must never return an error response. It can be defined as read-only, i.e. `define-read-only`. @@ -64,10 +66,17 @@ This function must never return an error response. It can be defined as read-onl ;; Metadata of individual NFT (get-meta (uint) (response (optional {name: (string-uft8 32), image: (string-ascii 256)}) uint)) + + ;; Metadata of individual NFT (alternative version - to be discussed) + (get-meta-ext (uint) (response (optional {name: (string-uft8 32), uri: (string-ascii 256), mime-type: (string-ascii 129)}) uint)) ) ) ``` +## Specification for SIP-009 compliant smart contracts +An NFT is defined in SIP-009. // TODO add link +That standard defines a token URI that returns metadata. If this trait is implemented by a smart contract compliant to SIP-009, then the metadata returned by `get-token-uri` must match the returned metadata of `get-meta` function. + # Using NFTs in applications Developers who wish to represent non-fungible tokens in an application should first be provided, or keep track of, various different non-fungible token implementations. When validating metadata of a non-fungible token contract, they should fetch the interface and/or source code for that contract. If the contract implements the trait, then the application can use this standard's contract interface for making transfers and getting other details defined in this standard. Furthermore, the received metadata should be verified whether they are compliant with the applications guidelines. @@ -76,6 +85,10 @@ All of the functions in this trait return the `response` type, which is a requir We remind implementation authors that the empty string is a valid response to name and image if you don't want to supply parts of the metadata. We also remind everyone that any smart contract can use the same name and image as your contract. How a client may determine which smart contracts are well-known (canonical) is outside the scope of this standard. +Furthermore, accessiblity and localization of content is not covered by the standard. + +Note, that metadata can change over time. + # Related Work NFTs are an established asset class on blockchains. Read for example [here](https://www.ledger.com/academy/what-are-nft). @@ -83,21 +96,25 @@ NFTs are an established asset class on blockchains. Read for example [here](http ## BNS The Blockchain Naming System uses native non-fungible tokens. It does define metadata for a name through attachements. The schema for names owned by a person follows the definition of (schema.org/Person)[https://schema.org/Person]. This could be an alternative to token URIs. -## SIP 9 -An NFT is defined in SIP-009. - ## EIP 721 Metadata for NFTs on Ethereum are defined in [EIP 721](https://eips.ethereum.org/EIPS/eip-721). Compliant smart contracts have to implement a `name` and `symbol` function as human readable identifiers, as well as `tokenURI` for access to the actual metadat. The schema of the metadata contains name, description and image. For NFTs on the Stacks blockchain, a name for the nft is already defined by the underlying native asset. Therefore, `name` and `symbol` is not needed. # Backwards Compatibility ## Boom -The NFT contract for Boom implements a variation of this trait using similar naming, but returning other types than response types: https://explorer.stacks.co/txid/0x423d113e14791f5d60f5efbee19bbb05cf5e68d84bcec4e611f2c783b08d05aa?chain=mainnet +The [NFT contract for Boom](https://explorer.stacks.co/txid/0x423d113e14791f5d60f5efbee19bbb05cf5e68d84bcec4e611f2c783b08d05aa?chain=mainnet) implements a variation of this trait using similar naming, but returning other types than response types. The function signatures for metadata are: * `(get-boom-meta () {uri: (string-ascii 35), name: (string-ascii 16), mime-type: (string-ascii 9)})` and * `(get-meta? uint {series-id: uint, number: uint, name: (string-utf8 80), uri: (string-ascii 2048), mime-type: (string-ascii 129), hash: (buff 64)})` +## Badges +The [badges contract](https://explorer.stacks.co/txid/0xb874ddbb4a602c22bb5647c7a2f8bfcafbbca7c0c663a175f2270ef3665f33de?chain=mainnet) defines metadata for nfts. + +The function signatures for metadata are: +* `(get-badge-meta () {uri: (string-ascii 78111)})` and +* `(get-meta? (uint) (optional {user: principal}))` + # Activation This SIP is activated if 5 contracts are deployed that use the same trait that follows this specification. This must happen before Bitcoin tip #700,000. From b93aebde5cdf0c4af3a468dff1ede7127650c075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 15:17:26 +0100 Subject: [PATCH 6/7] clarify alternative --- sips/sip-012-nft-metadata.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sips/sip-012-nft-metadata.md b/sips/sip-012-nft-metadata.md index f31764c3..e859f997 100644 --- a/sips/sip-012-nft-metadata.md +++ b/sips/sip-012-nft-metadata.md @@ -47,9 +47,11 @@ This function must never return an error response. It can be defined as read-onl ### Metadata of NFT -`(get-meta (uint) (response (optional {name: (string-uft8 30), image: (string-ascii 255)}) uint))` +`(get-meta (uint) (response (optional {name: (string-uft8 32), image: (string-ascii 256)}) uint))` -`(get-meta-ext (uint) (response (optional {name: (string-uft8 32), uri: (string-ascii 256), mime-type: (string-ascii 129)}) uint))` +alternative: + +`(get-meta (uint) (response (optional {name: (string-uft8 32), uri: (string-ascii 256), mime-type: (string-ascii 129)}) uint))` Takes no arguments and returns a response containing the name and URL of an image representing the class of NFTs defined by this contract. The data uple must be wrapped in an `optional`. If the corresponding NFT doesn't exist or the contract doesn't maintain metadata, the response must be `(ok none)`. If a valid URI exists for the NFT, the response must be `(ok (some metadata))`. @@ -63,12 +65,11 @@ This function must never return an error response. It can be defined as read-onl ;; Metadata of NFT class (get-nft-meta (response (optional {name: (string-uft8 32), image: (string-ascii 256)}) uint)) - ;; Metadata of individual NFT - (get-meta (uint) (response (optional {name: (string-uft8 32), image: (string-ascii 256)}) uint)) + (get-meta (uint) (response (optional {name: (string-uft8 32), uri: (string-ascii 256), mime-type: (string-ascii 129)}) uint)) ;; Metadata of individual NFT (alternative version - to be discussed) - (get-meta-ext (uint) (response (optional {name: (string-uft8 32), uri: (string-ascii 256), mime-type: (string-ascii 129)}) uint)) + (get-meta (uint) (response (optional {name: (string-uft8 32), image: (string-ascii 256)}) uint)) ) ) ``` From 601f68f21dc852b80c31f73f7ceb0f6225d27f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 17:24:12 +0100 Subject: [PATCH 7/7] use uri and mime-type instead of image --- sips/sip-012-nft-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/sip-012-nft-metadata.md b/sips/sip-012-nft-metadata.md index e859f997..6ef4ca3d 100644 --- a/sips/sip-012-nft-metadata.md +++ b/sips/sip-012-nft-metadata.md @@ -63,7 +63,7 @@ This function must never return an error response. It can be defined as read-onl (define-trait nft-meta ( ;; Metadata of NFT class - (get-nft-meta (response (optional {name: (string-uft8 32), image: (string-ascii 256)}) uint)) + (get-nft-meta (response (optional {name: (string-uft8 32), uri: (string-ascii 256), mime-type: (string-ascii 129)}) uint)) ;; Metadata of individual NFT (get-meta (uint) (response (optional {name: (string-uft8 32), uri: (string-ascii 256), mime-type: (string-ascii 129)}) uint))