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

Update EIP-5521: Add more descriptions #5563

Merged
merged 32 commits into from
Sep 1, 2022
Merged
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
59bbdb4
Create eip-referable-NFT.md
OniReimu Aug 22, 2022
f6253d1
Update eip-***.md
OniReimu Aug 22, 2022
4cb5d13
Update eip-referableNFT.md
OniReimu Aug 22, 2022
c7bb8b8
update eip name
OniReimu Aug 22, 2022
ce5750a
Update eip-0000.md
OniReimu Aug 22, 2022
26536e7
Update eip-0000.md
OniReimu Aug 22, 2022
9228835
Update eip-0000.md
OniReimu Aug 22, 2022
4bfe81e
Update eip-0000.md
OniReimu Aug 22, 2022
c3060a8
Update eip-0000.md
OniReimu Aug 22, 2022
38e3c28
Update eip-0000.md
OniReimu Aug 22, 2022
e9c4731
Update eip number
OniReimu Aug 22, 2022
bf68206
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
e7275a2
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
e33b053
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
9aa7f3d
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
44fb0b8
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
e34fe37
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
32baeb1
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
b81b3ea
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
a6ee02f
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
adb67d3
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
5a1df32
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
478c0fb
Update EIPS/eip-5521.md
OniReimu Aug 24, 2022
bca965b
Merge branch 'master' into master
OniReimu Aug 24, 2022
26435d5
Update eip-5521.md
OniReimu Aug 24, 2022
726a11a
Update eip-5521.md
OniReimu Aug 31, 2022
5541afe
Update eip-5521.md
OniReimu Aug 31, 2022
9d1846d
Update eip-5521.md
OniReimu Sep 1, 2022
a2ca608
Merge branch 'master' into master
OniReimu Sep 1, 2022
f648722
Update
OniReimu Sep 1, 2022
5fd1b53
Merge branch 'master' of https://github.com/OniReimu/EIPs
OniReimu Sep 1, 2022
eb9ebc3
Update .gitignore
OniReimu Sep 1, 2022
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
185 changes: 185 additions & 0 deletions EIPS/eip-5521.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
---
eip: 5521
title: Referable NFT
description: An EIP-721 extension to construct reference relationships among NFTs
author: Saber Yu (@OniReimu), Qin Wang <[email protected]>, Shange Fu <[email protected]>, Shiping Chen <[email protected]>, Sherry Xu <[email protected]>, Jiangshan Yu <[email protected]>
Copy link
Member

Choose a reason for hiding this comment

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

Is this change intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

csiso is a typo.

discussions-to: https://ethereum-magicians.org/t/eip-x-erc-721-referable-nft/10310
status: Draft
type: Standards Track
category: ERC
created: 2022-08-10
requires: 165, 721, 5006
---

## Abstract
This standard is an extension of [EIP-721](./eip-721.md). It proposes two referrable indicators, referring and referred, and a time-based indicator `createdTimestamp`. The relationship between each NFT forms a Directed acyclic graph (DAG). The standard allows users to query, track and analyze their relationships.

## Motivation
Many scenarios require inheritance, reference, and extension of NFTs. For instance, an artist may develop his NFT work based on a previous NFT, or a DJ may remix his record by referring to two pop songs, etc. Proposing a referable solution for existing NFTs and enabling efficient queries on cross-references make much sense.

By adding the `referring` indicator, users can mint new NFTs (e.g., C, D, E) by referring to existing NFTs (e.g., A, B), while `referred` enables the referred NFTs (A, B) to be aware that who has quoted it (e.g., A &#8592; D; C &#8592; E; B &#8592; E, and A &#8592; E). The `createdTimestamp` is an indicator used to show the creation time of NFTs (A, B, C, D, E).

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

`referring`: an out-degree indicator, used to show the users this NFT refers to;
`referred`: an in-degree indicator, used to show the users who have refereed this NFT;
`createdTimestamp`: a time-based indicator, used to compare the timestamp of mint.

`safeMint`: mint a new rNFT;
`setNode`: set the referring list of an rNFT and update the referred list of each one in the referring list;
`setNodeReferring`: set the referring list of an rNFT;
`setNodeReferred`: set the referred list of the given rNFTs;
`referringOf`: Get the referring list of an rNFT;
`referredOf`: Get the referred list of an rNFT.

## Rationale
This standard is intended to establish the referable DAG for queries on cross-relationship and accordingly provide the simplest functions. It provides advantages as follows.

*Clear ownership inheritance*: This standard extends the static NFT into a virtually extensible NFT network. Artists do not have to create work isolated from others. The ownership inheritance avoids reinventing the same wheel.

*Incentive Compatibility*: This standard clarifies the referable relationship across different NFTs, helping to integrate multiple up-layer incentive models for both original NFT owners and new creators.

*Easy Integration*: This standard makes it easier for the existing token standards or third-party protocols. For instance, the referable NFT can be applied to rentable scenarios (cf. [EIP-5006](./eip-5006.md) to build a hierarchical rental market, where multiple users can rent the same NFT during the same time or one user can rent multiple NFTs during the same duration).

## Backwards Compatibility
This standard can be fully [EIP-721](./eip-721.md) compatible by adding an extension function set.

## Test Cases
Truffle and Openzeppelin are required to run the following in a test network.
```node
truffle develop
rNFT = await ERC_rNFT.new("ERC_rNFT","ERC_rNFT")
rNFT.safeMint(1,[])
rNFT.referredOf(1)
rNFT.referringOf(1)

rNFT.safeMint(2,[1])
rNFT.referredOf(2)
rNFT.referringOf(2)

rNFT.safeMint(3,[1,2])
rNFT.referredOf(2)
rNFT.referredOf(3)
rNFT.referringOf(3)

```

## Reference Implementation
```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.4;

interface IERC_rNFT {

// Logged when a node in the rNFT gets referred and changed
/// @notice Emitted when the `node` (i.e., an rNFT) is changed
event UpdateNode(uint256 indexed tokenId, address indexed owner, uint256[] _referringList, uint256[] _referredList);

/// @notice Set the referred and referring relationship of an rNFT
/// Throws if `tokenId` is not valid rNFT
/// @param _tokenIds The list of the rNFTs that `tokenId` refers to
function setNode(uint256 tokenId, uint256[] memory _tokenIds) external;

/// @notice Get the list of the rNFTs that `tokenId` refers to
/// Throws if `tokenId` is not valid rNFT
/// @param tokenId The rNFT of the referring list
function referringOf(uint256 tokenId) external view returns(uint256[] memory);

/// @notice Get the list of the rNFT that refers to `tokenId`
/// Throws if `tokenId` is not valid rNFT
/// @param tokenId The rNFT of the referred list
function referredOf(uint256 tokenId) external view returns(uint256[] memory);
}
```

```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./IERC_rNFT.sol";

contract ERC_rNFT is ERC721, IERC_rNFT {

struct Relationship {
uint256[] referring; // referring list
uint256[] referred; // referred list
uint256 createdTimestamp; // unix timestamp when the rNFT is being created
}

mapping (uint256 => Relationship) internal _relationship;
address contractOwner = address(0);

constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {
contractOwner = msg.sender;
}

function safeMint(uint256 tokenId, uint256[] memory _tokenIds) public {
_safeMint(msg.sender, tokenId);
setNode(tokenId, _tokenIds);
}

/// @notice set the referring list of an rNFT
/// Throws if `tokenId` is not a valid rNFT
/// @param _tokenIds array of rNFTs
function setNodeReferring(uint256 tokenId, uint256[] memory _tokenIds) private {
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC_rNFT: transfer caller is not owner nor approved");
if (contractOwner != msg.sender && _tokenIds.length == 0) { revert("ERC_rNFT: the referring list cannot be empty"); }

Relationship storage relationship = _relationship[tokenId];
relationship.referring = _tokenIds;
relationship.createdTimestamp = block.timestamp;
emit UpdateNode(tokenId, msg.sender, relationship.referring, relationship.referred);
}

/// @notice set the referred list of an rNFT
/// Throws if `tokenId` is not a valid rNFT
/// @param _tokenIds array of rNFTs
function setNodeReferred(uint256 tokenId, uint256[] memory _tokenIds) private {
for (uint i = 0; i < _tokenIds.length; i++) {
Relationship storage relationship = _relationship[_tokenIds[i]];

if (relationship.createdTimestamp >= block.timestamp) { revert("ERC_rNFT: the referred rNFT needs to be a predecessor"); } // Make sure the reference complies with the timing sequence

relationship.referred.push(tokenId);
emit UpdateNode(_tokenIds[i], ownerOf(_tokenIds[i]), relationship.referring, relationship.referred);
}
}

/// @notice set the referred list of an rNFT and update the referring list of each one in the referred list
/// Throws if `tokenId` is not a valid rNFT
/// @param _tokenIds array of rNFTs
function setNode(uint256 tokenId, uint256[] memory _tokenIds) public virtual override {
setNodeReferring(tokenId, _tokenIds);
setNodeReferred(tokenId, _tokenIds);
}

/// @notice Get the referring list of an rNFT
/// @param tokenId The considered rNFT
/// @return The referring list of an rNFT
function referringOf(uint256 tokenId) external view virtual override returns(uint256[] memory) {
require(_exists(tokenId), "ERC_rNFT: token ID not existed");
return _relationship[tokenId].referring;
}

/// @notice Get the referred list of an rNFT
/// @param tokenId The considered rNFT
/// @return The referred list of an rNFT
function referredOf(uint256 tokenId) external view virtual override returns(uint256[] memory) {
require(_exists(tokenId), "ERC_rNFT: token ID not existed");
return _relationship[tokenId].referred;
}

/// @dev See {IERC165-supportsInterface}.
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC_rNFT).interfaceId || super.supportsInterface(interfaceId);
}
}
```

## Security Considerations
The `createdTimestamp` only covers the block-level timestamp (based on block headers), which does not support fine-grained comparison such as transaction-level.

## Copyright
Copyright and related rights waived via [CC0](../LICENSE.md).