ERC-6551 Token bound accounts owned by NFTs (Non-fungible Token Bound Accounts)

Review · NFTs & multi-token · created 2023-02-23

Gives every NFT a unique smart contract account, allowing the NFT to directly hold assets and interact with apps. There is no need to modify existing NFT contracts.

Why it is needed

NFTs (non-fungible tokens) can represent a unique asset, but cannot themselves own or act on anything. This means an NFT is merely an item that sits inside a wallet and cannot be used like a wallet. As a result, it is difficult to represent with a single NFT real-world assets such as a game character that accumulates equipment and abilities over time, a car composed of multiple parts, an investment portfolio holding multiple assets, or a membership card that accumulates usage history. Attaching item tokens to a character NFT requires building a separate contract mechanism each time, and transferring the attached assets along with the NFT when it is sold is also cumbersome. This standard aims to give every NFT the same rights as an Ethereum user. It does this by assigning each NFT a dedicated account address computed deterministically, and giving control of that account to the NFT's holder.

What to implement

In depth

Singleton registry and fixed address

The registry is a singleton contract that serves as the single entry point for looking up every token bound account address. It has only two functions, createAccount and account, and is permissionless, immutable, and ownerless. The registry must be deployed to address 0x000000006551c19487814612e58FE06813775758 using Nick's Factory (0x4e59b44847b379578588920cA78FbF26c0B4956C) and salt 0x0000000000000000000000000000000000000000fd8eb4e1dca713016c518e31. The single transaction given in the original text can deploy the registry to the same address on any EVM compatible chain. Because there is a single ownerless registry, accounts only need to trust one implementation contract, and events and address lookups are consolidated in one place, simplifying indexing.

ERC-1167 proxies and NFT information embedded in bytecode

The registry deploys each account as an ERC-1167 minimal proxy, but appends immutable constant data after the bytecode. The deployed bytecode must follow this structure: the ERC-1167 header (10 bytes), the implementation address (20 bytes), the ERC-1167 footer (15 bytes), followed by salt (bytes32), chainId (uint256), tokenContract (address), and tokenId (uint256), each 32 bytes. The implementation contract reads this data with extcodecopy to know which NFT it is bound to, and because the data is embedded in the code, it cannot be changed. Every account must be deployed with create2, and its address is deterministically derived from a combination of the implementation address, token contract address, token ID, chain ID, and salt.

Account interface: determining the owning NFT and the signer

Every account implementation must implement ERC-165 interface detection and ERC-1271 signature verification, and must implement the IERC6551Account interface (identifier 0x6faff5f1). token() returns the chain ID, token contract, and token ID of the NFT that owns the account, and this value must never change. state() returns a value that must change whenever the account's state changes. isValidSigner must return the magic value 0x523e3260 if the given address is authorized to act on behalf of the account. By default the NFT holder must be treated as a valid signer, but an implementation may add extra logic to invalidate the holder or grant signing authority to other addresses.

The execution interface is left open, but must be announced via ERC-165

Every account must implement an execution interface through which a valid signer can perform arbitrary operations, and must announce that support via ERC-165. However, no specific interface is mandated. As one option, the suggested IERC6551Executable (identifier 0x51945447) execute function must accept operation values of 0 (CALL), 1 (DELEGATECALL), 2 (CREATE), and 3 (CREATE2), and may support additional operations or restrict execution to specific signers. If execution fails, the error is propagated and the call reverts. The execution interface is left unfixed so as to accommodate the many execution interfaces that already exist and to remain compatible with interfaces that may be standardized in the future.

Counterfactual accounts and cross-chain addresses

Because every account is created with create2, an account can exist in a counterfactual state, with its address fixed before actual deployment, and can receive assets in advance at that address. In addition, thanks to the singleton registry at a known fixed address, the same account can exist on multiple chains. Because createAccount takes chainId as a parameter, the account contract can be deployed at the same address on any supported chain, and it is possible to implement cross-chain execution in which an NFT on one chain controls its own account on another chain. Since token identifiers are unique only within a single chain, the chain ID is used together with the contract address and token ID when identifying an NFT.

Why multiple accounts per NFT are allowed

This standard does not limit each NFT to a single account. By varying the salt and implementation address, one NFT can have multiple accounts. An alternative design limiting each NFT to a single account was considered but not adopted. First, since smart contracts are permissionless, anyone can deploy an additional registry, so a single-account limit could not be enforced in practice anyway. Second, enforcing a single account would require including a fixed, trusted implementation in the standard, which would constrain the account's capabilities and was judged premature. Third, entities active on-chain commonly use multiple accounts, such as a hot account for daily use and a cold account for storage, and the view taken was that NFTs should have the same capability.

Backward compatibility and proposal status

This standard does not extend ERC-721 and requires no changes to existing NFT contracts. The registry does not check via ERC-165 whether a token is ERC-721 before creating an account. This makes it compatible with contracts that predate ERC-721, such as CryptoKitties, and with things like ENS NameWrapper names that implement only part of the ERC-721 interface, and it can also be used with non-fungible and fungible tokens alike (though that use case is outside the scope of this proposal). If needed, an account implementation can add its own ERC-721 interface check. This proposal is currently in Review status, meaning its contents may still change.

Things to watch out for

Original ERC · ← ERCs