-
Notifications
You must be signed in to change notification settings - Fork 334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CIP-0102? | Royalty Datum Metadata #551
Changes from 8 commits
1b8654f
461e207
a05fa4a
860a77a
1fb1f14
f198e2a
693adac
1709808
6409e4c
304dcb0
6893503
fce05fc
0b9a955
34089a1
357202f
713aad5
0041a70
c51bc4a
506447d
7725aaf
6d897eb
4ecab23
2e7f21f
d77752c
ecf6b28
1a177b4
f28f8c2
38a3146
9480534
75eb324
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,7 +95,7 @@ datum = #6.121([metadata, version, extra]) | |
|
||
#### 222 NFT Standard | ||
|
||
Besides the necessary standard for the `reference NFT` we're introducing three specific token standards in this CIP. Note that the possibilities are endless here and more standards can be built on top of this CIP for FTs, other NFTs, rich fungible tokens, etc. The first is the `222` NFT standard with the registered `asset_name_label` prefix value | ||
Besides the necessary standard for the `reference NFT` we're introducing four specific token standards in this CIP. Note that the possibilities are endless here and more standards can be built on top of this CIP for FTs, other NFTs, rich fungible tokens, etc. The first is the `222` NFT standard with the registered `asset_name_label` prefix value | ||
|
||
| asset_name_label | class | description | | ||
| --------------------------- | ------------ | -------------------------------------------------------------------- | | ||
|
@@ -329,6 +329,92 @@ We want to bring the metadata of the RFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8 | |
3. Reference the output in the transaction. (off-chain) | ||
4. Verify validity of datum of the referenced output by checking if policy ID of `reference NFT` and `user token` and their asset names without the `asset_name_label` prefix match. (on-chain) | ||
|
||
#### 500 Royalty Standard | ||
|
||
The fourth introduced standard is the `500` Royalty NFT standard with the registered `asset_name_label` prefix value | ||
|
||
| asset_name_label | class | description | | ||
| --------------------------- | ------------ | -------------------------------------------------------------------- | | ||
| 500 | NFT | Royalty NFT stored in a UTxO containing a datum with royalty information | | ||
|
||
##### Class | ||
|
||
The `royalty NFT` is an NFT (non-fungible token). | ||
|
||
##### Pattern | ||
|
||
The `royalty NFT` **must** have an identical `policy id` as the collection. | ||
|
||
The `asset name` **must** be `001f4d70526f79616c7479` (hex encoded), it contains the [CIP-0067](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0067/README.md) label `500` followed by the word "Royalty". | ||
|
||
Example:\ | ||
`royalty NFT`: `(500)Royalty`\ | ||
`reference NFT`: `(100)Test123` | ||
|
||
##### Metadata | ||
|
||
The royalty info datum is specified as follows (CDDL): | ||
|
||
```cddl | ||
big_int = int / big_uint / big_nint | ||
big_uint = #6.2(bounded_bytes) | ||
big_nint = #6.3(bounded_bytes) | ||
|
||
optional_big_int = #6.121([big_int]) / #6.122([]) | ||
|
||
royalty_recipient = #6.121([ | ||
address, ; definition can be derived from: | ||
; https://github.com/input-output-hk/plutus/blob/master/plutus-ledger-api/src/PlutusLedgerApi/V1/Address.hs#L31 | ||
int, ; variable fee ( calculation: ⌊1 / (fee / 10)⌋ ); integer division with precision 10 | ||
optional_big_int, ; min fee (absolute value in lovelace) | ||
optional_big_int, ; max fee (absolute value in lovelace) | ||
]) | ||
|
||
royalty_recipients = [ * royalty_recipient ] | ||
|
||
; version is of type int, we start with version 1 | ||
version = 1 | ||
|
||
; Custom user defined plutus data. | ||
; Setting data is optional, but the field is required | ||
; and needs to be at least Unit/Void: #6.121([]) | ||
extra = plutus_data | ||
|
||
royalty_info = #6.121([royalty_recipients, version, extra]) | ||
``` | ||
|
||
Example of onchain variable fee calculation: | ||
``` | ||
; Given a royalty fee of 1.6% (0.016) | ||
|
||
; To store this in the royalty datum | ||
1 / (0.016 / 10) => 625 | ||
|
||
; To read it back | ||
10 / 625 => 0.016 | ||
``` | ||
Because the computational complexity of Plutus primitives scales with size, this approach significantly minimizes resource consumption. | ||
|
||
To prevent abuse, it is **recommended** that the `royalty NFT` is stored at the script address of a validator that ensures the specified fees are not arbitrarily changed, such as an always-fails validator. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should change the conception that we had during the writing of CIP-27 that royalties should never change. What if I want to put royalties on a collection but only for the first year, or to remove royalties if I no longer intend to continue supporting and working on a project? Having mutable royalties does introduce additional complexity in needing to maintain the token and/or lock it in a script that only you (or a DAO multisig, etc) can control... But probably best for the long term if we do not encourage people to lock up minUTxO worth of Lovelace in a token just for the sake of "not changing the royalties". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with this notion to some degree. There are lots of perfectly valid use cases for changing royalties. The key word here is recommended. With this distinction we allow for such use cases, as well as accommodating existing protocols such as Nebula which store the royalty in wallets rather than script addresses. However, I believe the recommendation against arbitrary royalty control is important. A collection whose fees are controlled arbitrarily can be easily sabotaged at the whim of the creator by increasing the royalties beyond a reasonable amount. This is bad assurance for buyers, and we should set the precedent for better schemes - the simplest of which is an always-fails validator. |
||
|
||
##### Retrieve metadata as 3rd party | ||
|
||
A third party has the following NFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(222)TestToken` and they want to lookup the royalties. The steps are | ||
|
||
1. Construct `royalty NFT` from `user token`: `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(500)Royalty` | ||
2. Look up `royalty NFT` and find the output it's locked in. | ||
3. Get the datum from the output and lookup metadata by going into the first field of constructor 0. | ||
4. Convert to JSON and encode all string entries to UTF-8 if possible, otherwise leave them in hex. | ||
|
||
##### Retrieve metadata from a Plutus validator | ||
|
||
We want to bring the royalty metadata of the NFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(222)TestToken` in the Plutus validator context. To do this we | ||
|
||
1. Construct `royalty NFT` from `user token`: `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(500)Royalty` (off-chain) | ||
2. Look up `royalty NFT` and find the output it's locked in. (off-chain) | ||
3. Reference the output in the transaction. (off-chain) | ||
4. Verify validity of datum of the referenced output by checking if policy ID of `royalty NFT` and `user token` and their asset names without the `asset_name_label` prefix match. (on-chain) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having you tested the realistic feasibility of adding this logic to a standard "marketplace" smart contract? Especially with multiple royalty recipients? While this is certainly a novel concept and a step in the right direction, I wonder on the impact to CPU, memory, and subsequent transaction fees by using this reference token datum versus other, largely off-chain solutions atm. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have implemented this in Grabbit, but haven't benchmarked it very thoroughly yet. I can get back to you on CPU, memory, etc. on the client side. As far as TX fees go, this shouldn't have much of an impact. Reading the fee is as simple as parsing the datum, which doesn't require consuming it at all. One valid concern is the memory cost to the blockchain itself. This is definitely something worth thinking about, and the design has carefully considered memory usage to be as minimal as possible. I personally think the ability for validators to read the royalties directly & trustlessly is worth the cost many times over. |
||
## Rationale | ||
|
||
Without seperation of `reference NFT` and `user token` you lose all flexibility and moving the `user token` would be quite cumbersome as you would need to add the metadata everytime to the new output where the `user token` is sent to. Hence you separate metadata and `user token` and lock the metadata inside another UTxO, so you can freely move the `user token` around. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the min and max fee optional or are they required? If so, should there be a note that if you do not wish to set them then these values should be set to 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are optional.
optional_big_int
is defined in CDDL above this, but perhaps it should be expressed in a less niche format as well.They are both expected to be a bigint or not be included. Max fee being set to 0 to represent no max fee would be confusing, as it could easily be misread to mean no fees at all, i.e. if the implementer of the protocol missed that wrinkle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derp yeah, I guess I didn't read the names of the variables very well, but I would still add the
?
to indicate in the CDDL that they are optional :)