This proposal defines a system which gives every ERC-721 token a smart contract account. These token bound accounts allow ERC-721 tokens to own assets and interact with applications, without requiring changes to existing ERC-721 smart contracts or infrastructure.
Motivation
The ERC-721 standard enabled an explosion of non-fungible token applications. Some notable use cases have included breedable cats, generative artwork, and liquidity positions.
Non-fungible tokens are increasingly becoming a form of on-chain identity. This follows quite naturally from the ERC-721 specification - each non-fungible token has a globally unique identifier, and by extension, a unique identity.
Unlike other forms of on-chain identity, ERC-721 tokens cannot act as an agent or associate with other on-chain assets. This limitation stands in contrast with many real-world instances of non-fungible assets. For example:
A character in a role-playing game that accumulates assets and abilities over time based on actions they have taken
An automobile composed of many fungible and non-fungible components
An automated investment portfolio composed of multiple fungible assets
A punch pass membership card granting access to an establishment and recording a history of past interactions
Several proposals have attempted to give ERC-721 tokens the ability to own assets. Each of these proposals have defined an extension to the ERC-721 standard. This requires smart contract authors to include proposal support in their ERC-721 token contracts. As a result, these proposals are largely incompatible with previously deployed ERC-721 contracts.
This proposal grants every ERC-721 token the full capabilities of an Ethereum account while maintaining backwards compatibility with previously deployed ERC-721 token contracts. It does so by deploying unique, deterministically-addressed smart contract accounts for each ERC-721 token via a permissionless registry.
Each token bound account is owned by a single ERC-721 token, allowing the token to interact with the blockchain, record transaction history, and own on-chain assets. Control of each token bound account is delegated to the owner of the ERC-721 token, allowing the owner to initiate on-chain actions on behalf of their token.
Token bound accounts are compatible out of the box with nearly all existing infrastructure that supports Ethereum accounts, from on-chain protocols to off-chain indexers. Token bound accounts can own any type of on-chain asset, and can be extended to support new asset types created in the future.
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.
Overview
The system outlined in this proposal has two main components:
A permissionless registry for deploying token bound accounts
A standard interface for token bound account implementations
The following diagram illustrates the relationship between ERC-721 tokens, ERC-721 token owners, token bound accounts, and the Registry:
Registry
The registry serves as a single entry point for projects wishing to utilize token bound accounts. It has two functions:
createAccount - deploys a token bound account for an ERC-721 token given an implementation address
account - a read-only function that computes the token bound account address for an ERC-721 token given an implementation address
The registry SHALL deploy each token bound account as an ERC-1167 minimal proxy with immutable constant data appended to the bytecode.
The the deployed bytecode of each token bound account SHALL have the following structure:
For example, the token bound account with implementation address 0xbebebebebebebebebebebebebebebebebebebebe, salt 0, chain ID 1, token contract 0xcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf and token ID 123 would have the following deployed bytecode:
Each token bound account proxy SHALL delegate execution to a contract that implements the IERC6551Account interface.
The registry contract is permissionless, immutable, and has no owner. The complete source code for the registry can be found in the Registry Implementation section below. The registry SHALL be deployed at address TBD using Nick’s Factory (0x4e59b44847b379578588920cA78FbF26c0B4956C) with salt 0x6551655165516551655165516551655165516551655165516551655165516551.
The registry SHALL deploy all token bound account contracts using the create2 opcode so that the account address for every ERC-721 token is deterministic. The account address for each ERC-721 token SHALL be derived from the unique combination of implementation address, token contract address, token ID, EIP-155 chain ID, and an optional salt.
The registry SHALL implement the following interface:
interfaceIERC6551Registry{/// @dev The registry SHALL emit the AccountCreated event upon successful account creation
eventAccountCreated(addressaccount,addressimplementation,uint256chainId,addresstokenContract,uint256tokenId,uint256salt);/// @dev Creates a token bound account for an ERC-721 token.
///
/// If account has already been created, returns the account address without calling create2.
///
/// If initData is not empty and account has not yet been created, calls account with
/// provided initData after creation.
///
/// Emits AccountCreated event.
///
/// @return the address of the account
functioncreateAccount(addressimplementation,uint256chainId,addresstokenContract,uint256tokenId,uint256salt,bytescalldatainitData)externalreturns(address);/// @dev Returns the computed address of a token bound account
///
/// @return The computed address of the account
functionaccount(addressimplementation,uint256chainId,addresstokenContract,uint256tokenId,uint256salt)externalviewreturns(address);}
Account Interface
All token bound accounts SHOULD be created via the registry.
All token bound account implementations MUST implement ERC-165 interface detection.
All token bound account implementations MUST implement ERC-1271 signature validation.
All token bound account implementations MUST implement the following interface:
/// @dev the ERC-165 identifier for this interface is `0x400a0398`
interfaceIERC6551Account{/// @dev Token bound accounts MUST implement a `receive` function.
///
/// Token bound accounts MAY perform arbitrary logic to restrict conditions
/// under which Ether can be received.
receive()externalpayable;/// @dev Executes `call` on address `to`, with value `value` and calldata
/// `data`.
///
/// MUST revert and bubble up errors if call fails.
///
/// By default, token bound accounts MUST allow the owner of the ERC-721 token
/// which owns the account to execute arbitrary calls using `executeCall`.
///
/// Token bound accounts MAY implement additional authorization mechanisms
/// which limit the ability of the ERC-721 token holder to execute calls.
///
/// Token bound accounts MAY implement additional execution functions which
/// grant execution permissions to other non-owner accounts.
///
/// @return The result of the call
functionexecuteCall(addressto,uint256value,bytescalldatadata)externalpayablereturns(bytesmemory);/// @dev Returns identifier of the ERC-721 token which owns the
/// account
///
/// The return value of this function MUST be constant - it MUST NOT change
/// over time.
///
/// @return chainId The EIP-155 ID of the chain the ERC-721 token exists on
/// @return tokenContract The contract address of the ERC-721 token
/// @return tokenId The ID of the ERC-721 token
functiontoken()externalviewreturns(uint256chainId,addresstokenContract,uint256tokenId);/// @dev Returns the owner of the ERC-721 token which controls the account
/// if the token exists.
///
/// This is value is obtained by calling `ownerOf` on the ERC-721 contract.
///
/// @return Address of the owner of the ERC-721 token which owns the account
functionowner()externalviewreturns(address);/// @dev Returns a nonce value that is updated on every successful transaction
///
/// @return The current account nonce
functionnonce()externalviewreturns(uint256);}
Rationale
Counterfactual Account Addresses
By specifying a canonical account registry, applications wishing to support this proposal can compute the address for a given token bound account using a certain implementation prior to the deployment of the contract for that account. This allows assets to be sent securely to the owner of a token without needing to know the owner’s address. A canonical registry also allows client side applications to query for assets owned by a token from a single entry point.
Account Ambiguity
The specification proposed above allows ERC-721 tokens to have multiple token bound accounts, one per implementation address. During the development of this proposal, alternative architectures were considered which would have assigned a single token bound account to each ERC-721 token, making each token bound account address an unambiguous identifier.
However, these alternatives present several trade offs.
First, due to the permissionless nature of smart contracts, it is impossible to enforce a limit of one token bound account per ERC-721 token. Anyone wishing to utilize multiple token bound accounts per ERC-721 token could do so by deploying an additional registry contract.
Second, limiting each ERC-721 token to a single token bound account would require a static, trusted account implementation to be included in this proposal. This implementation would inevitably impose specific constraints on the capabilities of token bound accounts. Given the number of unexplored use cases this proposal enables and the benefit that diverse account implementations could bring to the non-fungible token ecosystem, it is the authors’ opinion that defining a canonical and constrained implementation in this proposal is premature.
Finally, this proposal seeks to grant ERC-721 tokens the ability to act as agents on-chain. In current practice, on-chain agents often utilize multiple accounts. A common example is individuals who use a “hot” account for daily use and a “cold” account for storing valuables. If on-chain agents commonly use multiple accounts, it stands to reason that ERC-721 tokens ought to inherit the same ability.
Proxy Implementation
ERC-1167 minimal proxies are well supported by existing infrastructure and are a common smart contract pattern. This proposal deploys each token bound account using a custom ERC-1167 proxy implementation that stores the salt, implementation address, chain id, token contract address, and token ID as ABI-encoded constant data appended to the contract bytecode. This allows token bound account implementations to easily query this data while ensuring it remains constant. This approach was taken to maximize compatibility with existing infrastructure while also giving smart contract developers full flexibility when creating custom token bound account implementations.
EIP-155 Support
This proposal uses EIP-155 chain IDs to identify ERC-721 tokens along with their contract address and token ID. ERC-721 token identifiers are globally unique on a single Ethereum chain, but may not be unique across multiple Ethereum chains. Using chain IDs to uniquely identify ERC-721 tokens allows smart contract authors wishing to implement this proposal to optionally support multi-chain token bound accounts.
Backwards Compatibility
This proposal seeks to be maximally backwards compatible with existing non-fungible token contracts. As such, it does not extend the ERC-721 standard.
Additionally, this proposal does not require the registry to perform an ERC-165 interface check for ERC-721 compatibility prior to account creation. This is by design in order to maximize backwards compatibility with non-fungible token contracts that pre-date the ERC-721 standard, such as Cryptokitties. Smart contract authors implementing this proposal may optionally choose to enforce interface detection for ERC-721.
Non-fungible token contracts that do not implement an ownerOf method, such as Cryptopunks, are not compatible with this proposal. The system outlined in this proposal could be used to support such collections with minor modifications, but that is outside the scope of this proposal.
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 nonce to the marketplace order. If the nonce 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 an 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 it’s 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 these cycles 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.