ERC-7656 - Generalized Token-Linked Services

Created 2024-03-15
Status Last Call
Category ERC
Type Standards Track
Authors
Requires

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:

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.

Registry Interface

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.

Deployment Requirements

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)

Recommended Service Interface

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);

}

but if the service is a specific type of contract that implements its own interface, like for example token bound smart-wallets, supporting that interface ID is acceptable, to make it compatible out-of-the-box with alternative existing standards.

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:

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

Ownership Cycles

If a token-linked service functions as a smart wallet, an ownership cycle can render all assets permanently inaccessible. For example, if an ERC-721 token is transferred to the same smart wallet that it owns, neither the token nor the wallet’s assets can be retrieved, because the wallet cannot transfer the token back.

Preventing more complex ownership cycles on-chain is difficult, as it would require searching an unbounded number of possible transfers. Consequently, this proposal does not address cycle prevention. Projects adopting this proposal SHOULD include their own safeguards against such scenarios.

Fraud Prevention when linking accounts to NFTs

Token-linked services can take many forms—such as subscription services, smart wallets, or renting platforms—and a malicious seller could exploit this flexibility by altering or removing critical components just before finalizing a sale. For instance, the seller might transfer out assets from a wallet service, revoke access from a subscription, or break a rental agreement at the last moment, leaving the buyer with a compromised or worthless service.

Because this proposal accommodates a wide range of services, there is no one-size-fits-all fraud mitigation strategy. However, a common approach is to implement a lock mechanism that restricts changes for a certain period before the sale is completed, or until the buyer confirms the purchase. Such functionality MAY be included by service developers but is not mandated by this proposal.

Malicious or Unverified Implementations

Since the registry cannot ensure that only the legitimate NFT owner can create services for a given token, there is a risk that malicious or unverified code could be used. Users and marketplaces SHOULD carefully review or audit any implementation before linking it to an NFT.

Upgradability and Governance Risks

If a token-linked service is upgradable, the current owner (or a compromised owner) could upgrade the contract to exfiltrate assets or change functionality unexpectedly. Projects SHOULD implement secure upgrade mechanisms, such as time-locked upgrades or multi-signature approvals.

Re-entrancy and Cross-Contract Interactions

Token-linked services (especially those that hold assets or interact with external protocols) may be vulnerable to re-entrancy attacks or other cross-contract exploits. Implementers SHOULD follow standard security patterns and best practices.

Denial of Service (DoS)

If a service is designed or implemented incorrectly, it may become impossible to execute certain calls or transfer ownership, resulting in a DoS scenario. Implementers SHOULD consider fail-safes or recovery methods to prevent indefinite lockouts.

User Education and Phishing Risks

Even with secure smart contracts, end-users can still be tricked into interacting with fraudulent services. Clear user interfaces, warnings, and contract verifications (e.g., Etherscan or similar tools) SHOULD be encouraged to reduce phishing and social engineering risks.

Copyright

Copyright and related rights waived via CC0.