Abstract

This proposal defines a registry for generic services linked to specific non-fungible tokens (NFTs), i.e., contracts extending an NFT, owned by a single NFT and thus by the owner of the NFT. It achieves this goal using generic language for functions, errors, and events, and avoids conflicting with strict restrictions imposed by other proposals.

Motivation

Existing proposals aim to bind smart accounts to tokens, allowing their registries to deploy accounts owned by specific token IDs. The issue we attempt to address with this new proposal is that these proposals often explicitly require any contract deployed via their registries to implement specific interfaces to handle assets and execute transactions, effectively mandating that the deployed contract must be an account. This requirement is underscored by the choices for the names of functions and events in their interfaces. Additionally, some proposals specify that their registry smart contracts are deployed as singletons at specific addresses on any chain. Due to this centralization of services, projects building on them are prone to consider any contract deployed via that registry that is not an account as spam or invalid.

With this new ERC, we propose a generic registry that uses generic function/event names to allow the deployment of any kind of contract that makes sense when associated with an NFT, so that the contract is under the full control of the NFT’s owner. Since one of this proposal’s goals is flexibility, there is no expectation for an ERC7656Registry contract to be deployed as a singleton, allowing any project to adjust it to their needs; consequently, we require that any registry explicitly supports the IERC7656Registry interface.

The expansion of the registry’s capabilities to manage contracts implementing any kind of service beyond accounts provides several advantages:

  • Flexibility: Developers can allow NFTs to interact with a broader range of linked contracts, unlocking new use cases and functionalities (lending systems, vested asset distribution, fractional ownership, identity, etc.)
  • Compatibility: By ensuring that account-like contracts can still be identified as such, the proposal maintains backward compatibility with existing account-based proposals.
  • Innovation: This proposal encourages further innovation in the NFT space by removing limitations on the types of contracts that can be associated with NFTs, opening the door to pure-utility NFTs.

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.

The interface IERC7656Registry is defined as follows:

// interfaceId 0xc6bdc908
interface IERC7656Registry {
  /**
   * @notice The registry MUST emit the Created event upon successful contract creation.
   * @param contractAddress The address of the created contract
   * @param implementation The address of the implementation contract
   * @param salt The salt to use for the create2 operation
   * @param chainId The chain id of the chain where the contract is being created
   * @param tokenContract The address of the token contract
   * @param tokenId The id of the token
   */
  event Created(
    address contractAddress,
    address indexed implementation,
    bytes32 salt,
    uint256 chainId,
    address indexed tokenContract,
    uint256 indexed tokenId
  );

  /**
   * The registry MUST revert with CreationFailed error if the create2 operation fails.
   */
  error CreationFailed();

  /**
   * @notice Creates a token linked service for a non-fungible token.
   * If the service has already been created, returns the service address without calling create2.
   * @param implementation The address of the implementation contract
   * @param salt The salt to use for the create2 operation
   * @param chainId The chain id of the chain where the service is being created
   * @param tokenContract The address of the token contract
   * @param tokenId The id of the token
   * Emits Created event.
   * @return service The address of the token linked service
   */
  function create(
    address implementation,
    bytes32 salt,
    uint256 chainId,
    address tokenContract,
    uint256 tokenId
  ) external returns (address service);

  /**
   * @notice Returns the computed token linked service address for a non-fungible token.
   * @param implementation The address of the implementation contract
   * @param salt The salt to use for the create2 operation
   * @param chainId The chain id of the chain where the service is being created
   * @param tokenContract The address of the token contract
   * @param tokenId The id of the token
   * @return service The address of the token linked service
   */
  function compute(
    address implementation,
    bytes32 salt,
    uint256 chainId,
    address tokenContract,
    uint256 tokenId
  ) external view returns (address service);
}

Any ERC7656Registry implementation MUST support the IERC7656Registry’s interface ID, i.e., 0xc6bdc908.

The registry MUST deploy each token-linked service as an ERC-1167 minimal proxy with immutable constant data appended to the bytecode, similarly to existing token-bound account proposals.

The deployed bytecode of each token-bound service MUST have the following structure:

ERC-1167 Header               (10 bytes)
<implementation (address)>    (20 bytes)
ERC-1167 Footer               (15 bytes)
<salt (bytes32)>              (32 bytes)
<chainId (uint256)>           (32 bytes)
<tokenContract (address)>     (32 bytes)
<tokenId (uint256)>           (32 bytes)

Any contract created using a ERC7656Registry SHOULD implement the IERC7656Service interface:

// InterfaceId 0xfc0c546a
interface IERC7656Service {
  /**
  * @notice Returns the token linked to the contract
  * @return chainId The chainId of the token
  * @return tokenContract The address of the token contract
  * @return tokenId The tokenId of the token
  */
  function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId);
  
}

or an account interface or both. This flexibility makes existing account contracts compatible with this proposal out-of-the-box.

Rationale

The technical foundation of ERC-7656 centers on the extension and generalization of contract types that can be associated with NFTs. This approach was chosen to address specific limitations and opportunities identified in the design and application of NFT-linked contracts. Key technical decisions in this proposal include:

  • Addressing ERC Proliferation and Contract Size Limitations: The increasing number of ERC proposals extending the ERC-721 standard has led to complexity and potential confusion within the developer community. Since smart contracts have size limitations, incorporating multiple extensions into a single contract can become impractical or even impossible. ERC-7656 offers a solution by enabling the deployment of services that extend standard ERC-721 NFTs without requiring modifications to the ERC-721 standard itself. For example, instead of creating new ERCs to associate real-world assets with NFTs, developers can deploy specialized smart contracts owned by NFTs to add these features. This approach allows NFTs to seamlessly expand their capabilities while maintaining compliance with the existing ERC-721 standard, promoting a more efficient and modular development process.

  • Generic Function/Event Names: The choice to adopt a generic naming convention for functions, errors, and events is deliberate. This design decision enables the ERC-7656 registry to support a wide array of contract types beyond mere accounts. By not prescribing specific roles or functionalities, we allow for greater innovation and flexibility in the types of applications that can be built on this standard. This also simplifies the interface and makes it more adaptable to various use cases.

  • No Singleton Requirement for the Registry: Unlike some previous proposals, ERC-7656 does not mandate that the registry be deployed as a singleton. This decision was influenced by the recognition that different projects may have unique requirements and constraints. By allowing for multiple instances of the registry, projects can customize and optimize the registry’s deployment to fit their specific needs, enhancing the ecosystem’s overall diversity and resilience. However, for convenience, the registry has been deployed to the erc7656.eth address on the most used networks.

  • Explicit Support for the IERC7656Registry Interface: Requiring that any registry explicitly supports the IERC7656Registry interface is a technical decision aimed at ensuring interoperability and recognition. This requirement facilitates the identification and interaction with compliant registries, promoting a more standardized and cohesive ecosystem.

  • Flexibility in Contract Association: The proposal is designed to accommodate not just accounts, but any contract that can meaningfully be associated with an NFT. This decision stems from a technical evaluation of the evolving landscape of NFT use cases, recognizing the need for a standard that can support a broader range of functionalities, from complex financial instruments to identity verification systems.

  • Backward Compatibility: The proposal includes provisions for backward compatibility, particularly with account-like contracts from existing standards. This technical choice ensures that projects built on earlier standards can transition to or leverage the new standard without discarding existing infrastructure or investments.

These technical decisions collectively aim to broaden the scope and applicability of NFT-linked contracts, empower developers with more tools for innovation, and support a growing ecosystem of decentralized applications. By addressing both current limitations and future opportunities, ERC-7656 seeks to lay a flexible and robust foundation for the next generation of NFT technologies.

Reference Implementation

See ERC7656Registry.sol for an example implementation of IERC7656Registry.

An example of implementation of IERC7656Service:

contract LinkedService is IERC7656Service, EIP5313 {

  function token() public view virtual returns (uint256, address, uint256) {
    bytes memory footer = new bytes(0x60);
    assembly {
      extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)
    }
    return abi.decode(footer, (uint256, address, uint256));
  }

  function owner() public view virtual override returns (address) {
    (uint256 chainId, address tokenContract_, uint256 tokenId_) = token();
    if (chainId != block.chainid) return address(0);
    return IERC721(tokenContract_).ownerOf(tokenId_);
  }
}

Security Considerations

Fraud Prevention when linking accounts to NFTs

In order to enable trustless sales of token bound accounts, decentralized marketplaces will need to implement safeguards against fraudulent behavior by malicious account owners.

Consider the following potential scam:

  • Alice owns an ERC-721 token X, which owns token bound account Y.
  • Alice deposits 10ETH into account Y
  • Bob offers to purchase token X for 11ETH via a decentralized marketplace, assuming he will receive the 10ETH stored in account Y along with the token
  • Alice withdraws 10ETH from the token bound account, and immediately accepts Bob’s offer
  • Bob receives token X, but account Y is empty

To mitigate fraudulent behavior by malicious account owners, decentralized marketplaces SHOULD implement protection against these sorts of scams at the marketplace level. Contracts which implement this EIP MAY also implement certain protections against fraudulent behavior.

Here are a few mitigations strategies to be considered:

  • Attach the current token bound account state to the marketplace order. If the state of the account has changed since the order was placed, consider the offer void. This functionality would need to be supported at the marketplace level.
  • Attach a list of asset commitments to the marketplace order that are expected to remain in the token bound account when the order is fulfilled. If any of the committed assets have been removed from the account since the order was placed, consider the offer void. This would also need to be implemented by the marketplace.
  • Submit the order to the decentralized market via an external smart contract which performs the above logic before validating the order signature. This allows for safe transfers to be implemented without marketplace support.
  • Implement a locking mechanism on the token bound account implementation that prevents malicious owners from extracting assets from the account while locked

Preventing fraud is outside the scope of this proposal.

Ownership Cycles

All assets held in a token bound account may be rendered inaccessible if an ownership cycle is created. The simplest example is the case of an ERC-721 token being transferred to its own token bound account. If this occurs, both the ERC-721 token and all of the assets stored in the token bound account would be permanently inaccessible, since the token bound account is incapable of executing a transaction which transfers the ERC-721 token.

Ownership cycles can be introduced in any graph of n>0 token bound accounts. On-chain prevention of cycles with depth>1 is difficult to enforce given the infinite search space required, and as such is outside the scope of this proposal. Application clients and account implementations wishing to adopt this proposal are encouraged to implement measures that limit the possibility of ownership cycles.

Copyright and related rights waived via CC0.