NFT Relationship Enhancement
Abstract
This proposal builds on ERC-1155 and creates a standard for referring relationships and quantifiable attributes between non-isolated ERC-721 or ERC-1155 non-fungible tokens (NFTs). It enables users to build a graph of NFTs and set quantifiable attributes for each NFT, facilitating more complex NFT ecosystems. While a similar proposal exists for ERC-721 tokens, it does not provide a way to establish quantifiable relationships or object attributes.
Motivation
The current standard for NFTs lacks the ability to establish relationships and attributes between tokens. This limitation makes it difficult for users to build more complex NFT ecosystems that require referring relationships and quantifiable attributes between tokens. For example, a user may create a derivative NFT that refers to the original NFT and sets a quantifiable attribute for the relationship between the two NFTs, but without a standardized way to establish relationships and attributes between NFTs, managing these ecosystems becomes increasingly difficult and inefficient.
This proposal aims to address this issue by extending the ERC-721 and ERC-1155 standards to include the ability to establish referring relationships and quantifiable attributes between NFTs.
By enabling users to build more complex NFT ecosystems, this proposal will enhance the NFT ecosystem and open up new possibilities for NFT use cases. However, it’s important to consider potential drawbacks such as increased complexity and gas cost, and carefully design rules to mitigate these issues.
Specification
This EIP proposes the addition of five new functions to the ERC-721 and ERC-1155 standards: setRelationship
, setAttribute
, getRelationship
, getAttribute
, and getAttributeNames
. These functions allow users to establish referring relationships and set quantifiable attributes between NFTs.
setRelationship
The setRelationship
function establishes a referring relationship between two NFTs. It takes the following parameters:
function setRelationship(uint256 _originalID, uint256 _derivativeID, uint256 _attribute) external;
_originalID
: the ID of the original NFT_derivativeID
: the ID of the derivative NFT that refers to the original NFT_attribute
: the quantifiable attribute for this relationship, which defaults to 1 if not specified
When called, this function establishes a referring relationship between the two NFTs.
setAttribute
The setAttribute
function sets a quantifiable attribute for an NFT. It takes the following parameters:
function setAttribute(uint256 _id, string calldata _name, uint256 _value) external;
_id
: the ID of the NFT_name
: the name of the attribute to be set_value
: the value of the attribute to be set
When called, this function sets a quantifiable attribute for the NFT.
getAttribute
The getAttribute
function allows anyone to retrieve the value of a specific attribute associated with an NFT. It takes the following parameters:
function getAttribute(uint256 _id, string calldata _name) external view returns (bytes32);
_id
: The ID of the NFT for which you want to retrieve the attribute._name
: The name of the attribute you wish to retrieve.
This function returns the value of the specified attribute as a bytes32 data type.
getAttributeNames
The getAttributeNames function allows anyone to retrieve the names of all attributes associated with an NFT. It takes the following parameter:
function getAttributeNames(uint256 _id) external view returns (bytes32[] memory);
_id
: The ID of the NFT for which you want to retrieve the attribute names.
This function returns an array of bytes32 values representing the names of all attributes associated with the specified NFT.
getRelationship
The getRelationship
function allows anyone to retrieve the value of a referring relationship between two NFTs. It takes the following parameters:
function getRelationship(uint256 _originalID, uint256 _derivativeID) external view returns (uint256);
_originalID
: The ID of the original NFT._derivativeID
: The ID of the derivative NFT that refers to the original NFT.
This function returns the value of the referring relationship between the two NFTs as a uint256 data type.
Example Usage
NFTGraph nftContract = NFTGraph(addressOfContract);
// Retrieve the value of an attribute named "Color" for NFT with ID 123
bytes32 colorValue = nftContract.getAttribute(123, "Color");
// Retrieve the names of all attributes associated with NFT with ID 456
bytes32[] memory attributeNames = nftContract.getAttributeNames(456);
By including these functions and methods in the specification, you establish a clear and standardized way for users and developers to read attributes associated with NFTs.
Rationale
In developing this EIP, some key design decisions were made. For example, we limited the complexity of the relationship graph that can be created by only allowing for one referring relationship between two NFTs. This helps to ensure that the graph remains manageable and does not become too complex to be useful. Additionally, we kept the gas cost of setting attributes to a minimum by only allowing for one attribute to be set at a time.
While there are currently no similar features in other blockchain languages or standards, we drew inspiration from the concept of Graph Theory, which is a branch of mathematics that studies the relationships between objects. By adding the ability to establish relationships between NFTs and set quantifiable attributes for those relationships, we believe that the extended NFT standard will become even more useful and versatile for NFT creators and users.
Backwards Compatibility
This EIP is designed to be fully backward-compatible with existing ERC-721 and ERC-1155 contracts and tokens. Existing NFT contracts and tokens will continue to function as they did before, and the new setRelationship
and setAttribute
functions will only be available to contracts that explicitly implement this EIP.
Reference Implementation
To assist in understanding and implementing this proposal, we provide a reference Solidity interface and contract that define the functions for establishing relationships and reading attributes. Developers can use this interface as a foundation for integrating the NFT Relationship Enhancement into their own contracts.
ERC-165 Interface Support
The NFT Relationship Enhancement contract implements the ERC-165 standard interface to allow for interface detection. This enables smart contracts and applications to check if a given contract supports the functions defined in this proposal before interacting with it.
INFTGraph Interface
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC165/IERC165.sol"; // Import IERC165 for interface detection
interface INFTGraph is IERC165 {
// setRelationship: Establishes relationships between NFTs.
function setRelationship(uint256 _originalID, uint256 _derivativeID, uint256 _attribute) external;
// setAttribute: Sets quantifiable attributes for NFTs.
function setAttribute(uint256 _id, string calldata _name, uint256 _value) external;
// getRelationship: Retrieves relationship values between NFTs.
function getRelationship(uint256 _originalID, uint256 _derivativeID) external view returns (uint256);
// getAttribute: Retrieves the value of specific attributes associated with NFTs.
function getAttribute(uint256 _id, string calldata _name) external view returns (bytes32);
// getAttributeNames: Retrieves all attribute names associated with an NFT.
function getAttributeNames(uint256 _id) external view returns (bytes32[] memory);
}
The INFTGraph interface specifies the functions for setting relationships and attributes, as well as retrieving attribute information and relationship values.
NFTGraph Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/introspection/ERC165.sol"; // Import ERC165 for interface detection
import "./INFTGraph.sol"; // Import INFTGraph interface
contract NFTGraph is INFTGraph{
mapping(uint256 => mapping(uint256 => uint256)) public relationship;
mapping(uint256 => mapping(bytes32 => bytes32)) public attributes;
// Implement the setRelationship and setAttribute functions as described in the EIP specification.
// Implement the supportsInterface function for ERC-165.
function supportsInterface(bytes4 interfaceID) public view override returns (bool) {
return interfaceID == type(INFTGraph).interfaceId || super.supportsInterface(interfaceID);
}
// Additional implementation details...
function getRelationship(uint256 _originalID, uint256 _derivativeID) external view returns (uint256) {
return relationship[_originalID][_derivativeID];
}
function getAttribute(uint256 _id, string calldata _name) external view returns (bytes32) {
return bytes32(attributes[_id][_name]);
}
function getAttributeNames(uint256 _id) external view returns (bytes32[] memory) {
bytes32[] memory names = new bytes32[](attributes[_id].length);
for (uint256 i = 0; i < attributes[_id].length; i++) {
names[i] = bytes32(attributes[_id][i]);
}
return names;
}
function setRelationship(uint256 originalNFT, uint256 derivativeNFT, uint256 relationshipValue) public {
require(originalNFT != derivativeNFT, "Original and derivative NFTs must be different");
relationship[originalNFT][derivativeNFT] = relationshipValue;
}
function setAttribute(uint256 nft, bytes32 attributeName, bytes32 attributeValue) public {
attributes[nft][attributeName] = attributeValue;
}
}
The NFTGraph contract implements the functions specified in the INFTGraph interface and provides storage for relationships and attributes.
Developers can use this reference interface and contract as a starting point for integrating the NFT Relationship Enhancement functionality into their own projects. The interface provides a clear and standardized way to interact with the contract, promoting consistency and ease of integration.
Security Considerations
When implementing this proposal, contract developers should consider the following security aspects:
- Validation of Relationships: Contracts utilizing the setRelationship function must ensure that the relationships being established are valid and authorized by the relevant parties. Unauthorized or malicious relationships could lead to unintended consequences.
- Attribute Validation: Contracts implementing the setAttribute function should carefully validate attributes to prevent malicious or harmful values. Invalid or unvalidated attributes could disrupt the functionality of the NFT ecosystem.
- Access Control: Contracts should implement appropriate access control mechanisms to restrict who can call critical functions, especially those that modify relationships or attributes. Unauthorized access can lead to misuse or exploitation.
- Reentrancy Protection: Consider adding reentrancy protection mechanisms to functions that modify relationships or attributes. Reentrancy attacks could otherwise be exploited to manipulate contract behavior.
By addressing these considerations, developers can enhance the security of their contracts and protect the integrity of the NFT ecosystem.
Copyright
Copyright and related rights waived via CC0.