Skip to content
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

Merged
merged 30 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1b8654f
First pass: introduce royalty metadata as specified in Nebula
SamDelaney May 6, 2023
461e207
minor fixes
SamDelaney May 16, 2023
a05fa4a
fix token name
SamDelaney May 18, 2023
860a77a
consolidate TN spec & rename token -> NFT
SamDelaney May 25, 2023
1fb1f14
require royalty NFT policyId to match reference NFT
SamDelaney Jun 5, 2023
f198e2a
Change royalty location to suggestion
SamDelaney Jun 7, 2023
693adac
Elaborate on fee calculation
SamDelaney Jul 13, 2023
1709808
Add 500 token to CIP 67 registry
SamDelaney Jul 13, 2023
6409e4c
move to a new CIP
SamDelaney Aug 9, 2023
304dcb0
reference datum flag spec
SamDelaney Aug 10, 2023
6893503
Fix leftover whitespace
SamDelaney Aug 10, 2023
fce05fc
Path to Active & References
SamDelaney Aug 21, 2023
0b9a955
Merge branch 'cip68-royalties' of https://github.com/ikigai-github/CI…
SamDelaney Aug 21, 2023
34089a1
update CIP-67 registry entry
SamDelaney Aug 21, 2023
357202f
changes per review
SamDelaney Aug 29, 2023
713aad5
Update preamble to match CIP-0001 standard
SamDelaney Aug 30, 2023
0041a70
assigned 102 as CIP number
rphair Sep 5, 2023
c51bc4a
move docs to new folder
SamDelaney Sep 5, 2023
506447d
Merge branch 'cip68-royalties' of https://github.com/ikigai-github/CI…
SamDelaney Sep 5, 2023
7725aaf
update registry entry
SamDelaney Sep 5, 2023
6d897eb
add registry documentation link
SamDelaney Sep 6, 2023
4ecab23
Apply suggestions from code review
SamDelaney Oct 17, 2023
2e7f21f
all header level fixes agreed at CIP meeting
rphair Oct 17, 2023
d77752c
all header level fixes agreed at CIP meeting
rphair Oct 17, 2023
ecf6b28
agreed at meeting null implementors mandatory
rphair Oct 17, 2023
1a177b4
agreed at meeting to use tickboxes
rphair Oct 17, 2023
f28f8c2
agreed at meeting to use tickboxes
rphair Oct 17, 2023
38a3146
agreed at meeting to use tickboxes
rphair Oct 17, 2023
9480534
adding `cddl` syntax tag pending eventual GitHub support
rphair Oct 17, 2023
75eb324
adding `cddl` syntax tag pending eventual GitHub support
rphair Oct 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CIP-0067/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@
"class": "RFT",
"description": "CIP-0068 - Datum Metadata Standard (444 sub standard)"
},
{
"asset_name_label": 500,
"class": "NFT",
"description": "CIP-0102 - Datum Metadata Standard (500 sub standard)",
"documentation": "https://github.com/cardano-foundation/CIPs/blob/master/CIP-0102"
}
]
179 changes: 179 additions & 0 deletions CIP-0102/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
---
CIP: 102
Title: Royalty Datum Metadata
Authors:
- Sam Delaney <[email protected]>
rphair marked this conversation as resolved.
Show resolved Hide resolved
Implementors: []
Discussions:
- https://github.com/ikigai-github/CIPs/pull/1
SamDelaney marked this conversation as resolved.
Show resolved Hide resolved
- https://github.com/cardano-foundation/CIPs/pull/551
Status: Proposed
Category: Tokens
Created: 2023-08-08
License: CC-BY-4.0
---

## Abstract

This proposal makes use of the onchain metadata pattern established in [CIP-0068][] to provide a way to store royalties with greater assurance and customizability.

## Motivation: why is this CIP necessary?

The inability to create trustless onchain royalty validation with [CIP-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.

## Specification

### 500 Royalty Datum Standard

The following defines 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][] label `500` followed by the word "Royalty".

Example:\
`royalty NFT`: `(500)Royalty`\
`reference NFT`: `(100)Test123`

#### 500 Datum 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:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is an address truly needed here or could a payment credential (i.e. a vk or script hash) suffices? The address also contains a network discriminant and, a possible delegation payload. If there's a desire to handle delegation payload then, how will pointer addresses be handled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes there's a delegation consideration, as you figured. I'm not sure I understand the concern with pointer addresses, can you elaborate?

Another consideration is consistency with the existing implementations. I want to break things as little as possible here. At the moment no changes are required to Nebula, at least. I'm less sure of closed source implementations, for obvious reasons.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these the same pointer addresses that were determined to be obsolescent here? #374 (comment)

; 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])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion, bundle the version separately, so that the format can be adjusted if needed without prior knowledge of the version. For example:

royalty_info = #6.121([1, royalty_info_v1])
royalty_info_v1 = #6.121([royalty_recipients, extra])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very interesting idea. If I understand you correctly, this would be helpful for parsing the datum as a third party once multiple versions are introduced. One could parse the version number first, then know what type to expect for the royalty info itself. Is that what you're saying?

I like the idea, but I'm not sure how much of a difference it would actually make. Do you have a good idea of how much more efficient a change like this would be?

My only real concern is compatibility with existing implementations. I'm not sure this provides enough value to warrant making Nebula & other nebula-based implementations incompatible.

```

#### Example of onchain variable fee calculation:
```cddl
; 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.

### Reference Datum Royalty Flag

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

```cddl
extra =
{
...

? royalty_included : big_int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using #6.121[] / #6.122[] (False / True) instead ?

Copy link
Contributor Author

@SamDelaney SamDelaney Sep 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was my initial idea, and it would certainly work for v1. I have an idea for v2 to make use of this field to allow for multiple royalty datums to be attached to a policy, with the correct datum to be used for each individual NFT specified in this field.

With that plan in mind, I decided to make the type big_int instead, so the type expected by third parties doesn't have to change in future versions. Since computational complexity of Plutus primitives scale with size, performance should be effectively identical.

}
```

- If the field is present and > 1 the validators must require a royalty input.
- If the field is present and set to 0 the validators don't need to search for a royalty input.
- If the field is not present, validators should accept a royalty input, but not require one.

### Examples

#### Retrieve metadata as 3rd party

A third party has the following NFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(222)TestToken` and they want to look up 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 look up 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)

## Rationale: how does this CIP achieve its goals?

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.

### 500 Royalty Token Datum

This specification is largely based on [the royalty specification in Nebula](https://github.com/spacebudz/nebula/tree/main#royalty-info-specification), 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.

### Reference Datum Flag

In addition to providing a way to create guaranteed royalties, this has several advantages:

- Backwards Compatibility - Existing royalty implementations will still work, just not have the same assurances.
- Minimal Storage Requirement - An optional boolean has about the smallest memory impact possible. This is especially important because it's attached to the - Reference NFT and will be set for each individual NFT.
- Intra-Collection Utility - This already allows for minting a collection with some NFTs with royalties and some without. A future version of this standard will likely make use of this field to allow for multiple versions of royalties for even more granular control.


### Backward Compatibility
To keep metadata compatibility with changes coming in the future, we introduce a `version` field in the datum.


## Path to Active

### Acceptance Criteria

- [ ] This CIP should receive feedback, criticism, and refinement from: CIP Editors and the community of people involved with NFT projects to review any weaknesses or areas of improvement.
- [ ] Guidelines and examples of publication of data as well as discovery and validation should be included as part of of criteria for acceptance.
- [ ] Minimal reference implementation making use of [Lucid](https://github.com/spacebudz/lucid) (off-chain), [PlutusTx](https://github.com/input-output-hk/plutus) (on-chain): [Reference Implementation](./ref_impl/).
- [ ] Implementation and use demonstrated by the community: NFT Projects, Blockchain Explorers, Wallets, Marketplaces.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


### Implementation Plan

- [ ] Publish open source reference implementation and instructions related to the creation, storage and reading of royalty utxos.
- [ ] Implement in open source libraries and tooling such as [Lucid](https://github.com/spacebudz/lucid), [Blockfrost](https://github.com/blockfrost/blockfrost-backend-ryo), etc.
- [ ] Achieve additional "buy in" from existing community actors and implementors such as: blockchain explorers, token marketplaces, minting platforms, wallets.

## Copyright

This CIP is licensed under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/legalcode).

[CIP-0027]: https://github.com/cardano-foundation/CIPs/tree/master/CIP-0027
[CIP-0067]: https://github.com/cardano-foundation/CIPs/tree/master/CIP-0067
[CIP-0068]: https://github.com/cardano-foundation/CIPs/tree/master/CIP-0068
Empty file added CIP-0102/ref_impl/TODO.md
Empty file.