This proposal makes use of the onchain metadata pattern established inCIP-0068 to provide a way to store royalties with greater assurance and customizability.
The inability to create trustless onchain royalty validation withCIP-0027 is a major drawback to Cardano NFTs. The pattern defined in CIP-68 represents an opportunity to upgrade the standard to support onchain validation. This CIP aims to eliminate that drawback and demonstrate better support for developers, NFT creators, and NFT collectors, ultimately attracting dapps & NFT projects that would otherwise have taken their talents to another blockchain.
In addition, this standard allows royalties to be split between multiple addresses, another limitation of the CIP-27 royalty schema. Future versions of this standard could also easily support multiple royalty policies defined for a single collection, applied at the level of individual tokens.
The following defines the500
Royalty NFT standard with the registeredasset_name_label
prefix value
asset_name_label | class | description |
---|---|---|
500 | NFT | Royalty NFT stored in a UTxO containing a datum with royalty information |
Theroyalty NFT
is an NFT (non-fungible token).
Theroyalty NFT
must have an identicalpolicy id
as the collection.
Theasset name
must be001f4d70526f79616c7479
(hex encoded), it contains theCIP-0067 label500
followed by the word "Royalty".
Example:royalty NFT
:(500)Royalty
reference NFT
:(100)Test123
The royalty info datum is specified as follows (CDDL):
big_int = int / big_uint / big_nintbig_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 1version = 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_dataroyalty_info = #6.121([royalty_recipients, version, extra])
; Given a royalty fee of 1.6% (0.016); To store this in the royalty datum1 / (0.016 / 10) => 625; To read it back10 / 625 => 0.016
Because the computational complexity of Plutus primitives scales with size, this approach significantly minimizes resource consumption.
To prevent abuse, it isrecommended that theroyalty 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.
If not specified elsewhere in the token's datums, a malicious user could send transactions to a protocol which do not reference the royalty datum. For full assurances, a new optional flag should be added to the reference datum
extra ={...? royalty_included : big_int}
In-code examples can be found in thereference implementation.
A third party has the following NFTd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(222)TestToken
and they want to look up the royalties. The steps are
royalty NFT
fromuser token
:d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(500)Royalty
royalty NFT
and find the output it's locked in.We want to bring the royalty metadata of the NFTd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(222)TestToken
in the Plutus validator context. To do this we
royalty NFT
fromuser token
:d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(500)Royalty
(off-chain)royalty NFT
and find the output it's locked in. (off-chain)royalty NFT
anduser token
and their asset names without theasset_name_label
prefix match. (on-chain)The specification here is made to be as minimal as possible. This is done with expediency in mind and the expectation that additional changes to the specification may be made in the future. The sooner we have a standard established, the sooner we can make use of it. Rather than attempting to anticipate all use cases, we specify with forward-compatibility in mind.
This specification is largely based onthe royalty specification in Nebula, with a couple key departures:
The royalty token is recommended to be locked at a script address, rather than stored in the user's wallet. This encourages projects to guarantee royalties won't change by sending their royalties to an always-fails (or similar) script address, but still allows for creative royalty schemes and minimizes disruption to existing projects.
The policyId of the royalty NFT must match that of the reference NFT. This enables lookups based on the user token in the same way as is done for the tokens specified in the original CIP-68 standard.
In addition to providing a way to create guaranteed royalties, this has several advantages:
To keep metadata compatibility with changes coming in the future, we introduce aversion
field in the datum.
See theCIP-0068 Extension Boilerplate
This CIP is licensed underCC-BY-4.0.