-
Notifications
You must be signed in to change notification settings - Fork 323
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-0068 | Datum Metadata Standard #299
Changes from all commits
3db9bef
b6dabb3
2d06a8c
d124208
48ec7b4
5fa84ac
468d718
9955a70
d3d9861
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 |
---|---|---|
@@ -0,0 +1,241 @@ | ||
--- | ||
CIP: 68 | ||
Title: Datum Metadata Standard | ||
Authors: Alessandro Konrad <alessandro.konrad@live.de>, Thomas Vellekoop <thomas.vellekoop@iohk.io> | ||
Comments-URI: | ||
Status: Proposed | ||
Type: Informational | ||
Created: 2022-07-13 | ||
Post-History: | ||
License: CC-BY-4.0 | ||
--- | ||
|
||
## Abstract | ||
|
||
This proposal defines a metadata standard for native assets making use of output datums not only for NFTs but any asset class. | ||
|
||
## Motivation | ||
|
||
This proposal addresses a few shortcomings of [CIP-0025](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0025): | ||
|
||
- Lack of programmability; | ||
- Difficult metadata update / evolution; | ||
- Non-inspectable metadata from within Plutus validators... | ||
|
||
Besides these shortcomings CIP-0025 has some [flaws](https://github.com/cardano-foundation/CIPs/pull/85#issuecomment-1054123645) in its design. | ||
For people unaware of CIP-0025 or want to use a different way of minting or want to use a different metadata format/mechanism you open up a protocol to metadata spoofing, because this standard is so established and metadata in minting transactions are interpreted by most platforms by default. Since this standard is not enforced at the protocol level there is no guarantee everyone will be aware of it or follow the rules. At the same time you limit and constraint the capabilities of the ledger if everyone was forced to follow the rules of CIP-0025. | ||
|
||
This standard tackles all these problems and offers many more advantages, not only for NFTs, but also for any asset class that may follow. Additionally, this CIP will introduce a way to classify tokens so that third parties like wallets can easily know what the kind of token it is. | ||
|
||
|
||
## Specification | ||
|
||
### Considerations | ||
|
||
The basic idea is to have two assets issued, where one references the other. We call these two a `reference NFT` and an `user token`, where the `user token` can be an NFT, FT or any other asset class that is transferable and represents any value. So, the `user token` is the actual asset that lives in a user's wallet. | ||
|
||
To find the metadata for the `user token` you need to look for the output, where the `reference NFT` is locked in. How this is done concretely will become clear below. Moreover, this output contains a datum, which holds the metadata. The advantage of this approach is that the issuer of the assets can decide how the transaction output with the `reference NFT` is locked and further handled. If the issuer wants complete immutable metadata, the `reference NFT` can be locked at the address of an unspendable script. Similarly, if the issuer wants the NFTs/FTs to evolve or wants a mechanism to update the metadata, the `reference NFT` can be locked at the address of a script with arbitrary logic that the issuer decides. | ||
|
||
Lastly and most importantly, with this construction, the metadata can be used by a PlutusV2 script with the use of reference inputs [CIP-0031](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0031). This will drive further innovation in the token space. | ||
|
||
### Reference NFT label | ||
|
||
This is the registered `asset_name_label` value | ||
|
||
| asset_name_label | class | description | | ||
| --------------------------- | ------------ | --------------------------------------------------------- | | ||
| 100 | NFT | Reference NFT locked at a script containing the datum | | ||
|
||
### Constraints and conditions | ||
|
||
For a correct relationship between the `user token` and the `reference NFT` a few conditions **must** be met. | ||
- The `user token` and `reference NFT` **must** be under the same policy ID. | ||
- For a specific `user token` there **must** exist exactly **one** `reference NFT` | ||
- The `user token` and associated `reference NFT` **must** follow the standard naming pattern. The asset name of both assets is prefixed with its respective `asset_name_label` followed by a pattern defined by the asset class (e.g. asset_name_label 222) | ||
|
||
Some remarks about the above, | ||
1. The `user token` and `reference NFT` do not need to be minted in the same transaction. The order of minting is also not important. | ||
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. This is a clever approach that also doesn't depend on any logging standard. One way you could get around some of the restrictions with your approach is to instead having some meta-NFT for the project that is used in all the transactions that create the reference NFTs and the user tokens. This would avoid the asset-name stuffing issue your CIP introduces and this meta-NFT could be kept after the initial mint is done to add other reference NFT CIPs for the collection in the future without having to keep the policy used for the user tokens open. This is of course significantly more complicated than your approach though 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. Thank you for the feedback. The About your idea on keeping the minting policy open for future standards, we thought the same. Have a look at the following construction visualized by Alessandro, Besides this, we thought it would be a good idea for pre-determined collections to use a Merkle root in the minting policy to commit to what can be minted, this allows for decentralized minting as well. If the collection is open, say for a game another route need to be used, of course. 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 was thinking more something similar to what NMKR is doing with their DID proposal (#294) except instead of an open minting policy to store multiple DIDs, the open minting policy is what is connected to other minting scripts In other words, the minting policy script in the your image servers two purposes: minting the assets and connecting to the reference scripts. This could be split up into two different scripts similar to the diagram in the DID proposal |
||
2. It may be the case that there can be multiple `user tokens` (multiple asset names or quantity greater than 1) referencing the same `reference NFT`. | ||
|
||
The datum in the output with the `reference NFT` contains the metadata at the first field of the constructor 0. The version number is at the second field of this constructor: | ||
``` | ||
big_int = int / big_uint / big_nint | ||
big_uint = #6.2(bounded_bytes) | ||
big_nint = #6.3(bounded_bytes) | ||
|
||
metadata = | ||
{ * metadata => metadata } | ||
/ [ * metadata ] | ||
/ big_int | ||
/ bounded_bytes | ||
|
||
version = int | ||
|
||
datum = #6.121([metadata, version]) | ||
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. Note: while reviewing another CIP that proposes an extension to CIP-0025, it occurred to me that CIP-0025 does not currently provide a nice extension mechanism. While there's now a version number, it forces every version increment to support all the newly introduce 'features'. I'd hope CIP-0068 to resolve that short-coming. I'd suggest a restricted set of core features and a flexible extension mechanisms which allows people to extend the standard to new use-cases while letting downstream consumers chose which extensions they intend to support. This could very much be CIP-driven, for example an extra field: 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. @KtorZ, thank you for reviewing this CIP.
I agree, and I think it does resolve these. Note that this CIP keeps things flexible with CIP 67 (1). The goal was to create a flexible framework for all kind of tokens, not just NFT's. I think the main why reason this is achieved, is because data is spread out and localized. @alessandrokonrad made the following diagram that shows how flexible this standard could be. Here there is one main minting policy script which can mint the main token. These could be FT's, NFT, fractionalized NFT, or any other token standard that might come up. Each standard is marked with One standard to rule them all! 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.
That's a good idea. This would make the 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 am only starting now actually (well, ~20 minutes ago). This remark was more of a memo but I admittedly hadn't read the CIP in full when I wrote it 2 hours ago.
I think so. Provided that this proposal reaches a final state and that any modification / additions is done exclusively through extensions.
So, if I understand the idea correctly, the "registry" is a user-specified subset of an agreed upon set of standard referenced in CIP-0067. This would therefore acts as this kind of extension mechanism that I am suggesting, right? Or do I get this completely wrong and simply am reading what I would want to be reading :)? 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.
Yes that is correct. The question is just do we need for every little change/addition in the metadata an entire new asset name label standard. I think a combination of your idea with extensions within the same standard and having asset name labels to define new structures and token types is the best. 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. @KtorZ how would you continue with this extension idea? Do you think it's overkill when we already have CIP-0067 to register token types with different metadata formats? Wouldn't the extensions need their own standard/registry? |
||
``` | ||
|
||
## 222 NFT Standard | ||
|
||
Besides the necessary standard for the `reference NFT` we're introducing two 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, semi fungible tokens, etc. The first is the `222` NFT standard with the registered `asset_name_label` prefix value | ||
|
||
| asset_name_label | class | description | | ||
| --------------------------- | ------------ | -------------------------------------------------------------------- | | ||
| 222 | NFT | NFT hold by the user's wallet making use of CIP-0025 inner structure | | ||
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. Shouldn't that:
? |
||
|
||
|
||
### Class | ||
|
||
The `user token` represents an NFT (non-fungible token). | ||
|
||
### Pattern | ||
|
||
The `user token` and `reference NFT` **must** have an identical name, preceded by the `asset_name_label` prefix. | ||
|
||
Example:\ | ||
`user token`: `(222)Test123`\ | ||
`reference NFT`: `(100)Test123` | ||
|
||
|
||
### Metadata | ||
|
||
This is a low-level representation of the metadata, following closely the structure of CIP-0025. All UTF-8 encoded keys and values need to be converted into their respective byte's representation when creating the datum on-chain. | ||
|
||
``` | ||
files_details = | ||
{ | ||
? name : bounded_bytes, ; UTF-8 | ||
mediaType : bounded_bytes, ; UTF-8 | ||
src : bounded_bytes ; UTF-8 | ||
} | ||
|
||
metadata = | ||
{ | ||
name : bounded_bytes, ; UTF-8 | ||
image : bounded_bytes, ; UTF-8 | ||
? mediaType : bounded_bytes, ; UTF-8 | ||
? description : bounded_bytes, ; UTF-8 | ||
? files : [* files_details] | ||
} | ||
|
||
datum = #6.121([metadata, 1]) ; version 1 | ||
``` | ||
Example datum as JSON: | ||
```json | ||
{"constructor" : 0, "fields": [{"map": [{"k": "6E616D65", "v": "5370616365427564"}, {"k": "696D616765", "v": "697066733A2F2F74657374"}]}, {"int": 1}]} | ||
``` | ||
|
||
### Retrieve metadata as 3rd party | ||
|
||
A third party has the following NFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(222)TestToken` they want to lookup. The steps are | ||
|
||
1. Construct `reference NFT` from `user token`: `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(100)TestToken` | ||
2. Look up `reference 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 metadata of the NFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(222)TestToken` in the Plutus validator context. To do this we | ||
|
||
1. Construct `reference NFT` from `user token`: `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(100)TestToken` (off-chain) | ||
2. Look up `reference 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 `reference NFT` and `user token` and their asset names without the `asset_name_label` prefix match. (on-chain) | ||
|
||
## 333 FT Standard | ||
|
||
The second introduced standard is the `333` FT standard with the registered `asset_name_label` prefix value | ||
|
||
| asset_name_label | class | description | | ||
| --------------------------- | ------------ | -------------------------------------------------------------------- | | ||
| 333 | FT | FT hold by the user's wallet making use of Cardano foundation off-chain registry inner structure | | ||
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. Same remark as for the NFT class. I believe this should be a separate proposal / addition to the registry instead of a blessed / implicit one. 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. Yes of course it will be added to the registry of CIP-0067. The table here is just helpful as extra information and pointer to the registry. |
||
|
||
|
||
### Class | ||
|
||
The `user token` is an FT (fungible token). | ||
|
||
|
||
### Pattern | ||
|
||
The `user token` and `reference NFT` **must** have an identical name, preceded by the `asset_name_label` prefix. | ||
|
||
Example:\ | ||
`user token`: `(333)Test123`\ | ||
`reference NFT`: `(100)Test123` | ||
|
||
|
||
### Metadata | ||
|
||
This is a low-level representation of the metadata, following closely the structure of the Cardano foundation off-chain metadata registry. All UTF-8 encoded keys and values need to be converted into their respective byte's representation when creating the datum on-chain. | ||
|
||
``` | ||
; Explanation here: https://developers.cardano.org/docs/native-tokens/token-registry/cardano-token-registry/ | ||
|
||
metadata = | ||
{ | ||
name : bounded_bytes, ; UTF-8 | ||
description : bounded_bytes, ; UTF-8 | ||
? ticker: bounded_bytes, ; UTF-8 | ||
? url: bounded_bytes, ; UTF-8 | ||
? logo: bounded_bytes, ; UTF-8 | ||
? decimals: big_int | ||
} | ||
|
||
datum = #6.121([metadata, 1]) ; version 1 | ||
``` | ||
Example datum as JSON: | ||
```json | ||
{"constructor" : 0, "fields": [{"map": [{"k": "6E616D65", "v": "5370616365427564"}, {"k": "6465736372697074696F6E", "v": "54686973206973206D79207465737420746F6B656E"}]}, {"int": 1}]} | ||
``` | ||
|
||
### Retrieve metadata as 3rd party | ||
|
||
A third party has the following FT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(333)TestToken` they want to lookup. The steps are | ||
|
||
1. Construct `reference NFT` from `user token`: `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(100)TestToken` | ||
2. Look up `reference 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 metadata of the FT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(333)TestToken` in the Plutus validator context. To do this we | ||
|
||
1. Construct `reference NFT` from `user token`: `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(100)TestToken` (off-chain) | ||
2. Look up `reference 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 `reference NFT` and `user token` and their asset names without the `asset_name_label` prefix match. (on-chain) | ||
|
||
## Rationale | ||
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. This section seems a bit thin. In particular, I'd love to see considerations such as:
|
||
|
||
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. | ||
|
||
In order to reference the correct UTxO containing the metadata, it needs to be authenticated, otherwise metadata spoofing attacks become possible. One way to achieve that is by adding an NFT (`reference NFT`) to the UTxO. This NFT needs to under the same Policy ID as the `user token`, followed by an asset name pattern defined in the standard. This way you create a secure link between `reference NFT` and `user token` without the need for any extra data and you can make use of this off-chain and on-chain. | ||
|
||
The security for the link is derived from the minting policy itself, so it's important to write the validator with the right constraints and rules since this CIP solely defines the interface to keep flexibility as high as possible. | ||
|
||
|
||
## Backward Compatibility | ||
|
||
To keep metadata compatibility with changes coming in the future, we introduce a `version` field in the datum. | ||
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. This section is in principle meant for discussing backward-compatibility with existing, established standards. So in that case, that would be CIP-0025 & CIP-0010, to cover things such as what Blake mentioned regarding existing CIP-0010 labels. |
||
|
||
|
||
## Path to Active | ||
|
||
- Agree on a binary encoding for asset name labels in [CIP-0067](https://github.com/cardano-foundation/CIPs/pull/298). | ||
- Get support for this CIP by wallets, explorers, tools, minting platforms and other 3rd parties. | ||
- Minimal reference implementation making use of [Lucid](https://github.com/spacebudz/lucid) (off-chain), [PlutusTx](https://github.com/input-output-hk/plutus) (on-chain): | ||
[Implementation](./ref_impl/) | ||
- Open-source more practical implementations/projects which make use of this CIP. | ||
|
||
|
||
## References | ||
|
||
- CIP-0025: https://github.com/cardano-foundation/CIPs/blob/master/CIP-0025 | ||
- CIP-0031: https://github.com/cardano-foundation/CIPs/tree/master/CIP-0031 | ||
|
||
## Copyright | ||
|
||
This CIP is licensed under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/legalcode). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Minimal implementation of CIP-0068 | ||
|
||
## Concept | ||
|
||
The minting policy is a one-shot policy. Only single NFT (pair of `reference NFT` and `user token`) can be minted. When deciding to burn the NFT the whole pair must be burned, otherwise the validator will throw an error. | ||
PlutusTx was used to create the [on-chain code](onchain.hs) and [Lucid](https://github.com/spacebudz/lucid) for the [off-chain part](offchain.ts). It can be run in [Deno](https://deno.land/) and with a few modifications also in [Node.js](https://nodejs.org/) and Browser. | ||
|
||
## Api | ||
|
||
- mintNFT(assetName : string, metadata: Metadata) | ||
- burnNFT(assetName : string) | ||
- viewNFT(assetName : string) | ||
|
||
## Run | ||
|
||
``` | ||
deno run -A offchain.ts | ||
``` |
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.
This table comes a bit out of nowhere at this stage. There has been no mention of the asset name thus far. I feel like the concept / specification should be introduced earlier and that registry information like this should be left in annex / at the of the specification.
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.
That is true because it's part of CIP-0067 which was proposed at the same time as this CIP. It's more of placeholder for now, but gives a better understanding of the idea.