Skip to content

Commit

Permalink
CIP-0068 | Add RFT Rich-fungible token support (#494)
Browse files Browse the repository at this point in the history
* CIP-0068: Add SFT Semi-fungible token support

* this "backward compatibility" facet is part of Rationale

* comma end last items works best for group editing

* Update CIP-0068/README.md

* Update CIP-0068/README.md

---------

Co-authored-by: Robert Phair <rphair@cosd.com>
Co-authored-by: Matthias Benkort <5680256+KtorZ@users.noreply.github.com>
  • Loading branch information
3 people authored May 2, 2023
1 parent e635f7d commit e6d250b
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 17 deletions.
7 changes: 6 additions & 1 deletion CIP-0067/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@
"asset_name_label": 333,
"class": "FT",
"description": "CIP-0068 - Datum Metadata Standard (333 sub standard)"
}
},
{
"asset_name_label": 444,
"class": "RFT",
"description": "CIP-0068 - Datum Metadata Standard (444 sub standard)"
},
]
115 changes: 99 additions & 16 deletions CIP-0068/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,20 @@ extra = plutus_data
datum = #6.121([metadata, version, extra])
```

## 222 NFT Standard
#### 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
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

| asset_name_label | class | description |
| --------------------------- | ------------ | -------------------------------------------------------------------- |
| 222 | NFT | NFT hold by the user's wallet making use of CIP-0025 inner structure |


### Class
##### Class

The `user token` represents an NFT (non-fungible token).

### Pattern
##### Pattern

The `user token` and `reference NFT` **must** have an identical name, preceded by the `asset_name_label` prefix.

Expand All @@ -115,7 +115,7 @@ Example:\
`reference NFT`: `(100)Test123`


### Metadata
##### 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.

Expand Down Expand Up @@ -147,10 +147,10 @@ datum = #6.121([metadata, 1, extra]) ; version 1
```
Example datum as JSON:
```json
{"constructor" : 0, "fields": [{"map": [{"k": "6E616D65", "v": "5370616365427564"}, {"k": "696D616765", "v": "697066733A2F2F74657374"}]}, {"int": 1}]}
{"constructor" : 0, "fields": [{"map": [{"k": {"bytes": "6E616D65"}, "v": {"bytes": "5370616365427564"}}, {"k": {"bytes": "696D616765"}, "v": {"bytes": "697066733A2F2F74657374"}}]}, {"int": 1}]}
```

### Retrieve metadata as 3rd party
##### Retrieve metadata as 3rd party

A third party has the following NFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(222)TestToken` they want to lookup. The steps are

Expand All @@ -159,7 +159,7 @@ A third party has the following NFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5c
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
##### 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

Expand All @@ -168,7 +168,7 @@ We want to bring the metadata of the NFT `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)

## 333 FT Standard
#### 333 FT Standard

The second introduced standard is the `333` FT standard with the registered `asset_name_label` prefix value

Expand All @@ -177,12 +177,12 @@ The second introduced standard is the `333` FT standard with the registered `ass
| 333 | FT | FT hold by the user's wallet making use of Cardano foundation off-chain registry inner structure |


### Class
##### Class

The `user token` is an FT (fungible token).


### Pattern
##### Pattern

The `user token` and `reference NFT` **must** have an identical name, preceded by the `asset_name_label` prefix.

Expand All @@ -191,7 +191,7 @@ Example:\
`reference NFT`: `(100)Test123`


### Metadata
##### 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.

Expand Down Expand Up @@ -225,10 +225,10 @@ datum = #6.121([metadata, 1, extra]) ; version 1
```
Example datum as JSON:
```json
{"constructor" : 0, "fields": [{"map": [{"k": "6E616D65", "v": "5370616365427564"}, {"k": "6465736372697074696F6E", "v": "54686973206973206D79207465737420746F6B656E"}]}, {"int": 1}]}
{"constructor" : 0, "fields": [{"map": [{"k": {"bytes": "6E616D65"}, "v": {"bytes": "5370616365427564"}}, {"k": {"bytes": "6465736372697074696F6E"}, "v": {"bytes": "54686973206973206D79207465737420746F6B656E"}}]}, {"int": 1}]}
```

### Retrieve metadata as 3rd party
##### Retrieve metadata as 3rd party

A third party has the following FT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(333)TestToken` they want to lookup. The steps are

Expand All @@ -237,7 +237,7 @@ A third party has the following FT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cb
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
##### 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

Expand All @@ -246,6 +246,89 @@ We want to bring the metadata of the FT `d5e6bf0500378d4f0da4e8dde6becec7621cd8c
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)

#### 444 RFT Standard

The third introduced standard is the `444` Rich-FT standard with the registered `asset_name_label` prefix value

| asset_name_label | class | description |
| --------------------------- | ------------ | -------------------------------------------------------------------- |
| 444 | RFT | RFT hold by the user's wallet making use of the union of CIP-0025 inner structure AND the Cardano foundation off-chain registry inner structure |

Rich-Fungible tokens don't fit cleanly into the other two FT/NFT classes of tokens and thus need their own standard. An example of an RFT would be a fractionalized NFT. The single reference NFT `(100)` represents the NFT itself, and the many `(444)` tokens represent the fractionalized shares. Minting 100 tokens and setting decimals to 2 would represent a single NFT that is split into 100 fractions.

##### Class

The `user token` is an RFT (rich-fungible token).

##### Pattern

The `user token` and `reference NFT` **must** have an identical name, preceded by the `asset_name_label` prefix.

Example:\
`user token`: `(444)Test123`\
`reference NFT`: `(100)Test123`

##### Metadata

This is a low-level representation of the metadata, following closely the structure of CIP-0025 with the optional decimals field added. 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
; ... Additional properties are allowed
}
metadata =
{
name : bounded_bytes, ; UTF-8
image : bounded_bytes, ; UTF-8
? mediaType : bounded_bytes, ; UTF-8
? description : bounded_bytes, ; UTF-8
? decimals: int,
? files : [* files_details]
; ... Additional properties are allowed
}
; A URI as a UTF-8 encoded bytestring.
; The URI scheme must be one of `https`, `ipfs` or `data`
; Do not encode plain file payloads as URI.
; 'logo' does not follow the explanation of the token-registry, it needs to be a valid URI and not a plain bytestring.
; Only use the following media types: `image/png`, `image/jpeg`, `image/svg+xml`
uri = bounded_bytes
; 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
datum = #6.121([metadata, 1, extra]) ; version 1
```
Example datum as JSON:
```json
{"constructor" : 0, "fields": [{"map": [{"k": {"bytes": "6E616D65"}, "v": {"bytes":"5370616365427564"}}, {"k": {"bytes": "6465736372697074696F6E"}, "v": {"bytes":"54686973206973206D79207465737420746F6B656E"}}, {"k": {"bytes": "696D616765"}, "v": {"bytes": "697066733A2F2F74657374"}}, {"k": {"bytes": "646563696D616C73"}, "v": {"int": 2}}]}, {"int": 1}]}
```

##### Retrieve metadata as 3rd party

A third party has the following RFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(444)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 RFT `d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc.(444)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

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.
Expand All @@ -255,7 +338,7 @@ In order to reference the correct UTxO containing the metadata, it needs to be a
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
### Backward Compatibility

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

Expand Down

0 comments on commit e6d250b

Please sign in to comment.