Abstract

This extension defines an interface that adds a naming mechanism to ERC-721 tokens. It allows each token to have a unique name with a set expiration date, ensuring uniqueness within the current NFT contract. The interface includes functions for assigning, updating, and querying names and their associated tokens, ensuring that names remain unique until they expire. The entity responsible for setting names depends on the specific use case scenario when utilizing this extension.

Motivation

As decentralized domain registration methods evolve with the integration of NFTs, we see an opportunity to extend this paradigm to the realm of usernames. By associating token IDs with usernames, we enhance the intuitive identification of entities within decentralized ecosystems.

This integration serves multiple purposes:

  • Intuitiveness: Numeric token IDs lack intuitive identification. By incorporating usernames, token IDs become more representative, improving usability.

  • Username Economy Exploration: The registration mechanism opens avenues for exploring the username economy, offering benefits such as identity verification and social interactions.

  • Synergy with NFTs: The fusion of usernames with NFTs unlocks synergistic growth, enabling novel applications like authenticated social interactions and personalized digital assets.

Specification

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

Implementers of this extension MUST have all of the following functions:

pragma solidity ^0.8.0;

/**
 * @title INameRegistry
 * @dev Interface for the NameRegistry smart contract.
 * This interface allows interaction with a NameRegistry, 
 * enabling the registration, management, and lookup of names 
 * with associated expiry dates tied to specific tokens.
 */
interface IERC7644 /* is IERC721 */ {

    /**
     * @dev Emitted when the name of a token is changed.
     * @param tokenId The token ID whose name is changed.
     * @param oldName The previous name of the token.
     * @param newName The new name assigned to the token.
     * @param expiryDate The expiry date of the new name registration.
     */
    event NameChanged(uint256 indexed tokenId, bytes32 oldName, bytes32 newName, uint256 expiryDate);

    /**
     * @dev Returns the name of the specified token, if the name has not expired.
     * @param tokenId The token ID to query for its name.
     * @return The name of the token, or an empty bytes32 if no name is set or it has expired.
     */
    function nameOf(uint256 tokenId) external view returns (bytes32);

    /**
     * @dev Returns the token ID associated with a given name, if the name registration has not expired.
     * @param _name The name to query for its associated token ID.
     * @return The token ID associated with the name, or zero if no token is found or the name has expired.
     */
    function tokenIdOf(bytes32 _name) external view returns (uint256);

    /**
     * @dev Allows a token owner to set or update the name of their token, subject to a duration for the name's validity.
     * @param tokenId The token ID whose name is to be set or updated.
     * @param _name The new name to assign to the token.
     * @param duration The duration in seconds for which the name is valid, starting from the time of calling this function.
     * Note: The name must be unique and not currently in use by an active (non-expired) registration.
     */
    function setName(uint256 tokenId, bytes32 _name, uint256 duration) external;

    /**
     * @dev Returns the tokenId and expiryDate for a given name, if the name registration has not expired.
     * @param _name The name to query for its associated token ID and expiry date.
     * @return tokenId The token ID associated with the name.
     * @return expiryDate The expiry date of the name registration.
     */
    function nameInfo(bytes32 _name) external view returns (uint256 tokenId, uint256 expiryDate);
	
}

Rationale

Name Expiry

By implementing expiration periods for usernames, we introduce several advantages. This mechanism ensures a dynamic environment where unused or outdated usernames can be released, fostering a healthy ecosystem. It encourages turnover of usernames, preventing long-term hoarding and promoting active participation. Users are motivated to manage their username portfolio, renewing valuable names while relinquishing irrelevant ones. Ultimately, this fosters fairness and efficiency, ensuring naming resources are utilized effectively and refreshed to meet evolving needs.

Name Uniqueness

Enforcing unique usernames is crucial for maintaining a clear and intuitive identification system. It prevents confusion and enables seamless interactions within decentralized ecosystems. Unique usernames enhance discoverability and facilitate trust in transactions and social interactions. This requirement underscores the importance of clarity in decentralized environments, where precise identification is essential for building trust and facilitating efficient interactions.

Name Registration System

Introducing a registration system for usernames safeguards against abusive behaviors and promotes fair access to naming resources. Reservation and renewal mechanisms prevent monopolization of desirable usernames while enabling legitimate users to secure names of interest. Reservation ensures fair opportunities to claim desired usernames, preventing hoarding and speculative activities. Renewal mechanisms encourage active engagement and investment in the naming ecosystem. Together, these features create a balanced and inclusive environment, fostering a vibrant community of users.

Backwards Compatibility

This standard is fully ERC-721 compatible.

Reference Implementation

pragma solidity ^0.8.0;

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

contract ERC7644 is ERC721 {
    event NameChanged(uint256 indexed tokenId, bytes32 oldName, bytes32 newName, uint256 expiryDate);

    struct NameRegistration {
        uint256 tokenId;
        uint256 expiryDate;
    }

    mapping(uint256 => bytes32) private _tokenNames;
    mapping(bytes32 => NameRegistration) private _nameRegistrations;
    mapping(uint256 => uint256) private _lastSetNameTime;

    uint256 public constant MAX_DURATION = 10 * 365 days;
    uint256 public constant MIN_SET_NAME_INTERVAL = 1 days;

    constructor() ERC721("Asd Token", "ASDT") {}

    function nameOf(uint256 tokenId) public view returns (bytes32) {
        if(_tokenNames[tokenId] != bytes32(0) && _nameRegistrations[_tokenNames[tokenId]].expiryDate > block.timestamp)
        {
            return _tokenNames[tokenId];
        }else{
            return bytes32(0);
        }
    }

    function tokenIdOf(bytes32 _name) public view returns (uint256) {
        require(_nameRegistrations[_name].expiryDate > block.timestamp, "NameRegistry: Name expired");
        if(_nameRegistrations[_name].tokenId > 0)
        {
            return _nameRegistrations[_name].tokenId;
        }else{
            return uint256(0);
        }
    }

    function setName(uint256 tokenId, bytes32 _name, uint256 duration) public {
        require(ownerOf(tokenId) == msg.sender, "NameRegistry: Caller is not the token owner");
        require(duration <= MAX_DURATION, "NameRegistry: Duration exceeds maximum limit");
        require(block.timestamp - _lastSetNameTime[tokenId] >= MIN_SET_NAME_INTERVAL, "NameRegistry: Minimum interval not met");
        require(tokenIdOf(_name) == uint256(0) || tokenIdOf(_name) == tokenId, "NameRegistry: Name already in use and not expired");

        bytes32 oldName = _tokenNames[tokenId];
        uint256 expiryDate = block.timestamp + duration;
        _setTokenName(tokenId, _name, expiryDate);

        emit NameChanged(tokenId, oldName, _name, expiryDate);

        _lastSetNameTime[tokenId] = block.timestamp;
    }

    function nameInfo(bytes32 _name) public view returns (uint256, uint256) {
        require(_nameRegistrations[_name].tokenId > 0 && _nameRegistrations[_name].expiryDate > block.timestamp, "NameRegistry: Name expired or does not exist");
        NameRegistration memory registration = _nameRegistrations[_name];
        return (registration.tokenId, registration.expiryDate);
    }

    function _setTokenName(uint256 tokenId, bytes32 _name, uint256 expiryDate) internal {
        _tokenNames[tokenId] = _name;
        _nameRegistrations[_name] = NameRegistration(tokenId, expiryDate);
    }
}

Security Considerations

Mitigating Abusive Behaviors and Resource Hoarding

The design includes mechanisms to prevent abusive behaviors and resource hoarding. Minimum intervals for name setting and maximum durations for name expiry are established to deter spam and malicious attacks, limit rapid consecutive name registrations, and encourage fair and efficient use of naming resources. These measures mitigate potential security risks, ensuring names cannot be monopolized indefinitely and promoting a sustainable and equitable environment for all users.

Username Restrictions

To facilitate indexing and gas efficiency, usernames should adhere to a length constraint of 3 to 32 characters. This range prevents the registration of overly long names, which can be costly in terms of gas and difficult to manage. Limiting characters to the range of [a-zA-Z0-9] enhances readability and prevents the abuse of the naming system by restricting the use of special characters that could complicate domain resolution or user recognition. Implementing these constraints not only promotes a high level of usability within the ecosystem but also guards against the proliferation of spam registrations, ensuring that the registry remains accessible and functional for genuine users.

Copyright and related rights waived via CC0.