ERC-7401: Parent-Governed Non-Fungible Tokens Nesting

An interface for Non-Fungible Tokens Nesting with emphasis on parent token's control over the relationship.


Metadata
Status: FinalStandards Track: ERCCreated: 2023-07-26
Authors
Bruno Škvorc (@Swader), Cicada (@CicadaNCR), Steven Pineda (@steven2308), Stevan Bogosavljevic (@stevyhacker), Jan Turk (@ThunderDeliverer)
Requires

Abstract


❗️ ERC-7401 supersedes ERC-6059. ❗️

The Parent-Governed NFT Nesting standard extends ERC-721 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.

Nestable tokens

The graph illustrates how a child token can also be a parent token, but both are still administered by the root parent token's owner.

Motivation


With NFTs being a widespread form of tokens in the Ethereum ecosystem and being used for a variety of use cases, it is time to standardize additional utility for them. Having the ability for tokens to own other tokens allows for greater utility, usability and forward compatibility.

In the four years since ERC-721 was published, the need for additional functionality has resulted in countless extensions. This ERC improves upon ERC-721 in the following areas:

This proposal fixes the inconsistency in the ERC-6059 interface specification, where interface ID doesn't match the interface specified as the interface evolved during the proposal's lifecycle, but one of the parameters was not added to it. The missing parameter is, however, present in the interface ID. Apart from this fix, this proposal is functionally equivalent to ERC-6059.

Bundling

One of the most frequent uses of ERC-721 is to disseminate the multimedia content that is tied to the tokens. In the event that someone wants to offer a bundle of NFTs from various collections, there is currently no easy way of bundling all of these together and handle their sale as a single transaction. This proposal introduces a standardized way of doing so. Nesting all of the tokens into a simple bundle and selling that bundle would transfer the control of all of the tokens to the buyer in a single transaction.

Collecting

A lot of NFT consumers collect them based on countless criteria. Some aim for utility of the tokens, some for the uniqueness, some for the visual appeal, etc. There is no standardized way to group the NFTs tied to a specific account. By nesting NFTs based on their owner's preference, this proposal introduces the ability to do it. The root parent token could represent a certain group of tokens and all of the children nested into it would belong to it.

The rise of soulbound, non-transferable, tokens, introduces another need for this proposal. Having a token with multiple soulbound traits (child tokens), allows for numerous use cases. One concrete example of this can be drawn from supply chains use case. A shipping container, represented by an NFT with its own traits, could have multiple child tokens denoting each leg of its journey.

Membership

A common utility attached to NFTs is a membership to a Decentralised Autonomous Organization (DAO) or to some other closed-access group. Some of these organizations and groups occasionally mint NFTs to the current holders of the membership NFTs. With the ability to nest mint a token into a token, such minting could be simplified, by simply minting the bonus NFT directly into the membership one.

Delegation

One of the core features of DAOs is voting and there are various approaches to it. One such mechanic is using fungible voting tokens where members can delegate their votes by sending these tokens to another member. Using this proposal, delegated voting could be handled by nesting your voting NFT into the one you are delegating your votes to and transferring it when the member no longer wishes to delegate their votes.

Specification


The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.


ID MUST never be a 0 value, as this proposal uses 0 values do signify that the token/destination is not an NFT.

Rationale


Designing the proposal, we considered the following questions:

  1. How to name the proposal?
    In an effort to provide as much information about the proposal we identified the most important aspect of the proposal; the parent centered control over nesting. The child token's role is only to be able to be Nestable and support a token owning it. This is how we landed on the Parent-Centered part of the title.
  2. Why is automatically accepting a child using EIP-712 permit-style signatures not a part of this proposal?
    For consistency. This proposal extends ERC-721 which already uses 1 transaction for approving operations with tokens. It would be inconsistent to have this and also support signing messages for operations with assets.
  3. Why use indexes?
    To reduce the gas consumption. If the token ID was used to find which token to accept or reject, iteration over arrays would be required and the cost of the operation would depend on the size of the active or pending children arrays. With the index, the cost is fixed. Lists of active and pending children per token need to be maintained, since methods to get them are part of the proposed interface.
    To avoid race conditions in which the index of a token changes, the expected token ID as well as the expected token's collection smart contract is included in operations requiring token index, to verify that the token being accessed using the index is the expected one.
    Implementation that would internally keep track of indices using mapping was attempted. The minimum cost of accepting a child token was increased by over 20% and the cost of minting has increased by over 15%. We concluded that it is not necessary for this proposal and can be implemented as an extension for use cases willing to accept the increased transaction cost this incurs. In the sample implementation provided, there are several hooks which make this possible.
  4. Why is the pending children array limited instead of supporting pagination?
    The pending child tokens array is not meant to be a buffer to collect the tokens that the root owner of the parent token wants to keep, but not enough to promote them to active children. It is meant to be an easily traversable list of child token candidates and should be regularly maintained; by either accepting or rejecting proposed child tokens. There is also no need for the pending child tokens array to be unbounded, because active child tokens array is.
    Another benefit of having bounded child tokens array is to guard against spam and griefing. As minting malicious or spam tokens could be relatively easy and low-cost, the bounded pending array assures that all of the tokens in it are easy to identify and that legitimate tokens are not lost in a flood of spam tokens, if one occurs.
    A consideration tied to this issue was also how to make sure, that a legitimate token is not accidentally rejected when clearing the pending child tokens array. We added the maximum pending children to reject argument to the clear pending child tokens array call. This assures that only the intended number of pending child tokens is rejected and if a new token is added to the pending child tokens array during the course of preparing such call and executing it, the clearing of this array SHOULD result in a reverted transaction.
  5. Should we allow tokens to be nested into one of its children?
    The proposal enforces that a parent token can't be nested into one of its child token, or downstream child tokens for that matter. A parent token and its children are all managed by the parent token's root owner. This means that if a token would be nested into one of its children, this would create the ownership loop and none of the tokens within the loop could be managed anymore.
  6. Why is there not a "safe" nest transfer method?
    nestTransfer is always "safe" since it MUST check for IERC7059 compatibility on the destination.
  7. How does this proposal differ from the other proposals trying to address a similar problem?
    This interface allows for tokens to both be sent to and receive other tokens. The propose-accept and parent governed patterns allow for a more secure use. The backward compatibility is only added for ERC-721, allowing for a simpler interface. The proposal also allows for different collections to inter-operate, meaning that nesting is not locked to a single smart contract, but can be executed between completely separate NFT collections.
    Additionally this proposal addresses the inconsistencies between interfaceId, interface specification and example implementation of ERC-6059.

Propose-Commit pattern for child token management

Adding child tokens to a parent token MUST be done in the form of propose-commit pattern to allow for limited mutability by a 3rd party. When adding a child token to a parent token, it is first placed in a "Pending" array, and MUST be migrated to the "Active" array by the parent token's root owner. The "Pending" child tokens array SHOULD be limited to 128 slots to prevent spam and griefing.

The limitation that only the root owner can accept the child tokens also introduces a trust inherent to the proposal. This ensures that the root owner of the token has full control over the token. No one can force the user to accept a child if they don't want to.

Parent Governed pattern

The parent NFT of a nested token and the parent's root owner are in all aspects the true owners of it. Once you send a token to another one you give up ownership.

We continue to use ERC-721's ownerOf functionality which will now recursively look up through parents until it finds an address which is not an NFT, this is referred to as the root owner. Additionally we provide the directOwnerOf which returns the most immediate owner of a token using 3 values: the owner address, the tokenId which MUST be 0 if the direct owner is not an NFT, and a flag indicating whether or not the parent is an NFT.

The root owner or an approved party MUST be able to do the following operations on children: acceptChild, rejectAllChildren and transferChild.

The root owner or an approved party MUST also be allowed to do these operations only when token is not owned by an NFT: transferFrom, safeTransferFrom, nestTransferFrom, burn.

If the token is owned by an NFT, only the parent NFT itself MUST be allowed to execute the operations listed above. Transfers MUST be done from the parent token, using transferChild, this method in turn SHOULD call nestTransferFrom or safeTransferFrom in the child token's smart contract, according to whether the destination is an NFT or not. For burning, tokens must first be transferred to an EOA and then burned.

We add this restriction to prevent inconsistencies on parent contracts, since only the transferChild method takes care of removing the child from the parent when it is being transferred out of it.

Child token management

This proposal introduces a number of child token management functions. In addition to the permissioned migration from "Pending" to "Active" child tokens array, the main token management function from this proposal is the transferChild function. The following state transitions of a child token are available with it:

  1. Reject child token
  2. Abandon child token
  3. Unnest child token
  4. Transfer the child token to an EOA or an ERC721Receiver
  5. Transfer the child token into a new parent token

To better understand how these state transitions are achieved, we have to look at the available parameters passed to transferChild:


Based on the desired state transitions, the values of these parameters have to be set accordingly (any parameters not set in the following examples depend on the child token being managed):

  1. Reject child token
    Reject child token
  2. Abandon child token
    Abandon child token
  3. Unnest child token
    Unnest child token
  4. Transfer the child token to an EOA or an ERC721Receiver
    Transfer child token to EOA
  5. Transfer the child token into a new parent token
    Transfer child token to parent token
    This state change places the token in the pending array of the new parent token. The child token still needs to be accepted by the new parent token's root owner in order to be placed into the active array of that token.

Backwards Compatibility


The Nestable token standard has been made compatible with ERC-721 in order to take advantage of the robust tooling available for implementations of ERC-721 and to ensure compatibility with existing ERC-721 infrastructure.

The only incompatibility with ERC-721 is that Nestable tokens cannot use a token ID of 0.

There is some differentiation of how the ownerOf method behaves compared to ERC-721. The ownerOf method will now recursively look up through parent tokens until it finds an address that is not an NFT; this is referred to as the root owner. Additionally, we provide the directOwnerOf, which returns the most immediate owner of a token using 3 values: the owner address, the tokenId, which MUST be 0 if the direct owner is not an NFT, and a flag indicating whether or not the parent is an NFT. In case the token is owned by an EoA or an ERC-721 Receiver, the ownerOf method will behave the same as in ERC-721.

Test Cases


Tests are included in nestable.ts.

To run them in terminal, you can use the following commands:


Reference Implementation


See NestableToken.sol.

Security Considerations


The same security considerations as with ERC-721 apply: hidden logic may be present in any of the functions, including burn, add child, accept child, and more.

Since the current owner of the token is allowed to manage the token, there is a possibility that after the parent token is listed for sale, the seller might remove a child token just before before the sale and thus the buyer would not receive the expected child token. This is a risk that is inherent to the design of this standard. Marketplaces should take this into account and provide a way to verify the expected child tokens are present when the parent token is being sold or to guard against such a malicious behaviour in another way.

It is worth noting that balanceOf method only accounts for immediate tokens owned by the address; the tokens that are nested into a token owned by this address will not be reflected in this value as the recursive lookup needed in order to calculate this value is potentially too deep and might break the method.

Caution is advised when dealing with non-audited contracts.

Copyright


Copyright and related rights waived via CC0.