ERC-5521 Referable NFT (rNFT) (Referable NFT)
Final · NFTs & multi-token · created 2022-08-10
An extension standard for ERC-721 NFTs that records referring and referred relationships and a creation time, so that citation relationships between NFTs form a directed acyclic graph (DAG) that anyone can query.
Why it is needed
Existing NFT standards have no way to record which works an NFT was built on. Even when an artist develops an existing NFT into a new work, or a DJ mixes two tracks into a remix, the relationship between the original and the new work is not left on chain. As a result, each NFT is isolated, a sale is a one-time transaction, and the original creator has difficulty continuing to earn from the value their intellectual property later generates. To fill this gap, the source proposal adds referring relationships between NFTs so that citations can be queried efficiently, and lays the groundwork for an economic model that encourages continued creation, use and promotion. New creators can build on existing works without starting from scratch, and an original NFT can learn where it has been cited.
What to implement
function setNode(uint256 tokenId, address[] memory addresses, uint256[][] memory tokenIds) external;: Sets the referring list of an rNFT and updates the referred list of each referenced rNFT.function referringOf(address _address, uint256 tokenId) external view returns(address[] memory, uint256[][] memory);: Returns the list that the rNFT with the given contract address and token ID refers to.function referredOf(address _address, uint256 tokenId) external view returns(address[] memory, uint256[][] memory);: Returns the list of those that refer to the given rNFT.function createdTimestampOf(address _address, uint256 tokenId) external view returns(uint256);: Returns the timestamp at which the rNFT was created, as a uint256.function supportsInterface(bytes4 interfaceId) external view returns (bool);: Checks whether this contract supports the given interface, following ERC-165.function setNodeReferredExternal(address _address, uint256 tokenId, uint256[] memory _tokenIds) external;: Called from an external contract to record in the referred list that the external contract's rNFT referred to rNFTs of this contract.
In depth
Two reference metrics and the DAG structure
This standard is an ERC-721 extension that records, for each token, referring (the list I refer to, outgoing edges), referred (the list that refers to me, incoming edges), and a creation time, createdTimestamp. References must always point to tokens created earlier, so the overall relationship forms a directed acyclic graph (DAG). In the reference implementation, the Relationship struct holds the referring and referred mappings, the key arrays referringKeys and referredKeys for events, and createdTimestamp, and optional attributes such as privityOfAgreement and profitSharing can be added as extensions.
Required conditions for setNode
setNode(tokenId, addresses, tokenIds) sets the referring list of an rNFT and updates the referred list of each referenced rNFT. There are four requirements. The size of addresses MUST equal the size of tokenIds, if the size of tokenIds is not 0 then the inner array size MUST NOT be 0, tokenId MUST be unique within the same contract, and when addresses[i] is address(this), tokenId MUST NOT equal tokenIds[i][j]. In other words, a token cannot reference itself. Duplicate checks on addresses and tokenIds are RECOMMENDED. Internally, setNodeReferring handles the referring list and setNodeReferred handles the referred list.
Cross-contract references and the TargetContract interface
To reference an rNFT in another contract, use setNodeReferredExternal(_address, tokenId, _tokenIds) on the TargetContract interface. Before calling, check with ERC-165 supportsInterface that the other contract supports this interface, and call only if it does. Such conditional calls preserve backward compatibility and integration with existing contracts. As a requirement, _address MUST NOT equal address(this) when executed in an external contract that implements the interface. TargetContract also includes referringOf, referredOf, createdTimestampOf and supportsInterface, so they can be queried from outside in the same way.
createdTimestamp and Chronological Ordering Guarantee
The core principle of this standard is that an rNFT must reference content the community has already accepted, that is, content that is earlier in time order as participants know it. To enforce this, createdTimestamp = block.timestamp records a block-level global timestamp, and the reference implementation reverts if the referenced rNFT's createdTimestamp is greater than or equal to the current block time, which forces the referenced target to be a preceding token. However, because the granularity of a reference is tied to the block timestamp, the order of two rNFTs created in the same block cannot be determined. createdTimestamp MUST NOT be modifiable by the caller in any way.
Information Carried by the UpdateNode Event
The UpdateNode event is emitted when setNode is called, and it carries the rNFT's token ID, its owner, and both the list of contract addresses and the list of token IDs that reference this rNFT or are referenced by it. Stakeholders can use this information to efficiently manage and explore the complex relationship network of the rNFT ecosystem. Implementers are free to choose the storage method. Whether they use a struct or several separate mappings, the contract behaves the same as long as the output can fill the UpdateNode event. The reference implementation's convertMap is a function that extracts the full contents of a mapping inside a struct into a key array and a value array, and it is needed to satisfy the event emission requirement.
Query Functions and Data Visibility
referringOf and referredOf take a contract address and a token ID. If the address is the contract itself, they return the list from internal storage. If it is an external contract, they check the interface and then query that contract for the list. Direct access to the _relationship state variable cannot support such cross-contract queries. If privacy is not a concern, the contract can be simplified by making _relationship public and relying on the getter that Solidity generates automatically. However, if it holds information such as users' interaction history or extension parameters, it is better to keep the state variable private and provide a dedicated getter. Exposing it at all times can reveal users' behavior patterns and preferences, which can lead to privacy violations.
ERC-721 Compatibility and Usability
Because this standard adds a set of extension functions, it can be fully compatible with ERC-721. The original text lists four advantages. Ownership inheritance becomes clear, so creators do not have to work in isolation, and reference relationships become explicit, so a higher-level incentive model can be layered on for both original owners and new creators. It is also easy to integrate with existing token standards and third-party protocols (for example, combining with ERC-5006 to build a hierarchical rental market), and cross-contract references enable scalable interoperability. This standard is finalized with Final status.
Things to watch out for
- Limits of Block-Level Timestamps: createdTimestamp holds only a block-level timestamp based on the block header, so it does not support finer-grained ordering comparisons such as at the transaction level.
- Ownership Changes and Reference Relationships Are Separate: A change of ownership does not affect reference relationships. Revenue sharing usually follows the agreement made when the NFT was created, and unless the agreement states otherwise, it is unrelated to ownership changes.
- References Do Not Extend to Descendant Tokens: By default, referencing a token does not reference its descendant tokens. If only a specific child token is referenced, the contractual relationship applies only to the owner of that child token. To specify revenue sharing explicitly, one approach is to record the chain of references from the root to the leaf in referring.
- Risks of Open Minting: safeMint is intentionally designed so that anyone can mint and set relationships without restriction. As a result, unauthorized or incorrect references can be created, and malicious actors can manipulate relationships or inflate the token supply. The original text treats this not as a design flaw but as an intentional tradeoff between flexibility and trustworthiness.
- Scope of On-Chain Integrity: The on-chain data integrity guarantee covers only what is recorded on the blockchain and cannot prevent off-chain errors or manipulation. Users and integrators need care and judgment when interpreting and using the relationship data this system provides.