For many developers eager to explore the NFT space, a significant barrier to entry is the substantial cost associated with deploying an NFT contract on the Ethereum blockchain. In periods of bullish market activity, deploying an ERC721 contract can cost thousands of dollars, which prevents many creators from realizing their unique collections.
While marketplaces like OpenSea have addressed this issue with the implementation of lazy-minting on their own collections, this is merely a workaround and not an optimal solution. A more comprehensive approach would be the deployment of a smart contract capable of managing multiple sub-collections, each with their unique name, symbol, tokenURI, and ownership. This not only allows for individual ownership, which enables token minting and royalty collection from marketplaces, but also allows the clusters to maintain their unique identity.
Additionally, this approach supports the creation of NFTs with a pure utility purpose that require an affiliation program. This interface also elegantly addresses the challenge of creating new collections within a family of NFTs, simplifying management and maintaining a clear hierarchy and organization.
I propose the following interface as a potential solution to these challenges:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
// Authors: Francesco Sullo <francesco@sullo.co>
/**
* @title IClusteredERC721
* @dev IClusteredERC721 interface allows managing clusters or sub-collections of ERC721 tokens within a single contract
ERC165 InterfaceId = 0x8a7bc8c2
*/
interface IClusteredERC721 {
/**
* @dev Emitted when a new cluster is added
*/
event ClusterAdded(uint256 indexed clusterId, string name, string symbol, string baseTokenURI, uint256 size, address owner);
/**
* @dev Emitted when ownership of a cluster is transferred
*/
event ClusterOwnershipTransferred(uint256 indexed clusterId, address indexed previousOwner, address indexed newOwner);
/**
* @notice Gets the id of the cluster to which a token belongs
* @param tokenId ID of the token
* @return uint256 ID of the cluster to which the token belongs
*/
function clusterOf(uint256 tokenId) external view returns (uint256);
/**
* @notice Gets the name of a cluster
* @param clusterId ID of the cluster
* @return string Name of the cluster
*/
function nameOf(uint256 clusterId) external view returns (string memory);
/**
* @notice Gets the symbol of a cluster
* @param clusterId ID of the cluster
* @return string Symbol of the cluster
*/
function symbolOf(uint256 clusterId) external view returns (string memory);
/**
* @notice Gets the range of token IDs that are included in a specific cluster
* @param clusterId ID of the cluster
* @return (uint256, uint256) Start and end of the token ID range
*/
function rangeOf(uint256 clusterId) external view returns (uint256, uint256);
/**
* @notice Gets the owner of a cluster
* @param clusterId ID of the cluster
* @return address Owner of the cluster
*/
function clusterOwner(uint256 clusterId) external view returns (address);
/**
* @notice Gets how many clusters have been added
* @return uint256 Total number of clusters
*/
function clustersCount() external view returns (uint256);
/**
* @notice Adds a new cluster
* @dev The ClusterAdded event MUST be emitted upon successful execution
* @param name Name of the cluster
* @param symbol Symbol of the cluster
* @param baseTokenURI Base Token URI of the cluster
* @param size Size of the cluster (number of tokens)
* @param clusterOwner Address of the cluster owner
*/
function addCluster(
string memory name,
string memory symbol,
string memory baseTokenURI,
uint256 size,
address clusterOwner
) external;
/**
* @notice Transfers ownership of a cluster
* @dev The ClusterOwnershipTransferred event MUST be emitted upon successful execution
* @param clusterId ID of the cluster
* @param newOwner Address of the new owner
*/
function transferClusterOwnership(uint256 clusterId, address newOwner) external;
/**
* @notice Gets the normalized token ID for a token
* @dev The normalized token ID is the token ID within the cluster, starting from 1
* @param tokenId ID of the token
* @return uint256 Normalized token ID
*/
function normalizedTokenId(uint256 tokenId) external view returns (uint256);
}
The interface is changing over time, based on comments and suggestions. To see latest version, refer to the PR at Add EIP: Clustered ERC-721 by sullof · Pull Request #7108 · ethereum/EIPs · GitHub