From f138053ebc3a8fd32f93757f4e40fefeb024697c Mon Sep 17 00:00:00 2001 From: "Mar.io" <34627453+Maar-io@users.noreply.github.com> Date: Fri, 5 May 2023 11:16:57 +0200 Subject: [PATCH 1/4] add_child and remove_child --- PSPs/drafts/psp-nesting.md | 189 +++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 PSPs/drafts/psp-nesting.md diff --git a/PSPs/drafts/psp-nesting.md b/PSPs/drafts/psp-nesting.md new file mode 100644 index 0000000..f1953a2 --- /dev/null +++ b/PSPs/drafts/psp-nesting.md @@ -0,0 +1,189 @@ +# Nesting NFTs + +- **PSP Number:** [To be assigned (=number of the initial PR to the PSPs repo)] +- **Authors:** [boyswan](https://github.com/boyswan), [Maar-io](https://github.com/Maar-io), +- **Status:** Draft +- **Created:** [2023-05-08] +- **Reference Implementation** [Link to a first reference implementation](https://github.com/rmrk-team/rmrk-ink) + +## Summary + +An interface for Nestable Non-Fungible Tokens with emphasis on parent token's control over the relationship. + +## Motivation + +The Parent-Governed Nestable NFT standard extends PSP34 by allowing for a new inter-NFT relationship and interaction. + +At its core, the idea behind the proposal is simple: the owner of an NFT does not have to be an Externally Owned Account (EOA) or a smart contract, it can also be an NFT. + +The process of nesting an NFT into another is functionally identical to sending it to another user. The process of sending a token out of another one involves issuing a transaction from the account owning the parent token. + +An NFT can be owned by a single other NFT, but can in turn have a number of NFTs that it owns. This proposal establishes the framework for the parent-child relationships of NFTs. A parent token is the one that owns another token. A child token is a token that is owned by another token. A token can be both a parent and child at the same time. Child tokens of a given token can be fully managed by the parent token's owner, but can be proposed by anyone. + +## Specification + +1. Interfaces +2. Events +3. Types +4. Errors + +### Interfaces +This section defines the required interface for this standard. + +#### Nesting::add_child +Selector: `0x1d6f5156`, first 4 bytes of `blake2b_256(Nesting::add_child)` +```json +{ + "args": [ + { + "label": "parent_token_id", + "type": { + "displayName": [ + "ParentTokenId" + ], + "type": "u128" + } + }, + { + "label": "child_nft", + "type": { + "displayName": [ + "ChildNft" + ], + "type": "ChildNft" + } + } + ], + "docs": [ + " Add a child NFT (from different collection) to the NFT in this collection.", + " The status of the added child is `Pending` if caller is not owner of child NFT", + " The status of the added child is `Accepted` if caller is is owner of child NFT", + " The caller needs not to be the owner of the parent_token_id,", + " but the Caller must be owner of the child NFT,", + " in order to perform transfer() ownership of the child nft to parent_token_id.", + "", + " # Requirements:", + " * `child_contract_address` needs to be added by collection owner", + " * `to_parent_token_id` must exist.", + " * `child_token_id` must exist.", + " * There cannot be two identical children.", + "", + " # Arguments:", + " * `to_parent_token_id`: is the tokenId of the parent NFT. The receiver of child.", + " * `child_nft`: (collection_id, token_id) of the child instance.", + "", + " # Result:", + " Ownership of child NFT will be transferred to this contract (cross contract call)", + " On success emits `ChildAdded`", + " On success emits `ChildAccepted` - only if caller is already the owner of the child NFT" + ], + "label": "Nesting::add_child", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": "Result" + } +} +``` + +#### Nesting::remove_child +Selector: `0x27e7420e`, first 4 bytes of `blake2b_256(Nesting::remove_child)` +```json +{ + "args": [ + { + "label": "parent_token_id", + "type": { + "displayName": [ + "ParentTokenId" + ], + "type": "u128" + } + }, + { + "label": "child_nft", + "type": { + "displayName": [ + "ChildNft" + ], + "type": "ChildNft" + } + } + ], + "docs": [ + " Remove a child NFT (from different collection) from parent_token_id in this collection.", + " The status of added child is `Pending` if caller is not owner of child NFT", + " The status of added child is `Accepted` if caller is owner of child NFT", + "", + " # Requirements:", + " * The status of the child is `Accepted`", + "", + " # Arguments:", + " * `parent_token_id`: is the tokenId of the parent NFT.", + " * `child_nft`: (collection_id, token_id) of the child instance.", + "", + " # Result:", + " Ownership of child NFT will be transferred to parent NFT owner (cross contract call)", + " On success emits `ChildRemoved`" + ], + "label": "Nesting::remove_child", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": "Result" + } +} +``` +### Events + +### Types +```rust +// AccountId is a 32 bytes Array, like in Substrate-based blockchains. +type AccountId = [u8; 32]; +``` +```rust +// ChildNft is a tuple of Child's contract address and child's Id +type ChildNft = (AccountId, u128) +``` + +### Errors +The suggested methods revert the transaction and return a [SCALE-encoded](https://github.com/paritytech/parity-scale-codec) `Result` type with one of the following `Error` enum variants: + +```rust +enum NestingError { + /// Custom error type for cases if writer of traits added own restrictions + Custom(String), + /// Returned if owner approves self + SelfApprove, + /// Returned if the caller doesn't have allowance for transferring. + NotApproved, + /// Returned if the owner already own the token. + TokenExists, + /// Returned if the token doesn't exist + TokenNotExists, + /// Returned if safe transfer check fails + SafeTransferCheckFailed(String), +} + +enum PSP34ReceiverError { + /// Returned if transfer is rejected. + TransferRejected(String), +} +``` + +## Tests + +If applicable, please include a list of potential test cases to validate an implementation. + +## Copyright + +Each PSP must be labeled as placed in the +[public domain](https://creativecommons.org/publicdomain/zero/1.0/). From 1df77bea31ac6e9eb0130047bcf1ab6538b9d46a Mon Sep 17 00:00:00 2001 From: "Mar.io" <34627453+Maar-io@users.noreply.github.com> Date: Fri, 5 May 2023 14:44:09 +0200 Subject: [PATCH 2/4] draft for review --- PSPs/drafts/psp-nesting.md | 405 ++++++++++++++++++++++++++++++++++--- 1 file changed, 381 insertions(+), 24 deletions(-) diff --git a/PSPs/drafts/psp-nesting.md b/PSPs/drafts/psp-nesting.md index f1953a2..c1d924d 100644 --- a/PSPs/drafts/psp-nesting.md +++ b/PSPs/drafts/psp-nesting.md @@ -30,7 +30,7 @@ An NFT can be owned by a single other NFT, but can in turn have a number of NFTs ### Interfaces This section defines the required interface for this standard. -#### Nesting::add_child +#### Nesting::add_child(&mut self, parent_token_id: u128, child_nft: ChildNft) -> Result<(), NestingError> Selector: `0x1d6f5156`, first 4 bytes of `blake2b_256(Nesting::add_child)` ```json { @@ -82,15 +82,14 @@ Selector: `0x1d6f5156`, first 4 bytes of `blake2b_256(Nesting::add_child)` "payable": false, "returnType": { "displayName": [ - "ink", "MessageResult" ], - "type": "Result" + "type": "Result<(), NestingError>" } } ``` -#### Nesting::remove_child +#### Nesting::remove_child(&mut self, parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> Selector: `0x27e7420e`, first 4 bytes of `blake2b_256(Nesting::remove_child)` ```json { @@ -135,15 +134,385 @@ Selector: `0x27e7420e`, first 4 bytes of `blake2b_256(Nesting::remove_child)` "payable": false, "returnType": { "displayName": [ - "ink", "MessageResult" ], - "type": "Result" + "type": "Result<(), NestingError>" } } ``` + +#### Nesting::accept_child(&mut self, parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> +Selector: `0x3b3e2643`, first 4 bytes of `blake2b_256(Nesting::accept_child)` + +```json +{ + "args": [ + { + "label": "parent_token_id", + "type": { + "displayName": [ + "ParentTokenId" + ], + "type": "u128" + } + }, + { + "label": "child_nft", + "type": { + "displayName": [ + "ChildNft" + ], + "type": "ChildNft" + } + } + ], + "docs": [ + " Accept a child NFT (from different collection) to be owned by parent token.", + "", + " # Requirements:", + " * The status of the child is `Pending`", + "", + " # Arguments:", + " * `parent_token_id`: is the tokenId of the parent NFT.", + " * `child_nft`: (collection_id, token_id) of the child instance.", + "", + " # Result:", + " Child Nft is moved from pending to accepted", + " On success emits `ChildAccepted`" + ], + "label": "Nesting::accept_child", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "MessageResult" + ], + "type": "Result<(), NestingError>" + } +} +``` + +### Nesting::reject_child(&mut self, parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> +Selector: `0xdd308ed4`, first 4 bytes of `blake2b_256(Nesting::reject_child)` +```json +{ + "args": [ + { + "label": "parent_token_id", + "type": { + "displayName": [ + "ParentTokenId" + ], + "type": "u128" + } + }, + { + "label": "child_nft", + "type": { + "displayName": [ + "ChildNft" + ], + "type": "ChildNft" + } + } + ], + "docs": [ + " Reject a child NFT (from different collection).", + "", + " # Requirements:", + " * The status of the child is `Pending`", + "", + " # Arguments:", + " * `parent_token_id`: is the tokenId of the parent NFT.", + " * `child_nft`: (collection_id, token_id) of the child instance.", + "", + " # Result:", + " Child Nft is removed from pending", + " On success emits `ChildRejected`" + ], + "label": "Nesting::reject_child", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "MessageResult" + ], + "type": "Result<(), NestingError>" + } +} +``` + +#### Nesting::transfer_child(&mut self, from: u128, to: u128, child_nft: ChildNft) -> Result<(), NestingError>; +Selector: `0xdb43324e`, first 4 bytes of `blake2b_256(Nesting::transfer_child)` +```json +{ + "args": [ + { + "label": "from", + "type": { + "displayName": [ + "From" + ], + "type": "u128" + } + }, + { + "label": "to", + "type": { + "displayName": [ + "To" + ], + "type": "u128" + } + }, + { + "label": "child_nft", + "type": { + "displayName": [ + "ChildNft" + ], + "type": "ChildNft" + } + } + ], + "docs": [ + " Transfer the child NFT from one parent to another (in this collection).", + "", + " # Requirements:", + " * The status of the child is `Accepted`", + "", + " # Arguments:", + " * `current_parent`: current parent tokenId which holds child nft", + " * `new_parent`: new parent tokenId which will hold child nft", + " * `child_nft`: (collection_id, token_id) of the child instance.", + "", + " # Result:", + " Ownership of child NFT will be transferred to this contract (cross contract call)", + " On success emits `ChildAdded`", + " On success emits `ChildAccepted` - only if caller is already owner of child NFT" + ], + "label": "Nesting::transfer_child", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "MessageResult" + ], + "type": "Result<(), NestingError>" + } +} +``` + +#### Nesting::get_parent_of_child(&self, child_nft: ChildNft) -> Option +Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_child)` +```json +{ + "args": [ + { + "label": "child_nft", + "type": { + "displayName": [ + "ChildNft" + ], + "type": "ChildNft" + } + } + ], + "docs": [ + " Returns the parent token id of the provided child nft." + ], + "label": "Nesting::get_parent_of_child", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "MessageResult" + ], + "type": "Option" + } +} +``` + ### Events +### ChildAdded +```json +{ + "args": [ + { + "docs": [], + "indexed": true, + "label": "to", + "type": { + "displayName": [ + "ParentId" + ], + "type": "u128" + } + }, + { + "docs": [], + "indexed": true, + "label": "collection", + "type": { + "displayName": [ + "ChildCollectionAddress" + ], + "type": "AccountId" + } + }, + { + "docs": [], + "indexed": true, + "label": "child", + "type": { + "displayName": [ + "ChildId" + ], + "type": "u128" + } + } + ], + "docs": [ + " Event emitted when a new child is added." + ], + "label": "ChildAdded" +} +``` +#### ChildAccepted +```json +{ + "args": [ + { + "docs": [], + "indexed": true, + "label": "parent", + "type": { + "displayName": [ + "ParentId" + ], + "type": "u128" + } + }, + { + "docs": [], + "indexed": true, + "label": "collection", + "type": { + "displayName": [ + "CollectionAddress" + ], + "type": "AccountId" + } + }, + { + "docs": [], + "indexed": true, + "label": "child", + "type": { + "displayName": [ + "ChildId" + ], + "type": "u128" + } + } + ], + "docs": [ + " Event emitted when a child is accepted." + ], + "label": "ChildAccepted" +} +``` + +### ChildRemoved +```json +{ + "args": [ + { + "docs": [], + "indexed": true, + "label": "parent", + "type": { + "displayName": [ + "ParentId" + ], + "type": 11 + } + }, + { + "docs": [], + "indexed": true, + "label": "child_collection", + "type": { + "displayName": [ + "ChildCollection" + ], + "type": "AccountId" + } + }, + { + "docs": [], + "indexed": true, + "label": "child_token_id", + "type": { + "displayName": [ + "ChildId" + ], + "type": "u128" + } + } + ], + "docs": [ + " Event emitted when a child is removed." + ], + "label": "ChildRemoved" +} +``` + +#### ChildRejected +```json +{ + "args": [ + { + "docs": [], + "indexed": true, + "label": "parent", + "type": { + "displayName": [ + "ParentId" + ], + "type": 11 + } + }, + { + "docs": [], + "indexed": true, + "label": "child_collection", + "type": { + "displayName": [ + "ChildCollection" + ], + "type": "AccountId" + } + }, + { + "docs": [], + "indexed": true, + "label": "child_token_id", + "type": { + "displayName": [ + "ChildId" + ], + "type": "u128" + } + } + ], + "docs": [ + " Event emitted when a child is rejected." + ], + "label": "ChildRejected" +} +``` ### Types ```rust // AccountId is a 32 bytes Array, like in Substrate-based blockchains. @@ -159,29 +528,17 @@ The suggested methods revert the transaction and return a [SCALE-encoded](https: ```rust enum NestingError { - /// Custom error type for cases if writer of traits added own restrictions - Custom(String), - /// Returned if owner approves self - SelfApprove, - /// Returned if the caller doesn't have allowance for transferring. - NotApproved, - /// Returned if the owner already own the token. - TokenExists, - /// Returned if the token doesn't exist - TokenNotExists, - /// Returned if safe transfer check fails - SafeTransferCheckFailed(String), -} - -enum PSP34ReceiverError { - /// Returned if transfer is rejected. - TransferRejected(String), + AlreadyAddedChild, + AddingPendingChild, + InvalidParentId, + ChildNotFound, + NotTokenOwner, } ``` ## Tests -If applicable, please include a list of potential test cases to validate an implementation. +Check reference implementation ## Copyright From bb500f8c23f16b7a68d195b0a6eedf49ab9061b3 Mon Sep 17 00:00:00 2001 From: "Mar.io" <34627453+Maar-io@users.noreply.github.com> Date: Mon, 8 May 2023 18:35:58 +0200 Subject: [PATCH 3/4] review update --- PSPs/drafts/psp-nesting.md | 66 +++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/PSPs/drafts/psp-nesting.md b/PSPs/drafts/psp-nesting.md index c1d924d..05a2ecb 100644 --- a/PSPs/drafts/psp-nesting.md +++ b/PSPs/drafts/psp-nesting.md @@ -30,7 +30,8 @@ An NFT can be owned by a single other NFT, but can in turn have a number of NFTs ### Interfaces This section defines the required interface for this standard. -#### Nesting::add_child(&mut self, parent_token_id: u128, child_nft: ChildNft) -> Result<(), NestingError> +#### Nesting::add_child(parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> + Selector: `0x1d6f5156`, first 4 bytes of `blake2b_256(Nesting::add_child)` ```json { @@ -41,7 +42,7 @@ Selector: `0x1d6f5156`, first 4 bytes of `blake2b_256(Nesting::add_child)` "displayName": [ "ParentTokenId" ], - "type": "u128" + "type": "Id" } }, { @@ -55,9 +56,9 @@ Selector: `0x1d6f5156`, first 4 bytes of `blake2b_256(Nesting::add_child)` } ], "docs": [ - " Add a child NFT (from different collection) to the NFT in this collection.", - " The status of the added child is `Pending` if caller is not owner of child NFT", - " The status of the added child is `Accepted` if caller is is owner of child NFT", + " Add a child NFT (from different collection) to the NFT in this (parent) collection.", + " The status of the added child is `Pending` if caller is not owner of parent NFT", + " The status of the added child is `Accepted` if caller is the owner of parent NFT", " The caller needs not to be the owner of the parent_token_id,", " but the Caller must be owner of the child NFT,", " in order to perform transfer() ownership of the child nft to parent_token_id.", @@ -73,9 +74,9 @@ Selector: `0x1d6f5156`, first 4 bytes of `blake2b_256(Nesting::add_child)` " * `child_nft`: (collection_id, token_id) of the child instance.", "", " # Result:", - " Ownership of child NFT will be transferred to this contract (cross contract call)", + " Ownership of child NFT will be transferred to this parent NFT", " On success emits `ChildAdded`", - " On success emits `ChildAccepted` - only if caller is already the owner of the child NFT" + " On success emits `ChildAccepted` - only if caller is already the owner of the parent NFT" ], "label": "Nesting::add_child", "mutates": true, @@ -89,7 +90,8 @@ Selector: `0x1d6f5156`, first 4 bytes of `blake2b_256(Nesting::add_child)` } ``` -#### Nesting::remove_child(&mut self, parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> +#### Nesting::remove_child(parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> + Selector: `0x27e7420e`, first 4 bytes of `blake2b_256(Nesting::remove_child)` ```json { @@ -100,7 +102,7 @@ Selector: `0x27e7420e`, first 4 bytes of `blake2b_256(Nesting::remove_child)` "displayName": [ "ParentTokenId" ], - "type": "u128" + "type": "Id" } }, { @@ -114,9 +116,7 @@ Selector: `0x27e7420e`, first 4 bytes of `blake2b_256(Nesting::remove_child)` } ], "docs": [ - " Remove a child NFT (from different collection) from parent_token_id in this collection.", - " The status of added child is `Pending` if caller is not owner of child NFT", - " The status of added child is `Accepted` if caller is owner of child NFT", + " Transfer the child NFT ownership from parent_token_id to the parent token owner.", "", " # Requirements:", " * The status of the child is `Accepted`", @@ -126,7 +126,7 @@ Selector: `0x27e7420e`, first 4 bytes of `blake2b_256(Nesting::remove_child)` " * `child_nft`: (collection_id, token_id) of the child instance.", "", " # Result:", - " Ownership of child NFT will be transferred to parent NFT owner (cross contract call)", + " Ownership of child NFT will be transferred to parent NFT owner", " On success emits `ChildRemoved`" ], "label": "Nesting::remove_child", @@ -141,7 +141,8 @@ Selector: `0x27e7420e`, first 4 bytes of `blake2b_256(Nesting::remove_child)` } ``` -#### Nesting::accept_child(&mut self, parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> +#### Nesting::accept_child(parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> + Selector: `0x3b3e2643`, first 4 bytes of `blake2b_256(Nesting::accept_child)` ```json @@ -153,7 +154,7 @@ Selector: `0x3b3e2643`, first 4 bytes of `blake2b_256(Nesting::accept_child)` "displayName": [ "ParentTokenId" ], - "type": "u128" + "type": "Id" } }, { @@ -192,7 +193,8 @@ Selector: `0x3b3e2643`, first 4 bytes of `blake2b_256(Nesting::accept_child)` } ``` -### Nesting::reject_child(&mut self, parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> +### Nesting::reject_child(parent_token_id: Id, child_nft: ChildNft) -> Result<(), NestingError> + Selector: `0xdd308ed4`, first 4 bytes of `blake2b_256(Nesting::reject_child)` ```json { @@ -203,7 +205,7 @@ Selector: `0xdd308ed4`, first 4 bytes of `blake2b_256(Nesting::reject_child)` "displayName": [ "ParentTokenId" ], - "type": "u128" + "type": "Id" } }, { @@ -242,7 +244,8 @@ Selector: `0xdd308ed4`, first 4 bytes of `blake2b_256(Nesting::reject_child)` } ``` -#### Nesting::transfer_child(&mut self, from: u128, to: u128, child_nft: ChildNft) -> Result<(), NestingError>; +#### Nesting::transfer_child(from: Id, to: Id, child_nft: ChildNft) -> Result<(), NestingError>; + Selector: `0xdb43324e`, first 4 bytes of `blake2b_256(Nesting::transfer_child)` ```json { @@ -253,7 +256,7 @@ Selector: `0xdb43324e`, first 4 bytes of `blake2b_256(Nesting::transfer_child)` "displayName": [ "From" ], - "type": "u128" + "type": "Id" } }, { @@ -262,7 +265,7 @@ Selector: `0xdb43324e`, first 4 bytes of `blake2b_256(Nesting::transfer_child)` "displayName": [ "To" ], - "type": "u128" + "type": "Id" } }, { @@ -303,7 +306,8 @@ Selector: `0xdb43324e`, first 4 bytes of `blake2b_256(Nesting::transfer_child)` } ``` -#### Nesting::get_parent_of_child(&self, child_nft: ChildNft) -> Option +#### Nesting::get_parent_of_child(&self, child_nft: ChildNft) -> Option + Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_child)` ```json { @@ -328,7 +332,7 @@ Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_chi "displayName": [ "MessageResult" ], - "type": "Option" + "type": "Option" } } ``` @@ -347,7 +351,7 @@ Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_chi "displayName": [ "ParentId" ], - "type": "u128" + "type": "Id" } }, { @@ -369,7 +373,7 @@ Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_chi "displayName": [ "ChildId" ], - "type": "u128" + "type": "Id" } } ], @@ -391,7 +395,7 @@ Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_chi "displayName": [ "ParentId" ], - "type": "u128" + "type": "Id" } }, { @@ -413,7 +417,7 @@ Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_chi "displayName": [ "ChildId" ], - "type": "u128" + "type": "Id" } } ], @@ -458,7 +462,7 @@ Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_chi "displayName": [ "ChildId" ], - "type": "u128" + "type": "Id" } } ], @@ -503,7 +507,7 @@ Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_chi "displayName": [ "ChildId" ], - "type": "u128" + "type": "Id" } } ], @@ -519,8 +523,12 @@ Selector: `0x40255e26`, first 4 bytes of `blake2b_256(Nesting::get_parent_of_chi type AccountId = [u8; 32]; ``` ```rust +// Id is a 128 bit unsigned integer +type Id = u128; +``` +```rust // ChildNft is a tuple of Child's contract address and child's Id -type ChildNft = (AccountId, u128) +type ChildNft = (AccountId, Id) ``` ### Errors From f5371db99222e25d4f844d55bf53e2ea0d52de0e Mon Sep 17 00:00:00 2001 From: "Mar.io" Date: Tue, 9 May 2023 14:37:51 +0200 Subject: [PATCH 4/4] added PR number --- PSPs/drafts/{psp-nesting.md => psp-60.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename PSPs/drafts/{psp-nesting.md => psp-60.md} (99%) diff --git a/PSPs/drafts/psp-nesting.md b/PSPs/drafts/psp-60.md similarity index 99% rename from PSPs/drafts/psp-nesting.md rename to PSPs/drafts/psp-60.md index 05a2ecb..2af4c79 100644 --- a/PSPs/drafts/psp-nesting.md +++ b/PSPs/drafts/psp-60.md @@ -1,8 +1,8 @@ # Nesting NFTs -- **PSP Number:** [To be assigned (=number of the initial PR to the PSPs repo)] -- **Authors:** [boyswan](https://github.com/boyswan), [Maar-io](https://github.com/Maar-io), -- **Status:** Draft +- **PSP Number:** 60 +- **Authors:** [boyswan](https://github.com/boyswan), [Maar-io](https://github.com/Maar-io), +- **Status:** Draft - **Created:** [2023-05-08] - **Reference Implementation** [Link to a first reference implementation](https://github.com/rmrk-team/rmrk-ink)