From ef4e78569fa685af361fb4f06074b7a53170df0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Thu, 10 Dec 2020 19:05:59 +0100 Subject: [PATCH 01/21] Create nft-standard.md --- nft-standard.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 nft-standard.md diff --git a/nft-standard.md b/nft-standard.md new file mode 100644 index 00000000..cd63f225 --- /dev/null +++ b/nft-standard.md @@ -0,0 +1,64 @@ +## Preamble +* Sip Number: 009 +* Title: Standard Trait Definition for Non-Fungibe Tokens +* Author: Friedger Müffke (mail@friedger.de) +* Consideration: Technical +* Type: Standard +* Status: Draft +* Created: 2020-12-10 +* License: CC0-1.0 +* Sign-off: + +## Abstract +Non-fungible token are unique digital assets that are registered on the Stacks blockchain through a smart contract with certain properties. +Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have more properties +that are not specified in this standard. + +## 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 + + +## Specification + +``` + +(define-trait stacks-token-nft-standard-v1 + ( + ;; Name, limited to 32 chars + (name () (string-ascii 32)) + + ;; Icon URL, limited to 2048 chars + (icon-url () (string-ascii 2048)) + + ;; Token ID, limited to uint range + (last-token-id () uint) + + ;; number of tokens owned by user + (get-balance (principal) uint) + + ;; Owner of given token identifier + (get-owner? (uint) (response principal (tuple (kind (string-ascii 32))))) + + ;; Transfer from to + (transfer? (principal principal uint) (response bool (tuple (kind (string-ascii 32))))) + + ;; Approve + (approve (principal uint) (response bool (tuple (kind (string-ascii 32))))) + + ;; Set approval for all + (set-approval-for-all (principal bool) (response bool (tuple (kind (string-ascii 32))))) + + ) +) +``` +## Related Work + +## Backwards Compatibility + +## Activation + +## Reference Implementations From c978f091e1ae948bb73bf121ffe753a2de350d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Mon, 4 Jan 2021 17:31:33 +0100 Subject: [PATCH 02/21] Update nft-standard.md --- nft-standard.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/nft-standard.md b/nft-standard.md index cd63f225..34f8974e 100644 --- a/nft-standard.md +++ b/nft-standard.md @@ -20,7 +20,9 @@ This SIP is made available under the terms of the Creative Commons CC0 1.0 Unive This SIP’s copyright is held by the Stacks Open Internet Foundation. ## Introduction +Tokens are digital assets that are registered on the Stacks blockchain through a smart contract. A non-fungible token (NFT) is a token that is globally unique and that users can identify through its identifier. The smart contract that registers the NFTs defines a name for the group of NFTs. +NFTs are enumerated, the id starts at 1 and the current last id is provided by the smart contract. ## Specification @@ -31,8 +33,11 @@ This SIP’s copyright is held by the Stacks Open Internet Foundation. ;; Name, limited to 32 chars (name () (string-ascii 32)) - ;; Icon URL, limited to 2048 chars + ;; Icon URL for group of NFTs, limited to 2048 chars (icon-url () (string-ascii 2048)) + + ;; Icon URL per NFT , limited to 2048 chars + (icon-url (uint) (string-ascii 2048)) ;; Token ID, limited to uint range (last-token-id () uint) @@ -57,8 +62,14 @@ This SIP’s copyright is held by the Stacks Open Internet Foundation. ``` ## Related Work +https://eips.ethereum.org/EIPS/eip-721 +https://www.ledger.com/academy/what-are-nft + + ## Backwards Compatibility +Not applicable + ## Activation ## Reference Implementations From 96a87f80700ca8fdd25926e7f8307dcdfbd08d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Mon, 4 Jan 2021 17:46:38 +0100 Subject: [PATCH 03/21] Update nft-standard.md --- nft-standard.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nft-standard.md b/nft-standard.md index 34f8974e..f0de31fa 100644 --- a/nft-standard.md +++ b/nft-standard.md @@ -46,16 +46,16 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t (get-balance (principal) uint) ;; Owner of given token identifier - (get-owner? (uint) (response principal (tuple (kind (string-ascii 32))))) + (get-owner? (uint) (response principal (tuple (kind (string-ascii 32)) (code uint)))) ;; Transfer from to - (transfer? (principal principal uint) (response bool (tuple (kind (string-ascii 32))))) + (transfer? (principal principal uint) (response bool (tuple (kind (string-ascii 32)) (code uint)))) ;; Approve - (approve (principal uint) (response bool (tuple (kind (string-ascii 32))))) + (approve (principal uint) (response bool (tuple (kind (string-ascii 32)) (code uint)))) ;; Set approval for all - (set-approval-for-all (principal bool) (response bool (tuple (kind (string-ascii 32))))) + (set-approval-for-all (principal bool) (response bool (tuple (kind (string-ascii 32)) (code uint)))) ) ) From ca68556cbb3e6464f51017d999912b483782fdca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Tue, 5 Jan 2021 14:07:14 +0100 Subject: [PATCH 04/21] move sip --- .../sips/sip-009-nft-standard.md | 52 +++++++------------ 1 file changed, 19 insertions(+), 33 deletions(-) rename nft-standard.md => sips/sips/sip-009-nft-standard.md (60%) diff --git a/nft-standard.md b/sips/sips/sip-009-nft-standard.md similarity index 60% rename from nft-standard.md rename to sips/sips/sip-009-nft-standard.md index f0de31fa..84404149 100644 --- a/nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -1,17 +1,19 @@ ## Preamble -* Sip Number: 009 -* Title: Standard Trait Definition for Non-Fungibe Tokens -* Author: Friedger Müffke (mail@friedger.de) -* Consideration: Technical -* Type: Standard -* Status: Draft -* Created: 2020-12-10 -* License: CC0-1.0 -* Sign-off: + +- Sip Number: 009 +- Title: Standard Trait Definition for Non-Fungibe Tokens +- Author: Friedger Müffke (mail@friedger.de) +- Consideration: Technical +- Type: Standard +- Status: Draft +- Created: 2020-12-10 +- License: CC0-1.0 +- Sign-off: ## Abstract -Non-fungible token are unique digital assets that are registered on the Stacks blockchain through a smart contract with certain properties. -Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have more properties + +Non-fungible token are unique digital assets that are registered on the Stacks blockchain through a smart contract with certain properties. +Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have more properties that are not specified in this standard. ## License and Copyright @@ -20,6 +22,7 @@ This SIP is made available under the terms of the Creative Commons CC0 1.0 Unive This SIP’s copyright is held by the Stacks Open Internet Foundation. ## Introduction + Tokens are digital assets that are registered on the Stacks blockchain through a smart contract. A non-fungible token (NFT) is a token that is globally unique and that users can identify through its identifier. The smart contract that registers the NFTs defines a name for the group of NFTs. NFTs are enumerated, the id starts at 1 and the current last id is provided by the smart contract. @@ -30,46 +33,29 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t (define-trait stacks-token-nft-standard-v1 ( - ;; Name, limited to 32 chars - (name () (string-ascii 32)) - - ;; Icon URL for group of NFTs, limited to 2048 chars - (icon-url () (string-ascii 2048)) - - ;; Icon URL per NFT , limited to 2048 chars - (icon-url (uint) (string-ascii 2048)) - ;; Token ID, limited to uint range - (last-token-id () uint) - - ;; number of tokens owned by user - (get-balance (principal) uint) + (last-token-id () uint) ;; Owner of given token identifier - (get-owner? (uint) (response principal (tuple (kind (string-ascii 32)) (code uint)))) + (get-owner? (uint) (optional principal)) ;; Transfer from to (transfer? (principal principal uint) (response bool (tuple (kind (string-ascii 32)) (code uint)))) - - ;; Approve - (approve (principal uint) (response bool (tuple (kind (string-ascii 32)) (code uint)))) - - ;; Set approval for all - (set-approval-for-all (principal bool) (response bool (tuple (kind (string-ascii 32)) (code uint)))) - ) ) ``` + ## Related Work https://eips.ethereum.org/EIPS/eip-721 https://www.ledger.com/academy/what-are-nft - ## Backwards Compatibility Not applicable ## Activation +This SIP is activated as soon as 5 contracts are using the same trait that follows this specification. + ## Reference Implementations From 98b2f036980ec2844de88adafddc04dfad98373c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Tue, 5 Jan 2021 14:19:42 +0100 Subject: [PATCH 05/21] fix order in transfer --- sips/sips/sip-009-nft-standard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 84404149..699aab67 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -40,7 +40,7 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t (get-owner? (uint) (optional principal)) ;; Transfer from to - (transfer? (principal principal uint) (response bool (tuple (kind (string-ascii 32)) (code uint)))) + (transfer? (uint principal principal) (response bool (tuple (kind (string-ascii 32)) (code uint)))) ) ) ``` From 7b6a919e1bfca10b2a191faa7c4e17dedfb4cdbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Tue, 5 Jan 2021 14:33:33 +0100 Subject: [PATCH 06/21] add implementation rules --- sips/sips/sip-009-nft-standard.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 699aab67..9572dd3f 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -29,6 +29,10 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t ## Specification +NFT smart contract shall implement the trait defined at `ST2NM3E9MAWWRNGFEKW75QR4XXVA856N4MHNMYA3T.stacks-token-nft-standard-v1` as well as satisfy the additional conditions. + +### Trait + ``` (define-trait stacks-token-nft-standard-v1 @@ -45,6 +49,12 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t ) ``` +### Implementation rules + +1. Contracts must use a least one nft asset. A post condition with deny mode and without any nft condition about a changed owner must fail for `transfer?` function calls. +1. After a successfull call to function `transfer?` the function `get-owner?` must return the recipient of the `transfer?` call as the new owner. +1. If a call to function `get-owner?` returns some principal `A` value then it must return the same value until `transfer?` is called with principal `A` as a sender + ## Related Work https://eips.ethereum.org/EIPS/eip-721 @@ -56,6 +66,6 @@ Not applicable ## Activation -This SIP is activated as soon as 5 contracts are using the same trait that follows this specification. +This SIP is activated as soon as 5 contracts are deployed that are using the same trait that follows this specification. ## Reference Implementations From 5f6cbd0105aeececf1daaab7a4defda7e545bc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 20 Jan 2021 11:36:23 +0100 Subject: [PATCH 07/21] fix response type, add error rules, add impl --- sips/sips/sip-009-nft-standard.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 9572dd3f..24d00b6f 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -34,14 +34,13 @@ NFT smart contract shall implement the trait defined at `ST2NM3E9MAWWRNGFEKW75QR ### Trait ``` - (define-trait stacks-token-nft-standard-v1 ( ;; Token ID, limited to uint range - (last-token-id () uint) + (last-token-id () (response uint uint)) ;; Owner of given token identifier - (get-owner? (uint) (optional principal)) + (get-owner? (uint) (response (optional principal) uint)) ;; Transfer from to (transfer? (uint principal principal) (response bool (tuple (kind (string-ascii 32)) (code uint)))) @@ -54,6 +53,11 @@ NFT smart contract shall implement the trait defined at `ST2NM3E9MAWWRNGFEKW75QR 1. Contracts must use a least one nft asset. A post condition with deny mode and without any nft condition about a changed owner must fail for `transfer?` function calls. 1. After a successfull call to function `transfer?` the function `get-owner?` must return the recipient of the `transfer?` call as the new owner. 1. If a call to function `get-owner?` returns some principal `A` value then it must return the same value until `transfer?` is called with principal `A` as a sender +1. The following error codes are defined + +| function | error | description | +|----------|-------|-------------| +|`transfer?`|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| ## Related Work @@ -69,3 +73,5 @@ Not applicable This SIP is activated as soon as 5 contracts are deployed that are using the same trait that follows this specification. ## Reference Implementations + +https://github.com/friedger/clarity-smart-contracts/blob/master/contracts/sips/nft-trait.clar From 660ba1b88ad7b4012c0cc3eb7a8914bcb7a7b771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Thu, 21 Jan 2021 01:10:12 +0100 Subject: [PATCH 08/21] Add reference implementation, more details for functions --- sips/sips/sip-009-nft-standard.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 24d00b6f..d9a4f2e5 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -29,7 +29,11 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t ## Specification -NFT smart contract shall implement the trait defined at `ST2NM3E9MAWWRNGFEKW75QR4XXVA856N4MHNMYA3T.stacks-token-nft-standard-v1` as well as satisfy the additional conditions. +NFT smart contract shall implement the trait defined at `ST314JC8J24YWNVAEJJHQXS5Q4S9DX1FW5Z9DK9NT.nft-trait` as well as satisfy the additional conditions. +The trait has three functions: +* `last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any nft. This is the upper limit when iterating through all nfts. +* `get-owner?` takes an nft identifier and returns a response containing the principal owning the nft for the given identifier. The principal is wrapped as an optional, that means if the corresponding nft does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. +* `transfer?` takes an nft identifier, a sender principal and a receiver principal. The function changes the ownership of the nft for the given identifier. The change has to be reflected in the `get-owner?` function, for details see implementation rules. ### Trait @@ -53,6 +57,7 @@ NFT smart contract shall implement the trait defined at `ST2NM3E9MAWWRNGFEKW75QR 1. Contracts must use a least one nft asset. A post condition with deny mode and without any nft condition about a changed owner must fail for `transfer?` function calls. 1. After a successfull call to function `transfer?` the function `get-owner?` must return the recipient of the `transfer?` call as the new owner. 1. If a call to function `get-owner?` returns some principal `A` value then it must return the same value until `transfer?` is called with principal `A` as a sender +1. For any call to `get-owner?`, resp. `transfer` with an id greater than `last-token-id`, the call should return a response `none`, resp. failed transfer. 1. The following error codes are defined | function | error | description | @@ -74,4 +79,9 @@ This SIP is activated as soon as 5 contracts are deployed that are using the sam ## Reference Implementations +Source code https://github.com/friedger/clarity-smart-contracts/blob/master/contracts/sips/nft-trait.clar + +Deployment on testnet + +https://explorer.stacks.co/txid/0x071d557c0edcb118203d2900b81aaf5bd9e5f52ec002b305ea34a13d419c7441?chain=testnet From 629a976d2be337d920b41b98dd2da95fe2b79863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Tue, 26 Jan 2021 12:34:07 +0100 Subject: [PATCH 09/21] Remove ? from function names --- sips/sips/sip-009-nft-standard.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index d9a4f2e5..6a19d79c 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -32,8 +32,8 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t NFT smart contract shall implement the trait defined at `ST314JC8J24YWNVAEJJHQXS5Q4S9DX1FW5Z9DK9NT.nft-trait` as well as satisfy the additional conditions. The trait has three functions: * `last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any nft. This is the upper limit when iterating through all nfts. -* `get-owner?` takes an nft identifier and returns a response containing the principal owning the nft for the given identifier. The principal is wrapped as an optional, that means if the corresponding nft does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. -* `transfer?` takes an nft identifier, a sender principal and a receiver principal. The function changes the ownership of the nft for the given identifier. The change has to be reflected in the `get-owner?` function, for details see implementation rules. +* `get-owner` takes an nft identifier and returns a response containing the principal owning the nft for the given identifier. The principal is wrapped as an optional, that means if the corresponding nft does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. +* `transfer` takes an nft identifier, a sender principal and a receiver principal. The function changes the ownership of the nft for the given identifier. The change has to be reflected in the `get-owner` function, for details see implementation rules. ### Trait @@ -44,25 +44,25 @@ The trait has three functions: (last-token-id () (response uint uint)) ;; Owner of given token identifier - (get-owner? (uint) (response (optional principal) uint)) + (get-owner (uint) (response (optional principal) uint)) ;; Transfer from to - (transfer? (uint principal principal) (response bool (tuple (kind (string-ascii 32)) (code uint)))) + (transfer (uint principal principal) (response bool (tuple (kind (string-ascii 32)) (code uint)))) ) ) ``` ### Implementation rules -1. Contracts must use a least one nft asset. A post condition with deny mode and without any nft condition about a changed owner must fail for `transfer?` function calls. -1. After a successfull call to function `transfer?` the function `get-owner?` must return the recipient of the `transfer?` call as the new owner. -1. If a call to function `get-owner?` returns some principal `A` value then it must return the same value until `transfer?` is called with principal `A` as a sender -1. For any call to `get-owner?`, resp. `transfer` with an id greater than `last-token-id`, the call should return a response `none`, resp. failed transfer. +1. Contracts must use a least one nft asset. A post condition with deny mode and without any nft condition about a changed owner must fail for `transfer` function calls. +1. After a successfull call to function `transfer` the function `get-owner` must return the recipient of the `transfer` call as the new owner. +1. If a call to function `get-owner` returns some principal `A` value then it must return the same value until `transfer` is called with principal `A` as a sender +1. For any call to `get-owner`, resp. `transfer` with an id greater than `last-token-id`, the call should return a response `none`, resp. failed transfer. 1. The following error codes are defined | function | error | description | |----------|-------|-------------| -|`transfer?`|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| +|`transfer`|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| ## Related Work @@ -82,6 +82,4 @@ This SIP is activated as soon as 5 contracts are deployed that are using the sam Source code https://github.com/friedger/clarity-smart-contracts/blob/master/contracts/sips/nft-trait.clar -Deployment on testnet - -https://explorer.stacks.co/txid/0x071d557c0edcb118203d2900b81aaf5bd9e5f52ec002b305ea34a13d419c7441?chain=testnet +Deployment on testnet: TODO From 76778eaff3647e9303086fab76fcb2b6027a5c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Tue, 23 Feb 2021 18:00:42 +0100 Subject: [PATCH 10/21] Update sip-009-nft-standard.md --- sips/sips/sip-009-nft-standard.md | 60 +++++++++++++++++-------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 6a19d79c..4ad7ca52 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -1,41 +1,49 @@ -## Preamble +# Preamble -- Sip Number: 009 -- Title: Standard Trait Definition for Non-Fungibe Tokens -- Author: Friedger Müffke (mail@friedger.de) -- Consideration: Technical -- Type: Standard -- Status: Draft -- Created: 2020-12-10 -- License: CC0-1.0 -- Sign-off: +SIP Number: 009 -## Abstract +Title: Standard Trait Definition for Non-Fungibe Tokens + +Author: Friedger Müffke (mail@friedger.de) + +Consideration: Technical + +Type: Standard + +Status: Draft + +Created: 10 December 2020 + +License: CC0-1.0 + +Sign-off: + +# Abstract Non-fungible token are unique digital assets that are registered on the Stacks blockchain through a smart contract with certain properties. Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have more properties that are not specified in this standard. -## License and Copyright +# 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 +# Introduction Tokens are digital assets that are registered on the Stacks blockchain through a smart contract. A non-fungible token (NFT) is a token that is globally unique and that users can identify through its identifier. The smart contract that registers the NFTs defines a name for the group of NFTs. NFTs are enumerated, the id starts at 1 and the current last id is provided by the smart contract. -## Specification +# Specification NFT smart contract shall implement the trait defined at `ST314JC8J24YWNVAEJJHQXS5Q4S9DX1FW5Z9DK9NT.nft-trait` as well as satisfy the additional conditions. The trait has three functions: -* `last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any nft. This is the upper limit when iterating through all nfts. -* `get-owner` takes an nft identifier and returns a response containing the principal owning the nft for the given identifier. The principal is wrapped as an optional, that means if the corresponding nft does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. -* `transfer` takes an nft identifier, a sender principal and a receiver principal. The function changes the ownership of the nft for the given identifier. The change has to be reflected in the `get-owner` function, for details see implementation rules. +* `last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any NFT. This is the upper limit when iterating through all NFTs. +* `get-owner` takes an NFT identifier and returns a response containing the principal owning the NFT for the given identifier. The principal is wrapped as an optional, that means if the corresponding NFT does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. +* `transfer` takes an NFT identifier, a sender principal and a receiver principal. The function changes the ownership of the NFT for the given identifier. The change has to be reflected in the `get-owner` function, for details see implementation rules. -### Trait +## Trait ``` (define-trait stacks-token-nft-standard-v1 @@ -52,10 +60,10 @@ The trait has three functions: ) ``` -### Implementation rules +## Implementation rules -1. Contracts must use a least one nft asset. A post condition with deny mode and without any nft condition about a changed owner must fail for `transfer` function calls. -1. After a successfull call to function `transfer` the function `get-owner` must return the recipient of the `transfer` call as the new owner. +1. Contracts must use a least one NFT asset. A post condition with deny mode and without any NFT condition about a changed owner must fail for `transfer` function calls. +1. After a successful call to function `transfer` the function `get-owner` must return the recipient of the `transfer` call as the new owner. 1. If a call to function `get-owner` returns some principal `A` value then it must return the same value until `transfer` is called with principal `A` as a sender 1. For any call to `get-owner`, resp. `transfer` with an id greater than `last-token-id`, the call should return a response `none`, resp. failed transfer. 1. The following error codes are defined @@ -64,20 +72,20 @@ The trait has three functions: |----------|-------|-------------| |`transfer`|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| -## Related Work +# Related Work https://eips.ethereum.org/EIPS/eip-721 https://www.ledger.com/academy/what-are-nft -## Backwards Compatibility +# Backwards Compatibility Not applicable -## Activation +# Activation -This SIP is activated as soon as 5 contracts are deployed that are using the same trait that follows this specification. +This SIP is activated if 5 contracts are deployed that are using the same trait that follows this specification. This has to happen before Stacks tip #5000. -## Reference Implementations +# Reference Implementations Source code https://github.com/friedger/clarity-smart-contracts/blob/master/contracts/sips/nft-trait.clar From 6ea5810cb0f4ca3b8257d7db0e830e36f82f8298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Mon, 15 Mar 2021 09:54:23 +0100 Subject: [PATCH 11/21] Minor changes from by @pxydn --- sips/sips/sip-009-nft-standard.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 4ad7ca52..b7582464 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -21,8 +21,7 @@ Sign-off: # Abstract Non-fungible token are unique digital assets that are registered on the Stacks blockchain through a smart contract with certain properties. -Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have more properties -that are not specified in this standard. +Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have more properties that are not specified in this standard. # License and Copyright @@ -40,7 +39,7 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t NFT smart contract shall implement the trait defined at `ST314JC8J24YWNVAEJJHQXS5Q4S9DX1FW5Z9DK9NT.nft-trait` as well as satisfy the additional conditions. The trait has three functions: * `last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any NFT. This is the upper limit when iterating through all NFTs. -* `get-owner` takes an NFT identifier and returns a response containing the principal owning the NFT for the given identifier. The principal is wrapped as an optional, that means if the corresponding NFT does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. +* `get-owner` takes an NFT identifier and returns a response containing the principal owning the NFT for the given identifier. The principal is wrapped as an optional, which means if the corresponding NFT does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. * `transfer` takes an NFT identifier, a sender principal and a receiver principal. The function changes the ownership of the NFT for the given identifier. The change has to be reflected in the `get-owner` function, for details see implementation rules. ## Trait @@ -63,7 +62,7 @@ The trait has three functions: ## Implementation rules 1. Contracts must use a least one NFT asset. A post condition with deny mode and without any NFT condition about a changed owner must fail for `transfer` function calls. -1. After a successful call to function `transfer` the function `get-owner` must return the recipient of the `transfer` call as the new owner. +1. After a successful call to function `transfer`, the function `get-owner` must return the recipient of the `transfer` call as the new owner. 1. If a call to function `get-owner` returns some principal `A` value then it must return the same value until `transfer` is called with principal `A` as a sender 1. For any call to `get-owner`, resp. `transfer` with an id greater than `last-token-id`, the call should return a response `none`, resp. failed transfer. 1. The following error codes are defined @@ -83,7 +82,7 @@ Not applicable # Activation -This SIP is activated if 5 contracts are deployed that are using the same trait that follows this specification. This has to happen before Stacks tip #5000. +This SIP is activated if 5 contracts are deployed that are using the same trait that follows this specification. This has to happen before Bitcoin tip #700,000. # Reference Implementations From 92d64c72f708b6917a6de16629abfb7028e7e9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Mon, 15 Mar 2021 20:51:11 +0100 Subject: [PATCH 12/21] add get-token-uri --- sips/sips/sip-009-nft-standard.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index b7582464..d2b191ca 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -38,7 +38,8 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t NFT smart contract shall implement the trait defined at `ST314JC8J24YWNVAEJJHQXS5Q4S9DX1FW5Z9DK9NT.nft-trait` as well as satisfy the additional conditions. The trait has three functions: -* `last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any NFT. This is the upper limit when iterating through all NFTs. +* `get-last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any NFT. This is the upper limit when iterating through all NFTs. +* `get-token-uri` takes an NFT identifier and returns a response containg a URI pointing to meta data of the given identifier. The URI is wrapped as an optional, which means if the corresponding NFT exists or the contract does not maintain meta data the response is `(ok none)`, otherwise, e.g. `(ok (some "https://example.com"))`. The length of the returned uri is limited to 256. The specification of the meta data should be covered in a separate SIP. * `get-owner` takes an NFT identifier and returns a response containing the principal owning the NFT for the given identifier. The principal is wrapped as an optional, which means if the corresponding NFT does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. * `transfer` takes an NFT identifier, a sender principal and a receiver principal. The function changes the ownership of the NFT for the given identifier. The change has to be reflected in the `get-owner` function, for details see implementation rules. @@ -48,9 +49,12 @@ The trait has three functions: (define-trait stacks-token-nft-standard-v1 ( ;; Token ID, limited to uint range - (last-token-id () (response uint uint)) + (get-last-token-id () (response uint uint)) - ;; Owner of given token identifier + ;; URI for meta data about the token + (get-token-uri (uint) (response (optional (string-ascii 256)) uint)) + + ;; Owner of given token identifier (get-owner (uint) (response (optional principal) uint)) ;; Transfer from to @@ -71,6 +75,8 @@ The trait has three functions: |----------|-------|-------------| |`transfer`|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| +1. The methods `get-last-token-id`. `get-token-uri` and `get-owner` can be implemented as read-only functions. + # Related Work https://eips.ethereum.org/EIPS/eip-721 From b7be83d1ef6e016c4bb76b1bf307834111395bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Mon, 15 Mar 2021 21:01:05 +0100 Subject: [PATCH 13/21] explain differences to EIP 721 --- sips/sips/sip-009-nft-standard.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index d2b191ca..6a3c3ab2 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -79,8 +79,11 @@ The trait has three functions: # Related Work -https://eips.ethereum.org/EIPS/eip-721 -https://www.ledger.com/academy/what-are-nft +NFTs are an established asset class on blockchains. Read for example [here](https://www.ledger.com/academy/what-are-nft). + +## EIP 721 +Ethereum has [ERP 721](https://eips.ethereum.org/EIPS/eip-721) that defined non-fungible tokens on the Ethereum blockchain. Notable differences are that the transfer function in EIP 721 uses a different ordering of the arguments ending with the token id. This SIP uses the token id as the first argument which is in line with all transfer functions in Clarity. Furthermore, this SIP only defines a URI pointing to meta data. There are not specifications about schema or other properties of token meta data. These should be specified in a separate SIP. + # Backwards Compatibility From e2fe2dfeced76968041328df4d63a6f2da138e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Tue, 16 Mar 2021 13:02:52 +0100 Subject: [PATCH 14/21] fix typos --- sips/sips/sip-009-nft-standard.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 6a3c3ab2..1ffb0e7e 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -2,7 +2,7 @@ SIP Number: 009 -Title: Standard Trait Definition for Non-Fungibe Tokens +Title: Standard Trait Definition for Non-Fungible Tokens Author: Friedger Müffke (mail@friedger.de) @@ -21,7 +21,7 @@ Sign-off: # Abstract Non-fungible token are unique digital assets that are registered on the Stacks blockchain through a smart contract with certain properties. -Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have more properties that are not specified in this standard. +Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have additional properties not specified in this standard. # License and Copyright @@ -39,7 +39,7 @@ NFTs are enumerated, the id starts at 1 and the current last id is provided by t NFT smart contract shall implement the trait defined at `ST314JC8J24YWNVAEJJHQXS5Q4S9DX1FW5Z9DK9NT.nft-trait` as well as satisfy the additional conditions. The trait has three functions: * `get-last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any NFT. This is the upper limit when iterating through all NFTs. -* `get-token-uri` takes an NFT identifier and returns a response containg a URI pointing to meta data of the given identifier. The URI is wrapped as an optional, which means if the corresponding NFT exists or the contract does not maintain meta data the response is `(ok none)`, otherwise, e.g. `(ok (some "https://example.com"))`. The length of the returned uri is limited to 256. The specification of the meta data should be covered in a separate SIP. +* `get-token-uri` takes an NFT identifier and returns a response containing a URI pointing to metadata of the given identifier. The URI is wrapped as an optional, which means if the corresponding NFT exists or the contract does not maintain metadata, the response is `(ok none)` - otherwise, e.g. `(ok (some "https://example.com"))`. The length of the returned URI is limited to 256. The specification of the metadata should be covered in a separate SIP. * `get-owner` takes an NFT identifier and returns a response containing the principal owning the NFT for the given identifier. The principal is wrapped as an optional, which means if the corresponding NFT does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. * `transfer` takes an NFT identifier, a sender principal and a receiver principal. The function changes the ownership of the NFT for the given identifier. The change has to be reflected in the `get-owner` function, for details see implementation rules. @@ -51,7 +51,7 @@ The trait has three functions: ;; Token ID, limited to uint range (get-last-token-id () (response uint uint)) - ;; URI for meta data about the token + ;; URI for metadata about the token (get-token-uri (uint) (response (optional (string-ascii 256)) uint)) ;; Owner of given token identifier @@ -65,7 +65,7 @@ The trait has three functions: ## Implementation rules -1. Contracts must use a least one NFT asset. A post condition with deny mode and without any NFT condition about a changed owner must fail for `transfer` function calls. +1. Contracts must use a least one NFT asset. A post-condition with deny mode and without any NFT condition about a changed owner must fail for `transfer` function calls. 1. After a successful call to function `transfer`, the function `get-owner` must return the recipient of the `transfer` call as the new owner. 1. If a call to function `get-owner` returns some principal `A` value then it must return the same value until `transfer` is called with principal `A` as a sender 1. For any call to `get-owner`, resp. `transfer` with an id greater than `last-token-id`, the call should return a response `none`, resp. failed transfer. @@ -75,14 +75,14 @@ The trait has three functions: |----------|-------|-------------| |`transfer`|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| -1. The methods `get-last-token-id`. `get-token-uri` and `get-owner` can be implemented as read-only functions. +1. The methods `get-last-token-id`, `get-token-uri` and `get-owner` can be implemented as read-only functions. # Related Work NFTs are an established asset class on blockchains. Read for example [here](https://www.ledger.com/academy/what-are-nft). ## EIP 721 -Ethereum has [ERP 721](https://eips.ethereum.org/EIPS/eip-721) that defined non-fungible tokens on the Ethereum blockchain. Notable differences are that the transfer function in EIP 721 uses a different ordering of the arguments ending with the token id. This SIP uses the token id as the first argument which is in line with all transfer functions in Clarity. Furthermore, this SIP only defines a URI pointing to meta data. There are not specifications about schema or other properties of token meta data. These should be specified in a separate SIP. +Ethereum has [ERP 721](https://eips.ethereum.org/EIPS/eip-721) that defined non-fungible tokens on the Ethereum blockchain. Notable differences are that the transfer function in EIP 721 uses a different ordering of the arguments ending with the token id. This SIP uses the token id as the first argument which is in line with all transfer functions in Clarity. Furthermore, this SIP only defines a URI pointing to metadata. There are no specifications about schema or other properties of token metadata. These should be specified in a separate SIP. # Backwards Compatibility @@ -91,11 +91,12 @@ Not applicable # Activation -This SIP is activated if 5 contracts are deployed that are using the same trait that follows this specification. This has to happen before Bitcoin tip #700,000. +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 +## Source code +### Friedger's clarity-smart-contracts https://github.com/friedger/clarity-smart-contracts/blob/master/contracts/sips/nft-trait.clar Deployment on testnet: TODO From aca65160848c975cacc0bbb22b38c5d89110cbab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 10:11:53 +0100 Subject: [PATCH 15/21] restructure as suggested by @muneebm --- sips/sips/sip-009-nft-standard.md | 110 ++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 27 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 1ffb0e7e..545b0672 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -20,9 +20,8 @@ Sign-off: # Abstract -Non-fungible token are unique digital assets that are registered on the Stacks blockchain through a smart contract with certain properties. -Users should be able to identify a single non-fungible token. Users should be able to own it and transfer it. Non-fungible tokens can have additional properties not specified in this standard. - +Non-fungible tokens or NFTs are digital assets registered on blockchain with unique identifiers and properties that distinguish them from each other. +It should be possible to uniquely identify, own and transfer a non-fungible token. This SIP aims to provide a flexible and easy-to-implement standard that can be used by developers on the Stacks blockchain when creating their own NFTs. This standard only specifies a basic set of requirements, non-fungible tokens can have more features than what's specified in this standard. # 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/ @@ -30,59 +29,116 @@ This SIP’s copyright is held by the Stacks Open Internet Foundation. # Introduction -Tokens are digital assets that are registered on the Stacks blockchain through a smart contract. A non-fungible token (NFT) is a token that is globally unique and that users can identify through its identifier. The smart contract that registers the NFTs defines a name for the group of NFTs. +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. -NFTs are enumerated, the id starts at 1 and the current last id is provided by the smart contract. +The Stacks blockchain's programming language for developing smart contracts, Clarity, has built-in language primitives to define and use non-fungible tokens. Although those primitives exists, there is value in defining a common interface (known in Clarity as a "trait") that allows different smart contracts to interoperate with non-fungible token contracts in a reusable way. This SIP defines that trait. + +NFTs always belong to one smart contract. They are enumerated starting at 1. The current last id is provided by a smart contract function. # Specification -NFT smart contract shall implement the trait defined at `ST314JC8J24YWNVAEJJHQXS5Q4S9DX1FW5Z9DK9NT.nft-trait` as well as satisfy the additional conditions. -The trait has three functions: -* `get-last-token-id` does not take any arguments and returns the highest number that is used as an identifier for any NFT. This is the upper limit when iterating through all NFTs. -* `get-token-uri` takes an NFT identifier and returns a response containing a URI pointing to metadata of the given identifier. The URI is wrapped as an optional, which means if the corresponding NFT exists or the contract does not maintain metadata, the response is `(ok none)` - otherwise, e.g. `(ok (some "https://example.com"))`. The length of the returned URI is limited to 256. The specification of the metadata should be covered in a separate SIP. -* `get-owner` takes an NFT identifier and returns a response containing the principal owning the NFT for the given identifier. The principal is wrapped as an optional, which means if the corresponding NFT does not exists the response is `(ok none)`, otherwise, e.g. `(ok (some 'ST12...))`. The owner can be a contract principal. -* `transfer` takes an NFT identifier, a sender principal and a receiver principal. The function changes the ownership of the NFT for the given identifier. The change has to be reflected in the `get-owner` function, for details see implementation rules. +Every SIP-009 compliant smart contract in Stacks blockchain must implement the trait, `nft-trait`, defined in the [Trait](#trait) section and must meet the requirements for the following functions: + +### Last token ID + +`(get-last-token-id () (response uint uint))` + +Takes no arguments and returns the identifier for the last NFT registered using the contract. The returned ID can be used as the upper limit when iterating through all NFTs. + +This function must never return an error response. It can be defined as read-only, i.e. `define-read-only`. + +### Token URI + +`(get-token-uri (uint) (response (optional (string-ascii 256)) uint))` + +Takes an NFT identifier and returns a response containing a valid URI which resolves to the NFT's metadata. The URI string 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 ""))`. The length of the returned URI is limited to 256 characters. The specification of the metadata should be covered in a separate SIP. + +This function must never return an error response. It can be defined as read-only, i.e. `define-read-only`. + +### Owner + +`(get-owner (uint) (response (optional principal) uint))` + +Takes an NFT identifier and returns a response containing the principal owning the NFT with the given identifier. The principal must be wrapped in an optional. If the corresponding NFT doesn't exist, the response must be `(ok none)`. The owner can be a contract principal. + +If a call to function `get-owner` returns some principal `A`, then it must return the same value until the `transfer` function is called with principal `A` as the sender. +For any call to `get-owner` with an id greater than the last token id returned by the `get-last-token-id` function, the call must return a response `(ok none)`. + +This function must never return an error response. It can be defined as read-only, i.e. `define-read-only`. + +### Transfer + +`(transfer (uint principal principal) (response bool (tuple (kind (string-ascii 32)) (code uint))))` + +The function changes the ownership of the NFT for the given identifier from the sender principal to the recipient principal. + +This function must be defined with define-public, as it alters state, and must be externally callable. + +After a successful call to `transfer`, the function `get-owner` must return the recipient of the `transfer` call as the new owner. + +For any call to `transfer` with an id greater than the last token id returned by the `get-last-token-id` function, the call must return an error response. + +When returning an error from this function, the error must follow the pattern defined below: + +| error | description | +|-------|-------------| +|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| ## Trait ``` -(define-trait stacks-token-nft-standard-v1 +(define-trait nft-trait ( - ;; Token ID, limited to uint range + ;; Last token ID, limited to uint range (get-last-token-id () (response uint uint)) - ;; URI for metadata about the token + ;; URI for metadata associated with the token (get-token-uri (uint) (response (optional (string-ascii 256)) uint)) - ;; Owner of given token identifier + ;; Owner of a given token identifier (get-owner (uint) (response (optional principal) uint)) - ;; Transfer from to + ;; Transfer from the sender to a new principal (transfer (uint principal principal) (response bool (tuple (kind (string-ascii 32)) (code uint)))) ) ) ``` -## Implementation rules +## Use of native asset functions + +Although it is not possible to mandate in a Clarity trait, contract implementers must use at least one built-in native non-fungible assets that are provided as Clarity primitives. This allows clients to use Post Conditions (explained below), and takes advantages of other benefits, like native support for these asset balances and transfers through `stacks-blockchain-api`. The reference implementations included in this SIP use the native asset primitives, and provide a good boilerplate for their usage. + +The native asset functions include: + +- `define-non-fungible-token` +- `nft-burn?` +- `nft-get-owner?` +- `nft-mint?` +- `nft-transfer?` + +The following requirements for using native asset functions are defined: +### Transfer +If the `transfer` function is called from a client without a post-condition in deny mode or without any NFT condition about a changed owner, then the function call must fail with `abort_by_post_condition`. +# Using NFTs in applications + +Developers who wish to use a non-fungible token contract in an application should first be provided, or keep track of, various different non-fungible token implementations. When validating 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. + +All of the functions in this trait return the `response` type, which is a requirement of trait definitions in Clarity. However, some 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. -1. Contracts must use a least one NFT asset. A post-condition with deny mode and without any NFT condition about a changed owner must fail for `transfer` function calls. -1. After a successful call to function `transfer`, the function `get-owner` must return the recipient of the `transfer` call as the new owner. -1. If a call to function `get-owner` returns some principal `A` value then it must return the same value until `transfer` is called with principal `A` as a sender -1. For any call to `get-owner`, resp. `transfer` with an id greater than `last-token-id`, the call should return a response `none`, resp. failed transfer. -1. The following error codes are defined +## Use of Post-conditions -| function | error | description | -|----------|-------|-------------| -|`transfer`|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| +The Stacks blockchain includes a feature known as "Post-Conditions" or "Constraints". By defining post-conditions, users can create transactions that include pre-defined guarantees about what might happen in that contract. -1. The methods `get-last-token-id`, `get-token-uri` and `get-owner` can be implemented as read-only functions. +For example, when applications call the `transfer` function, they should _always_ use post conditions to specify that the new owner of the NFT is the recipient principal in the `transfer` function call. # Related Work NFTs are an established asset class on blockchains. Read for example [here](https://www.ledger.com/academy/what-are-nft). ## EIP 721 -Ethereum has [ERP 721](https://eips.ethereum.org/EIPS/eip-721) that defined non-fungible tokens on the Ethereum blockchain. Notable differences are that the transfer function in EIP 721 uses a different ordering of the arguments ending with the token id. This SIP uses the token id as the first argument which is in line with all transfer functions in Clarity. Furthermore, this SIP only defines a URI pointing to metadata. There are no specifications about schema or other properties of token metadata. These should be specified in a separate SIP. +Ethereum has [EIP 721](https://eips.ethereum.org/EIPS/eip-721) that defined non-fungible tokens on the Ethereum blockchain. Notable differences are that the transfer function in EIP 721 uses a different ordering of the arguments ending with the token id. The transfer function in this SIP uses the token id as the first argument which is in line with the other native functions in Clarity. Furthermore, this SIP only defines a function for getting the URI pointing to the metadata of an NFT. The specifications for schema and other properties of the token metadata should be defined in a separate SIP. # Backwards Compatibility From a2cf277d6a1cb5676d0644b67a6fc658c7c6e9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 10:13:05 +0100 Subject: [PATCH 16/21] add Muneeb as author --- sips/sips/sip-009-nft-standard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 545b0672..f81e0274 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -4,7 +4,7 @@ SIP Number: 009 Title: Standard Trait Definition for Non-Fungible Tokens -Author: Friedger Müffke (mail@friedger.de) +Author: Friedger Müffke (mail@friedger.de), Muneeb Majeed Consideration: Technical From b16b169e54b1e03f2eb9a7f5c4343a2b27b40b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 10:45:57 +0100 Subject: [PATCH 17/21] add trait deployment addresses, improve wording, replace id with ID --- sips/sips/sip-009-nft-standard.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index f81e0274..ab4d9e8f 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -35,7 +35,7 @@ In blockchains with smart contracts, including the Stacks blockchain, developers The Stacks blockchain's programming language for developing smart contracts, Clarity, has built-in language primitives to define and use non-fungible tokens. Although those primitives exists, there is value in defining a common interface (known in Clarity as a "trait") that allows different smart contracts to interoperate with non-fungible token contracts in a reusable way. This SIP defines that trait. -NFTs always belong to one smart contract. They are enumerated starting at 1. The current last id is provided by a smart contract function. +Each NFT always belong to one smart contract. NFTs of a smart contract are enumerated starting at 1. The current last ID is provided by a smart contract function. The asset ID together with the contract ID defines a globally unique NFT. # Specification @@ -65,7 +65,7 @@ Takes an NFT identifier and returns a response containing the principal owning t If a call to function `get-owner` returns some principal `A`, then it must return the same value until the `transfer` function is called with principal `A` as the sender. -For any call to `get-owner` with an id greater than the last token id returned by the `get-last-token-id` function, the call must return a response `(ok none)`. +For any call to `get-owner` with an ID greater than the last token ID returned by the `get-last-token-id` function, the call must return a response `(ok none)`. This function must never return an error response. It can be defined as read-only, i.e. `define-read-only`. @@ -79,7 +79,7 @@ This function must be defined with define-public, as it alters state, and must b After a successful call to `transfer`, the function `get-owner` must return the recipient of the `transfer` call as the new owner. -For any call to `transfer` with an id greater than the last token id returned by the `get-last-token-id` function, the call must return an error response. +For any call to `transfer` with an ID greater than the last token ID returned by the `get-last-token-id` function, the call must return an error response. When returning an error from this function, the error must follow the pattern defined below: @@ -108,7 +108,7 @@ When returning an error from this function, the error must follow the pattern de ## Use of native asset functions -Although it is not possible to mandate in a Clarity trait, contract implementers must use at least one built-in native non-fungible assets that are provided as Clarity primitives. This allows clients to use Post Conditions (explained below), and takes advantages of other benefits, like native support for these asset balances and transfers through `stacks-blockchain-api`. The reference implementations included in this SIP use the native asset primitives, and provide a good boilerplate for their usage. +Although it is not possible to mandate in a Clarity trait, contract implementers must define at least one built-in native non-fungible [asset class](https://app.sigle.io/friedger.id/FDwT_3yuMrHDQm-Ai1OVS) that are provided as Clarity primitives. This allows clients to use Post Conditions (explained below), and takes advantages of other benefits, like native support for these asset balances and transfers through `stacks-blockchain-api`. The reference implementations included in this SIP use the native asset primitives, and provide a good boilerplate for their usage. The native asset functions include: @@ -120,7 +120,7 @@ The native asset functions include: The following requirements for using native asset functions are defined: ### Transfer -If the `transfer` function is called from a client without a post-condition in deny mode or without any NFT condition about a changed owner, then the function call must fail with `abort_by_post_condition`. +If the `transfer` function is called from a client without a [post-condition](https://docs.blockstack.org/understand-stacks/transactions#post-conditions) in deny mode or without any NFT condition about a changed owner, then the function call must fail with `abort_by_post_condition`. # Using NFTs in applications Developers who wish to use a non-fungible token contract in an application should first be provided, or keep track of, various different non-fungible token implementations. When validating 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. @@ -138,8 +138,7 @@ For example, when applications call the `transfer` function, they should _always NFTs are an established asset class on blockchains. Read for example [here](https://www.ledger.com/academy/what-are-nft). ## EIP 721 -Ethereum has [EIP 721](https://eips.ethereum.org/EIPS/eip-721) that defined non-fungible tokens on the Ethereum blockchain. Notable differences are that the transfer function in EIP 721 uses a different ordering of the arguments ending with the token id. The transfer function in this SIP uses the token id as the first argument which is in line with the other native functions in Clarity. Furthermore, this SIP only defines a function for getting the URI pointing to the metadata of an NFT. The specifications for schema and other properties of the token metadata should be defined in a separate SIP. - +Ethereum has [EIP 721](https://eips.ethereum.org/EIPS/eip-721) that defined non-fungible tokens on the Ethereum blockchain. Notable differences are that the transfer function in EIP 721 uses a different ordering of the arguments ending with the token id. The transfer function in this SIP uses the token ID as the first argument which is in line with the other native functions in Clarity. Furthermore, this SIP only defines a function for getting the URI pointing to the metadata of an NFT. The specifications for schema and other properties of the token metadata should be defined in a separate SIP. # Backwards Compatibility @@ -149,10 +148,10 @@ Not applicable 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. +A trait that follows this specification is available on mainnet as [`SP1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS6RXRAY4.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0xe547bca01878eaaf4758a067d3f522691a8928fcade0b0a4930a825c10d082bc?chain=mainnet) and on testnet as [`ST1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS54Q30F0.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0x07922820aca1ef6bbadddd1a19212befe11c9dc5da9bce0248d93915e2fb80d4?chain=testnet). + # Reference Implementations ## Source code ### Friedger's clarity-smart-contracts https://github.com/friedger/clarity-smart-contracts/blob/master/contracts/sips/nft-trait.clar - -Deployment on testnet: TODO From bd97e51786f708d38484ad24787aad837aa3993d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 14:04:27 +0100 Subject: [PATCH 18/21] fix typo --- sips/sips/sip-009-nft-standard.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index ab4d9e8f..453be092 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -121,13 +121,14 @@ The native asset functions include: The following requirements for using native asset functions are defined: ### Transfer If the `transfer` function is called from a client without a [post-condition](https://docs.blockstack.org/understand-stacks/transactions#post-conditions) in deny mode or without any NFT condition about a changed owner, then the function call must fail with `abort_by_post_condition`. + # Using NFTs in applications Developers who wish to use a non-fungible token contract in an application should first be provided, or keep track of, various different non-fungible token implementations. When validating 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. All of the functions in this trait return the `response` type, which is a requirement of trait definitions in Clarity. However, some 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. -## Use of Post-conditions +## Use of Post-Conditions The Stacks blockchain includes a feature known as "Post-Conditions" or "Constraints". By defining post-conditions, users can create transactions that include pre-defined guarantees about what might happen in that contract. From 9183bdcbfff1dcc886e32d8ac9a8a3fdeab313d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 17:25:09 +0100 Subject: [PATCH 19/21] fix mainnet address --- sips/sips/sip-009-nft-standard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 453be092..cee3d9e2 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -149,7 +149,7 @@ Not applicable 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. -A trait that follows this specification is available on mainnet as [`SP1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS6RXRAY4.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0xe547bca01878eaaf4758a067d3f522691a8928fcade0b0a4930a825c10d082bc?chain=mainnet) and on testnet as [`ST1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS54Q30F0.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0x07922820aca1ef6bbadddd1a19212befe11c9dc5da9bce0248d93915e2fb80d4?chain=testnet). +A trait that follows this specification is available on mainnet as [`SP1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS6RXRAY4.nft-trait`](https://explorer.stacks.co/txid/0xe547bca01878eaaf4758a067d3f522691a8928fcade0b0a4930a825c10d082bc?chain=mainnet) and on testnet as [`ST1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS54Q30F0.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0x07922820aca1ef6bbadddd1a19212befe11c9dc5da9bce0248d93915e2fb80d4?chain=testnet). # Reference Implementations From f50ad4aae229105a3f929732046e65e82b58e4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Wed, 17 Mar 2021 17:26:50 +0100 Subject: [PATCH 20/21] fix mainnet address --- sips/sips/sip-009-nft-standard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index cee3d9e2..453be092 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -149,7 +149,7 @@ Not applicable 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. -A trait that follows this specification is available on mainnet as [`SP1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS6RXRAY4.nft-trait`](https://explorer.stacks.co/txid/0xe547bca01878eaaf4758a067d3f522691a8928fcade0b0a4930a825c10d082bc?chain=mainnet) and on testnet as [`ST1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS54Q30F0.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0x07922820aca1ef6bbadddd1a19212befe11c9dc5da9bce0248d93915e2fb80d4?chain=testnet). +A trait that follows this specification is available on mainnet as [`SP1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS6RXRAY4.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0xe547bca01878eaaf4758a067d3f522691a8928fcade0b0a4930a825c10d082bc?chain=mainnet) and on testnet as [`ST1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS54Q30F0.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0x07922820aca1ef6bbadddd1a19212befe11c9dc5da9bce0248d93915e2fb80d4?chain=testnet). # Reference Implementations From 1455469aeb647ca726b56dac648dc5db2a777b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedger=20M=C3=BCffke?= Date: Fri, 19 Mar 2021 00:17:26 +0100 Subject: [PATCH 21/21] update response error type of transfer to uint --- sips/sips/sip-009-nft-standard.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sips/sips/sip-009-nft-standard.md b/sips/sips/sip-009-nft-standard.md index 453be092..d767e1a3 100644 --- a/sips/sips/sip-009-nft-standard.md +++ b/sips/sips/sip-009-nft-standard.md @@ -71,7 +71,7 @@ This function must never return an error response. It can be defined as read-onl ### Transfer -`(transfer (uint principal principal) (response bool (tuple (kind (string-ascii 32)) (code uint))))` +`(transfer (uint principal principal) (response bool uint))` The function changes the ownership of the NFT for the given identifier from the sender principal to the recipient principal. @@ -81,11 +81,7 @@ After a successful call to `transfer`, the function `get-owner` must return the For any call to `transfer` with an ID greater than the last token ID returned by the `get-last-token-id` function, the call must return an error response. -When returning an error from this function, the error must follow the pattern defined below: - -| error | description | -|-------|-------------| -|`{kind: "nft-transfer-failed", code: from-nft-transfer}`| Error if the call failed due to the underlying asset transfer. The code `from-nft-transfer` is the error code from the native asset transfer function| +It is recommended to use error codes from standardized list of codes and implement the function for converting the error codes to messages function that are defined in a separate SIP. ## Trait ``` @@ -101,7 +97,7 @@ When returning an error from this function, the error must follow the pattern de (get-owner (uint) (response (optional principal) uint)) ;; Transfer from the sender to a new principal - (transfer (uint principal principal) (response bool (tuple (kind (string-ascii 32)) (code uint)))) + (transfer (uint principal principal) (response bool uint)) ) ) ``` @@ -149,7 +145,7 @@ Not applicable 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. -A trait that follows this specification is available on mainnet as [`SP1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS6RXRAY4.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0xe547bca01878eaaf4758a067d3f522691a8928fcade0b0a4930a825c10d082bc?chain=mainnet) and on testnet as [`ST1JSH2FPE8BWNTP228YZ1AZZ0HE0064PS54Q30F0.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0x07922820aca1ef6bbadddd1a19212befe11c9dc5da9bce0248d93915e2fb80d4?chain=testnet). +A trait that follows this specification is available on mainnet as [`SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait`](https://explorer.stacks.co/txid/0x80eb693e5e2a9928094792080b7f6d69d66ea9cc881bc465e8d9c5c621bd4d07?chain=mainnet). # Reference Implementations