ERC-6551: Non-fungible Token Bound Accounts

When integrating the ERC6551Registry contract into other smart contracts, there is currently no way to securely validate that a passed address parameter is in fact the official ERC6551Registry address.

This is because ERC6551Registry in the reference implementation does not implement ERC165, the standard interface detection pattern. As a result, even though ERC6551Registry extends IERC6551Registry, a consuming contract cannot actually detect and prove the interface is properly implemented.

I can make the change and raise a PR for it, if you like.

Thanks for the comments @arrans!

I agree that this should be fleshed out more. There has been a lot of research done in the area of fraud prevention that should be highlighted in the proposal.

Since state is managed by the account and all operations that interact with tokens must be executed via the account, state commitments can ensure that no tokens have been transferred away from the account via direct transactions. The edge case here is approval-based transfers, which would not change account state.

However, account state can be deterministic based on operations executed from the account. This would allow commitments to future states that are only reachable if certain conditions are met (e.g. all active approvals have been revoked). Using deterministic state mitigates the shortcomings of the currently suggested fraud prevention schemes. We are currently considering how this should be written into the proposal, and would love your feedback.

This is a good point, and should be considered in a future proposal.

Since the Registry is a singleton contract whose address will be well-known and included in the proposal, contracts can trivially validate whether a given address matches the canonical Registry address. This address check also guarantees that the IERC6551Registry interface is properly implemented. As such, unlike other ERCs where multiple instances of a contract implementing a certain interface are expected, I don’t think ERC-165 interface detection on the Registry provides much benefit here.

That said, I’m not opposed to adding interface detection if there is a enough demand or a compelling argument for it.

1 Like

I’d like to point out a few considerations with the Create2 approach. Ensuring the same address across different blockchains involves adhering to a meticulous process which may not always be feasible. Imagine a scenario where a novel blockchain emerges in the future, and the nonce on that blockchain isn’t consistent with the wallet’s previous deployment nonce. Or consider wanting to leverage an optimized proxy size for new blockchains. There could be numerous reasons making it challenging to maintain the same address universally.

Now, let’s examine this from the perspective of a developer working with ERC6551. When coding the smart contract, hardcoding the address isn’t viable, as it would only be operational on the mainnet and not on staging or during development. Hence, the registry address would be input as a deployment parameter. But then arises the challenge: how does one ensure minimalistic yet effective validation of the address to prevent unintended inclusions? Currently, a feasible approach is implementing a try-catch mechanism and verifying the output of the ERC6551Registry.account function. However, in my opinion, a standardized support for the interface would streamline this process and enhance its robustness.

I don’t think this is an accurate characterization. The proposal specifies that the registry must be deployed using Nick’s factory in order to ensure the widest possible support across EVM chains. This factory exists on 31 chains according to BlockScan, and can be deployed to any EVM compatible chain using a pre-signed transaction. Additionally, because this factory is widely used, several chains that do not support pre-signed transactions have included it as a pre-deployed contract.

This proposal is dependent on the create2 opcode, which allows for deployments that are not tied to a given account nonce. Chains that do not support create2 will not be able to support ERC-6551 as a whole (as it requires EVM compatibility), regardless of the method used to deploy the registry contract.

If there are additional optimizations you would like to see included in the proxy bytecode I would love to discuss them and see if there is a way they could be incorporated into this proposal.

The registry can indeed be deployed to both testnets and local development environments at its canonical address. Instructions for how to do so should be documented, this would be a good addition to the reference implementation repo.

If the registry is deployed to staging / testnet environments, this is no longer an issue for developers.

I don’t think ERC-165 would be any more useful than an address equality check for this use case. ERC-165 makes a lot of sense for use cases where many contracts implement a common interface (such as the ERC-6551 accounts), but not as much sense for singleton contracts.

There are blockchains that enforce EIP155-like replay protection for security reasons. If that happens, Nick’s method doesn’t work because the chainId has to be part of the transaction. Did you try to deploy it on CELO or CELO Alfajores?

PS > I use CELO Alfajores as favorite testnet.

Nick’s factory already exists on Alfajores despite Celo’s lack of support for pre-signed transactions. As a result, the registry can be deployed without issue. Any other chains that enforce replay protection can include this factory as a pre-deployed contract as Celo has done.

1 Like

Then I don’t have other questions. Thanks

1 Like

Interesting! Paraphrasing to confirm that I understand: we can prove that it was impossible to perform a chain of actions that result in the assets being transferred therefore it is safe to assume that no fraud can be committed.

I think from a technical perspective this is a really elegant solution however I think that buyers still risk being cheated because it’s so much harder to reason about relative to the alternative of declaring transitively owned assets that the buyer expects. That said, I’m keen to see what you write up so please do share it.

Just checking in after reviewing the last draft
Really good progress, as I was reading, was skeptical at first of the multi-implementation per NFT but I agree in the end that is it actually a great way to not constraint implementations. Nice!

I got few comments

  1. Feel like the Account standard should be a separate ERC.
  2. on that note, execution standards has been attempted before and I wonder why you chose to use a different spec than ERC725X (ERC-725: General data key/value store and execution) which define 2 functions:

function execute(uint256 operationType, address target, uint256 value, bytes memory data) external payable returns(bytes memory)

function executeBatch(uint256[] memory operationsType, address[] memory targets, uint256[] memory values, bytes[] memory datas) external payable returns(bytes[] memory)

It is really similar than the one specified in 6551 except for

  • it has STATICCALL
  • it uses 256 bit to represent operation type
  • it support batch operation in its interface
  • have associated events

@wighawag thanks, that means a lot! I definitely agree that there should be a separate smart account execution interface standard, and that this will likely become standardized in the future. Unfortunately there is no strong standard that this proposal can adopt right now, which is why an optional, minimal execution interface has been included in the spec. In my opinion this strikes a balance between having a standard interface across 6551 accounts right now while also allowing future standardization to be adopted without changes to the proposal.

Unfortunately, because ERC-725 has been in draft stage for a long time and does not have a clear path towards finalization, including it as a dependency would mean that ERC-6551 could not become finalized before ERC-725 does.

There are also several developer experience papercuts that exist in ERC-725X that have been adjusted in the recommended ERC-6551 execution interface:

  • using uint8 to represent operation types allows implementations to represent the value of operation as either a uint8 or an enum in their codebase without affecting the execution function selector
  • ERC-725X’s representation of arguments to executeBatch as arrays rather than as a single array of structs introduces additional calldata overhead (and causes two of the parameter names to be grammatically incorrect)
  • ERC-725X’s interpretation of operationType values is different than the equivalent values in most common smart contract accounts (e.g. Safe, Kernel), where an operation value of 1 usually represents DELEGATECALL

If ERC-725 were to be changed to fix these issues and championed towards finalization, or another proposal to standardize the execution interfaces were to gain steam, I would fully support externalizing the account execution interface.

Would love to hear @frozeman’s thoughts on this.