Title: ERC_6551__Non_fungible_Token_Bound_Accounts
Category: ERCs

Author: jay
Date: 2023-02-23T16:20:15.137Z

An interface and registry for smart contract accounts owned by ERC-721 tokens.
https://github.com/ethereum/EIPs/pull/6551/files

Author: ccamrobertson
Date: 2023-02-23T19:57:03.568Z

I love the simplicity of this proposal, yet it enables significant capabilities for ERC721 creators and holders.
A few thoughts crop up:


It seems like there is significant opportunity for bad actors to deploy either duplicate registries or implementations for a given ERC721. Although I see that preventing fraud is outside the scope of this EIP, has there been thought given to providing more information about who calls createAccount? For instance I would likely have far more trust in an account created by the minter of the original account.


This might not be desirable, but I am trying to work out whether or not it would be possible to have a registry that allows for the broad registration of Account Implementations without targeting specific token IDs until a user needs to take an action with the account. I suppose this might result in something like an impliedAccount from createAccountImplementation that can be read from the contract prior to creation so that assets can be deposited to an entire collection vs. single tokenIds.


Despite the permissionless nature, is it expected that a canonical registry will (or should) emerge?


Excited to see this in the wild!

Author: jay
Date: 2023-02-25T01:15:51.778Z

These are great questions @ccamrobertson!

Yes, this does present an opportunity for fraud. The closest analogue I can think of would be airdropping “scam” NFTs into a wallet, which is quite common. This behavior is harmless to the end user so long as the malicious accounts are not interacted with. Because the address of the created account is tied to the implementation it points to, a holder can trust that an account created using a trusted implementation address is secure, regardless of who calls createAccount. The ability to permissionlessly deploy accounts for tokens that you do not own is desirable in many cases, and I think this functionality should be maintained if possible. One example is NFT creators who wish to deploy token bound accounts on behalf of their holders. Determining whether an account is trustworthy seems like something that should happen off-chain at the client level (as both the caller of createAccount and the implementation address will be queryable via transaction logs), or perhaps via an on-chain registry of trusted implementations (although that seems like a large increase in the scope of this EIP, and may warrant a separate proposal). Would love to hear any suggestions you have for how this type of fraud could be addressed within the scope of this proposal!
This is definitely an interesting idea to explore. I had considered adding a discoverAccount function which would emit an event registering an account address, but ultimately decided against it because the same data could be queried off-chain at the application level given an implementation address. Depositing assets to an entire collection is definitely an interesting use case, but the logic for this may be better implemented at the asset contract level rather than the within the context of this proposal. It might be interesting to add a registerImplementation function which emits an event notifying listeners that a new implementation is available. However, I fear this may compound the risk of malicious account implementations as it gives them an appearance of legitimacy.
Since the proposal defines both a registry implementation and a registry address that are permissionless, it is expected that this will become the canonical registry. A single entry point for token bound accounts will make it much easier for the ecosystem to adopt this proposal vs. multiple registries. The proposed registry should be flexible enough to accommodate the majority of token bound account implementations, as the EIP-1167 proxy pattern is very well supported. Of course, anyone is free to deploy their own registry implementation, or to deploy token bound account contracts without using a registry.

Author: scorpion9979
Date: 2023-02-26T17:18:33.001Z

I think this is a great concept that enables a whole new array of use cases and the best thing about it is that it would already work with any existing NFTs. I think it could also give rise to a whole new and unique model of project airdrops.

Author: jay
Date: 2023-02-26T21:47:57.045Z

Thanks @scorpion9979!
This proposal definitely has interesting implications for airdrops. It could eliminate the need for projects to capture point-in-time snapshots of token holders, since the token bound account address for each NFT is static and computable.
Additionally, because each token bound account address can be computed from the token ID of the NFT which owns it, the data required to distribute tokens could potentially be stored in a compressed format on chain. This may significantly reduce the gas cost required to perform airdrops.

Author: william1293
Date: 2023-02-27T06:02:23.696Z

As far as I know, a project called A3S Protocol has implemented a similar function, but I don’t know the difference between its implementation and your proposal.

Author: jay
Date: 2023-02-27T18:01:43.524Z

Thanks for highlighting this project @william1293! I haven’t seen it before. It looks like A3S uses a similar approach, but from a quick glance it seems like there are a few key differences:

A3S uses a single central NFT collection which it deploys smart contract accounts for. It is not compatible with other NFTs. This proposal gives every NFT the ability to have a smart contract account.
The A3S factory contract is centrally owned and upgradable by the owner. The registry defined in this proposal is neither owned or upgradable.
Each A3S account calls back into the central factory to determine ownership of the account, which theoretically gives A3S the ability to modify the owner of an A3S account without the current owner’s permission. This proposal defers ownership checks to the account implementation, allowing fully sovereign account implementations to be developed. The example account implementation defined in this proposal is sovereign by default.

In short, this proposal defines a system that gives every NFT a smart contract account in a decentralized manner. A3S seems to be a for-profit protocol company that creates smart contract accounts for their own NFT collection.

Author: hskang9
Date: 2023-02-28T17:55:56.562Z

That sounds similar to previous standard EIP-5252. I think proxy method to generate account contract would make the gas cost more efficient. I think EIP validators will finalize this one as they could not understand the code and gave up. It is such a shame they need take effort on discovering new primitives but then they are not funded when @vbuterin sells eth for $350 million.

Author: hskang9
Date: 2023-02-28T18:39:34.441Z

@SamWilsn I volunteer to review this EIP and get it finalized, and hopefully I might find someone interested to finalize my EIP as well?

Author: SamWilsn
Date: 2023-02-28T19:09:56.645Z

hskang9:

I volunteer to review this EIP and get it finalized


Always happy to have more peer reviewers!



 hskang9:

hopefully I might find someone interested to finalize my EIP as well?


ERC-5252? You can open a pull request to move it to status: Review whenever you think it’s ready. Peer review—while recommended—is optional.

Author: RobAnon
Date: 2023-02-28T21:27:18.723Z

Pretty neat, we’ve already done this with Revest FNFTs. Suggest including backwards compatibility in some manner.

Author: jay
Date: 2023-02-28T23:54:21.097Z

@hskang9 I’d love to learn more about the similarities you see between this proposal and ERC-5252. I’ve taken a read through that proposal and I’m not totally clear on how it could provide smart contract accounts for NFTs.

Author: jay
Date: 2023-03-01T00:17:53.815Z

@RobAnon thanks for sharing! From a cursory look, it seems like Revest uses ERC-1155 tokens under the hood. ERC-1155 support was considered during the development of this proposal, but ultimately decided against.
Since ERC-1155 tokens support multiple owners for a given token ID, binding an account to an ERC-1155 token would significantly complicate the ownership model of this proposal. In the case of an account bound to an ERC-1155 token, would each token holder be able to execute arbitrary transactions? Would signatures from all holders be required? Or would signatures only need to be collected from a majority of holders?
Some ERC-1155 tokens (potentially including Revest?) support single account ownership of of a ERC-1155 tokens by limiting the balance of each token ID to 1. The challenge to supporting these tokens is that the ERC-1155 standard doesn’t define a method for querying the total number of tokens in existence for a given token ID. It is therefore impossible to differentiate between ERC-1155 tokens that have multiple owners per token ID and those that have a single owner per token ID without using non-standard interfaces.
Since this proposal purposefully excludes ERC-1155 tokens from its scope, I don’t think Revest tokens can be supported in their current form. However, Revest would be welcome to implement a custom token bound account implementation that uses an alternative ownership scheme if they wish to support this proposal. Another potential solution would be to wrap the existing ERC-1155 tokens in an ERC-721 token, which would then make them compatible with this proposal.

Author: hskang9
Date: 2023-03-01T00:45:03.808Z

That is because you don’t clarify ambuiguity of account using NFT and haven’t even made a contract implementation yet. Your proposal currently have not clarified how much one’s NFT have access to its account contract and how the access will be managed. If you can’t make contract implementation, you don’t know what you are doing. What I look feasible in your proposal is unified interface for operating account bound contracts, and this is the first thing I will review. Other items will be reviewed once the contract for binding logic on each case in the proposal is implemented. Account bound finance(EIP-5252) fits in the case where one NFT manages access one contract bound to its nft owner. EIP-5252 creates account with contract clone method. Backward compatibility may be considered if this EIP tries to implement EIP-5252’s structure.
Also, one question about security considerations, have you considered adding metadata in your NFT registry and make it display in opensea metadata format? Why is this a security concern if a metadata can be retrieved from its registry for account bounding? Do you have the certain metadata format you would propose?
For example, OpenRarity have come up with their metadata to show NFT rarity.

Author: hskang9
Date: 2023-03-01T00:51:28.621Z

It would be actually great if we include each of our cases on clarifying account ambiguity into this eip and try to come up with most unified interface for all in democratic way.

Author: hskang9
Date: 2023-03-01T01:14:45.503Z

It is not true that EIP-1155 cannot be used for account bounding, account bounding can already be done by checking ownership of an EIP-1155 token of sender in account contract. Ids can actually be assigned to EIP-1155 and it is more gas efficient.

Author: jay
Date: 2023-03-01T01:36:23.981Z

@hskang9 a few comments:

haven’t even made a contract implementation yet

A fully-functional implementation is included in the “Reference Implementation” section.

If you can’t make contract implementation, you don’t know what you are doing

This seems unnecessarily hostile - let’s try to keep things civil 

Account bound finance(EIP-5252) fits in the case where one NFT manages access one contract bound to its nft owner

Right - I guess my question is whether ERC-5252 works with all existing NFT contracts? Or does it only work with NFTs that implement the ERC-5252 interface?

have you considered adding metadata in your NFT registry and make it display in opensea metadata format? Why is this a security concern if a metadata can be retrieved from its registry for account bounding?

This proposal is designed to work with external NFT contracts, especially ones that have already been deployed and already have metadata systems in place. As such, it doesn’t specify any requirements for metadata beyond those set out in EIP-721. This is not a security concern.

account bounding can already be done by checking ownership of an EIP-1155 token of sender in account contract

Right - you can check that a sender’s balance of an ERC-1155 token is non-zero. My point is that there can be many owners of a single ERC-1155 token with a given token ID. How would you recommend handling account authorization given multiple token holders?

Author: hskang9
Date: 2023-03-01T02:13:22.195Z

jay:

A fully-functional implementation is included in the “Reference Implementation” section.


The reference implementation does have a case of account contract being a proxy to interact with another smart contract, but it does not cover all ambiguity such as a case where an account being operated by the rules on smart contract.



 jay:

If you can’t make contract implementation, you don’t know what you are doing


Sorry if it hurts you, but it is true that you haven’t covered the case on other previous references where the contract account can have other utilities. The way to use smart contract with NFT as a proxy account is very interesting and has its point enough if it is clarified.



 jay:

Right - I guess my question is whether ERC-5252 works with all existing NFT contracts? Or does it only work with NFTs that implement the ERC-5252 interface?


Definitely not. your proposal is something new from what I did. Your proposal is providing a way where a proxy account can be made in EVM blockchain with NFT. Problem is you cover the proposal as if it covers all cases of account bound with NFT.



 jay:

This proposal is designed to work with external NFT contracts, especially ones that have already been deployed and already have metadata systems in place. As such, it doesn’t specify any requirements for metadata beyond those set out in EIP-721. This is not a security concern.


I agree as this is rather a specification of a proxy account by owning an nft from the reference implementation for now. The proposal has its value on its generic interface to interact with other contracts, but I think it is currently neglecting the fact on who is reponsible on which and which on connecting account to NFT. However, I believe this has a huge potential when it comes to proxy trade. As you declare there is no security concern, it is clear that this is just used as generic proxy account.



 jay:

Right - you can check that a sender’s balance of an ERC-1155 token is non-zero. My point is that there can be many owners of a single ERC-1155 token with a given token ID. How would you recommend handling account authorization given multiple token holders?


ERC-1155 with id can be made with setting asset key as an id (e.g. (“1”, 1), (“2”, 1)). This way one can handle account authorization as same as ERC721.
After looking at the reference implementation, I suggest amending the proposal name from “Non-fungible Token Bound Accounts” into “Non-fungible Token Bound Proxy Accounts”.

Author: hskang9
Date: 2023-03-01T02:40:59.449Z

jay:

Right - you can check that a sender’s balance of an ERC-1155 token is non-zero. My point is that there can be many owners of a single ERC-1155 token with a given token ID. How would you recommend handling account authorization given multiple token holders?


@jay 's point is fair. You need to specify how you handle this issue.

Author: jay
Date: 2023-03-01T15:14:35.102Z

hskang9:

does not cover all ambiguity such as a case where an account being operated by the rules on smart contract


I can definitely add some test cases to clarify how token bound accounts should function. The example account implementation in the proposal is intentionally simple.



 hskang9:

you haven’t covered the case on other previous references where the contract account can have other utilities


Projects wishing to implement this proposal are welcome to create custom account implementations which add additional functionality to token bound accounts. This proposal defines a minimal interface in order to leave room for diverse implementations.



 hskang9:

Problem is you cover the proposal as if it covers all cases of account bound with NFT


I’d love to hear more about some of the cases that couldn’t be supported by this proposal. EIP-1167 proxies were chosen to maximize the possible cases that can be supported, as they have wide ecosystem support and can support nearly all possible smart contract logic.



 hskang9:

it is currently neglecting the fact on who is reponsible on which and which on connecting account to NFT


I can definitely attempt to make this clearer in the proposal. Anyone can create a token bound account for any NFT, but only the owner of the NFT will be able to utilize the account. This is enforced by checking that a given implementation supports the token bound account interface described in this proposal before deploying the account. Bad actors could deploy malicious account implementations, which is a potential concern. However, much like “spam” NFTs that are airdropped into people’s wallets, there is no risk to the end user so long as they do not interact with accounts whose implementations are untrusted.



 hskang9:

ERC-1155 with id can be made with setting asset key as an id (e.g. (“1”, 1), (“2”, 1)). This way one can handle account authorization as same as ERC721.


This is correct. However, given an external ERC-1155 token contract with no knowledge of its token ID scheme, there is no way (according to the ERC-1155 standard interface) for an account implementation to determine that there is only one owner per token ID. As mentioned above, a custom account implementation could be created which implements the owner and executeCall functions such that they check balanceOf instead of ownerOf to allow usage with ERC-1155 tokens. I’m reluctant to specifically include this in the proposal to avoid bloating the specification, but if there is enough interest that can certainly be reconsidered.



 hskang9:

You need to specify how you handle this issue.


This question was actually directed at you @hskang9 
Would love to hear your thoughts on how you would like to see ERC-1155 tokens supported!

Author: hskang9
Date: 2023-03-03T00:41:39.870Z

jay:

I can definitely add some test cases to clarify how token bound accounts should function. The example account implementation in the proposal is intentionally simple.


You are missing the point. You claim to have provided a token bound proxy account that enables someone do anything and you limit what it does? It is great to make a test cases, but you just made a token bound proxy account for executing generic transactions with separate contracts. You don’t need to limit what you just make, that is not a standard.



 jay:

Projects wishing to implement this proposal are welcome to create custom account implementations which add additional functionality to token bound accounts. This proposal defines a minimal interface in order to leave room for diverse implementations.


Ok, so to leave a room, I think the proposal has to specify three cases where:

wallet <> token bound contract <> another contract (proxy account and your reference implementation)
wallet <> token bound contract

The question is, how will you securely specify second item as you referred on security concerns in your original proposal? and how will you aggregate all the other cases into one (e.g. FNFT by Revest, etc)?



 jay:

I’d love to hear more about some of the cases that couldn’t be supported by this proposal. EIP-1167 proxies were chosen to maximize the possible cases that can be supported, as they have wide ecosystem support and can support nearly all possible smart contract logic.


Obviously the same answer from the previous quote.



 jay:

Right - I guess my question is whether ERC-5252 works with all existing NFT contracts? Or does it only work with NFTs that implement the ERC-5252 interface?


ERC721A is subset of ERC721? Is it too hard to understand implementing NFT ownership of a newly created contract from create when you just coded? EIPs/assets/eip-5252/contracts/ABT.sol at 39fe51429c2295692b351e4e89f7c38cc42d8ae9 · hskang9/EIPs · GitHub



 jay:

I can definitely attempt to make this clearer in the proposal. Anyone can create a token bound account for any NFT, but only the owner of the NFT will be able to utilize the account. This is enforced by checking that a given implementation supports the token bound account interface described in this proposal before deploying the account. Bad actors could deploy malicious account implementations, which is a potential concern. However, much like “spam” NFTs that are airdropped into people’s wallets, there is no risk to the end user so long as they do not interact with accounts whose implementations are untrusted.


Ok now put that into the proposal without saying account ambiguity. You also forgot how your registry actually verifies NFT and connected account.



 jay:

This is correct. However, given an external ERC-1155 token contract with no knowledge of its token ID scheme, there is no way (according to the ERC-1155 standard interface) for an account implementation to determine that there is only one owner per token ID. As mentioned above, a custom account implementation could be created which implements the owner and executeCall functions such that they check balanceOf instead of ownerOf to allow usage with ERC-1155 tokens. I’m reluctant to specifically include this in the proposal to avoid bloating the specification, but if there is enough interest that can certainly be reconsidered.


No, you don’t know how ERC-1155 works. ERC-1155 can have total supply of 1, and this makes the fact that there is only one owner per token ID. Perhaps you could set total supply on 2 and ask why it does not work.



 jay:

This question was actually directed at you @hskang9 
Would love to hear your thoughts on how you would like to see ERC-1155 tokens supported!


No thank you. I work on this to make sense of a proposal. I am not joining your peripheral attempt on glorifying the proposal when it is just a proxy account implementation with NFT. You have to pay me first on handling your issue. I only work for free on this after being too afraid of the world where @vbuterin says zero knowledge solves everything but cannot code one line on actually shipping the product then cashes out 350 million dollars, Do Kwon saying algo-stablecoin will solve all money out there and make hundreds of people suicide after his scam got revealed. They all boast their knowledge about crypto, but they have zero knowledge in action. Also, I think using emojis doesn’t help on showing that you are seriously thinking to pass this. Are you trying to finalize this proposal or what?
From your answers, it seems you do have knowledge in smart contracts, but it does not look like you have knowledge in action as you haven’t known the EIP-1155 can limit the supply. If your proposal’s name is Non-fungible Token Bound Accounts, you must have knowledge in action on all NFTs, including ERC-1155. Hence, you need to implement the case where EIP-1155 is used too. Otherwise, this has to be limited to ERC-721 token bound accounts.

Author: jay
Date: 2023-03-03T01:35:26.313Z

@hskang9 Thanks for sharing your opinions!

Author: minhhung123
Date: 2023-03-04T13:31:45.019Z

Any error in the Registry Implementation. It said with error “Expected even number of hex-nibbles.” in the _creationCode function

Author: jay
Date: 2023-03-04T16:31:08.883Z

Thanks for catching that @minhhung123! I’ve updated the reference implementation to fix the issue. Please let me know if you run into any other problems.

Author: h4l
Date: 2023-03-21T12:13:09.689Z

I like this proposal. The functionality it enables of giving 721s predictably-addressed accounts seems genuinely useful. The design seems well thought out — it maintains backwards compatibility and has a small footprint, without unnecessary details.
I think there’s a potential foot-gun, which could also be called a feature, depending on your point of view. Transferring an NFT to the address of an account that is derived from the NFT will make the account inaccessible. This is probably more an implementation consideration than something to spec, but I wonder if implementations should reject a transfer of their own NFT to themselves (in their onERC721Received function), at least by default.
A thought on the security issue you describe for the owner of an NFT pulling assets just before completing a sale/transfer. I think a general method to avoid this (that doesn’t require explicit support from an NFT market or from account implementation contracts) could be to create a wrapper NFT contract whose tokens are minted to anyone transferring an NFT to the contract. Anyone holding a wrapper token can redeem the wrapper for the wrapped token, burning the wrapper in the process. If the tokenIds of the wrapper are not re-used, someone buying/receiving a specific wrapper tokenId can be sure that any accounts linked to the wrapped NFT have not performed any transactions since the wrapper was created, as the wrapper contract is the owner of the NFT while wrapped.
Although this wouldn’t prevent token-approval-based operations from occurring while the NFT is wrapped.
(I’m not suggesting changing the spec, just noting that it seems like this could be mitigated by anyone with an interest in doing so.)
Finally, just a thought on the 1155 support — even though a token can have multiple holders, it’s quite frequently used with a supply of 1 per token. As you describe in the backwards compatibility section, the registry implementation doesn’t enforce 721 interface implementation, so it seems like 1155 support could be an optional choice for an implementation. They could just use balanceOf(...) > 0 to authorise access, and if the 1155 contract allows a supply greater than 1, you just get a kind of 1-of-n multisig account!

Author: jay
Date: 2023-03-22T20:06:00.377Z

@h4l this is great feedback, thank you!



 h4l:

Transferring an NFT to the address of an account that is derived from the NFT will make the account inaccessible.


This is definitely a potential foot-gun, and I think it’s worth adding a section discussing this to the proposal. Implementations can definitely implement some protection against this by checking that the received NFT is not the NFT which owns the account. However, this issue also arises any time there is an ownership loop involving any number of NFTs. For example, you could have two NFTs and transfer the ownership of each NFT to the account of the other NFT. Since loops could involve a large number of NFTs, iterating through the ownership graph on every NFT transfer to prevent loops would likely exhaust the available gas.
The possibility of asset loss due to ownership loops seems similar to burning an NFT - it’s a destructive action that the holder of an NFT has the right to perform. It also has the potential to be the basis for interesting token mechanics, similar to burning. As a result, I think it should be up to each implementation to decide if they would like to prevent this behavior.



 h4l:

I think a general method to avoid this (that doesn’t require explicit support from an NFT market or from account implementation contracts) could be to create a wrapper NFT contract whose tokens are minted to anyone transferring an NFT to the contract.


This is a great approach! Another alternative approach inspired by this idea could be to wrap the NFTs stored within the token bound account, allowing additional logic to determine which actions can be taken on these NFTs while they are owned by the token bound account.



 h4l:

They could just use balanceOf(...) > 0 to authorise access, and if the 1155 contract allows a supply greater than 1, you just get a kind of 1-of-n multisig account!


This is very true! I wonder if it would be worth modifying the proposal such that it doesn’t assume the existence of a single owner per NFT. This could be accomplished by changing the owner() returns (address) method (which assumes the account has a single owner) to an isOwner(address potentialOwner) returns (bool) method, which returns true if potentialOwner is an owner of the token which controls the account. This may make implementing ERC-1155 support simpler for developers building custom implementations. Would be curious to hear your thoughts on this approach.

Author: h4l
Date: 2023-03-23T17:09:00.987Z

jay:

It also has the potential to be the basis for interesting token mechanics, similar to burning. As a result, I think it should be up to each implementation to decide if they would like to prevent this behavior.


Yep, I agree. Certainly lots of ways people can find to lock themselves out of their wallets.



 jay:

Another alternative approach inspired by this idea could be to wrap the NFTs stored within the token bound account, allowing additional logic to determine which actions can be taken on these NFTs while they are owned by the token bound account.


That’s an interesting idea, I could imagine NFT projects could provide an official account contract for their NFT which imposes some specific constraints that make sense in the context of the project. Like maybe the project can send some kind of assets/NFTs to the accounts of their NFTs that can only be withdrawn after a delay or after some constraints are met. And then they are naturally taken along with the original NFT if it’s sold/transferred.



 jay:

I wonder if it would be worth modifying the proposal such that it doesn’t assume the existence of a single owner per NFT. This could be accomplished by changing the owner() returns (address) method (which assumes the account has a single owner) to an isOwner(address potentialOwner) returns (bool) method, which returns true if potentialOwner is an owner of the token which controls the account. This may make implementing ERC-1155 support simpler for developers building custom implementations. Would be curious to hear your thoughts on this approach.


That seems like a good approach. And someone wanting to discover the owner for a 721’s account could still do so via the NFT contract, which they can find using token(). I would imagine it shouldn’t be mandatory for an implementation to support 1155s in addition to 721s. Do you think you would specify that implementors just need to support at least one of the two? As mentioned above, I can imagine use cases for projects just creating an implementation that only supports their single specific NFT contract.

Author: wwhchung
Date: 2023-03-30T17:46:05.746Z

A few comments after chatting with @jay and team that I think would be big improvements to the EIP:

Having multiple addresses per implementation in a predictable fashion:
Adding a counter variable per chain/tokenAddress/tokenId/implementation that auto-increments and is used in the nonce for create2
Adding lastTransactionTimestamp to the account interface.  The implementation MUST update the lastTransactionTimestamp upon any transaction execution.

The purpose of 2 is to help marketplaces address the issue of front-running removal of assets.  Adding this variable allows marketplaces that support this spec to reject order fulfillment automatically if the order contains a restriction on lastTransactionTimestamp.  Practically speaking, a supported marketplace would:

Show NFT’s wallet assets and contents, as well as any token approvals that exist.  Buyer/bidder has full transparency on what’s own.  Buyer/bidder submits the order with the matching lastTransactionTimestamp
Any attempts to frontrun the order will modify lastTransactionTimestamp and will cause the marketplace fulfillment to fail.

Author: wwhchung
Date: 2023-04-01T15:15:39.364Z

One other aspect is adding the ability to init bytecode in the registry create function, as well as take in an index.  This allows a really good property of being able to have predictable addresses regardless of a base implementation (which can proxy to an upgradeable implementation) on an indexed basis.
Which means that people can see a ‘default’ wallet for every nft in existance and start interacting with them.
We’ve had an extremely similar concept in development since last September, and would love to unify these concepts into a singular spec:
https://github.com/manifoldxyz/token-owned-wallet-solidity

Author: jay
Date: 2023-04-01T17:48:14.621Z

@wwhchung these are great suggestions, thanks for highlighting them!



 wwhchung:

Having multiple addresses per implementation in a predictable fashion:
Adding a counter variable per chain/tokenAddress/tokenId/implementation that auto-increments and is used in the nonce for create2


Allowing multiple accounts per NFT/implementation combination seems very useful. Perhaps instead of enforcing auto-incrementation at the registry level, the createAccount and account functions could take an extra uint256 seed parameter which would be used to compute the account address. This would allow account creators wishing to utilize auto-incrementing seeds to do so, while also opening up the possibility of “vanity” account addresses or stealth addresses.



 wwhchung:

Adding lastTransactionTimestamp to the account interface. The implementation MUST update the lastTransactionTimestamp upon any transaction execution.


I like the idea of tracking changes in account state over time, as that simplifies marketplace support. Perhaps instead of using a timestamp, a function nonce() public returns (uint256) could be added, allowing account implementations to choose how they would like to implement state tracking.
Will make some updates to the proposal to incorporate these concepts.

Author: wwhchung
Date: 2023-04-01T18:02:55.125Z

If you take a look at my recent repo changes that’s what I’m doing as well (index that can be specified as well as nonce rather than timestamp).

Author: wwhchung
Date: 2023-04-01T18:04:53.962Z

My reference implementation also has a static cloneable proxy that’s upgradeable on a user basis, which takes in initialization bytecode so that the deploy bytecode can be static yet the implementation can be dynamic which allows for predictable addresses.

Author: jay
Date: 2023-04-01T18:08:16.487Z

Awesome to see that you and the team are have been experimenting with this concept as well, thanks for making it open source! Would love to unify these approaches under a single proposed standard.
The main concern I have with index-based addresses is that it introduces a significant vector for spam/targeted attacks. Since token bound accounts are permissionlessly deployable by design, using only an index (and not the implementation address) when computing the account address would make it easy for bad actors to “claim” NFT account addresses with malicious/spammy implementations. For example, if the predetermined address for NFT #1 with index 0 is 0xabc..., a malicious actor could deploy a contract to 0xabc... that contains a backdoor allowing them to extract all assets from the account, making that address functionally unusable. This could also be done for a large number of indexes (e.g. the first 100 wallet indexes are all scam implementations), which would introduce a significant burden for both NFT holders wishing to use their accounts as well as developers wishing to adopt this proposal.
By including the implementation address as an input to the account address computation, malicious contracts deployed for an NFT can be ignored by the owner and any platforms choosing to support this proposal, as there are no “default” addresses that can be claimed. It also has the desirable quality of allowing an account’s implementation to be trusted solely based on the account’s address, which has useful ramifications.
Perhaps most importantly, including the implementation address in the account address computation allows developers wishing to provide index-based “default” wallets to easily do so in their applications, while enforcing index-only account addresses at the registry level removes the possibility of ever supporting non-index-based accounts.

Author: wwhchung
Date: 2023-04-01T18:19:00.741Z

Hmmm. That’s a good point.
Note that my proposal also computes based on implementation and index. But the spam attack vector still exists.
Perhaps the reference implementation only allows the current token owner to deploy which would prevent this attack at least for the reference implementation.
Note that I do need to patch up my proxy implementation to defer owner check to the implementation

github.com


manifoldxyz/token-owned-wallet-solidity/blob/85cba4b9b62120ca290850530a8faff4ccec5c31/contracts/TokenOwnedWalletProxy.sol#L118

      
              return TokenOwnedWalletBytecode.token();
          }
          
          /**
           * @dev Returns the owner of the token owned wallet
           */
          function owner() external view returns (address) {
              return _owner();
          }
          
          function _owner() private view returns (address) {
              (uint256 chainId, address contractAddress, uint256 tokenId) = TokenOwnedWalletBytecode
                  .token();
              if (chainId != block.chainid) return address(0);
              return IERC721(contractAddress).ownerOf(tokenId);
          }
          
          /**
           * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
           * function in the contract matches the call data.
           */

Author: jay
Date: 2023-04-01T18:45:25.866Z

wwhchung:

One other aspect is adding the ability to init bytecode in the registry create function


Allowing for optional initializer calls during account creation seems very useful!



 wwhchung:

Note that my proposal also computes based on implementation and index.


I missed that, thanks for clarifying.



 wwhchung:

Perhaps the reference implementation only allows the current token owner to deploy which would prevent this attack at least for the reference implementation.


I agree that desired protections against this type of attack should be enforced at the account implementation layer. However, I’m hesitant to recommend owner-only deployments as the default configuration for a wallet implementation, since there are many potential positive use cases for permissionless deployments that could be inhibited.

Author: wwhchung
Date: 2023-04-01T20:15:32.063Z

The reference implementation wouldn’t be part of the eip. But rather the main website explaining and promoting this eip. Similar to royalty registry or delegate.cash
The eip itself would be implementation agnostic. But the site should have a ref implementation that allows every token in existence to have an address from the get-go (which would be based on the ref implementation), and a one click way for any token owner to get write access to the address.
This ref implementation would be deployed to every chain at the same address. And I am suggesting it to be similar to this one:

github.com


manifoldxyz/token-owned-wallet-solidity/blob/main/contracts/TokenOwnedWalletProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/// @author: manifold.xyz

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/StorageSlot.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./lib/TokenOwnedWalletBytecode.sol";

interface ITokenOwnedWalletProxyOwner {
    function _owner() external view returns (address);
}

contract TokenOwnedWalletProxy is Initializable {
    uint256 public nonce;

    constructor() {
        assert(




  This file has been truncated. show original





Which has the features of upgradeable implementation, prevention of non token owners from initializing the wallet and nonce incrementing built in.
Again, this is outside the scope of the eip but IMO needed for the main site and launch in a way that ppl can start engaging with these wallets (pushing content) without the token owner even needing to deploy the wallet.

Author: jay
Date: 2023-04-04T13:48:07.524Z

@wwhchung Agreed that a demo site will be super helpful to help folks understand how this EIP works! In the meantime, this is great as an example in the reference implementation repo. Appreciate the PR

Author: dongshu2013
Date: 2023-04-05T05:52:45.306Z

We have similar ideas at our latest version of EIP-4972: Update EIP-4972 Pull Request. Although our proposal is not for ERC-721 but for domain names, the ideas behind are quite similar: to give nft/name a bounded account
Our interface is little bit different from EIP-6551 but I think there is a potential to merge these two EIPs to provide a more universal interface. Here are some of my thoughts to simplify the interface:


The “executeCall” function is actually not related to this EIP. Each implementation should be able to customize the way to call other functions. It could be included in the reference implementation though.


The “owner” function is not necessary since it can be derived from the token contract and token id.


The chainId param is not necessary for functions/events at IERC6551Registry since you can always get it from block.chainId, (except you want to register account at chain A for chain B, which doesn’t make sense to me). You can still use chainId as a field in the byte code spec when registering the account but it should not be included in the function params. The chainId returned by token function can also be emitted.

Author: dongshu2013
Date: 2023-04-05T06:02:21.480Z

Another thoughts is that instead of putting implementation as a param of all functions of IERC6551Registry, is it better to extract it out as a general view function so all accounts registered by this registry are guaranteed to share same implementation? It may not be as flexible as current version but it will prevent potential fragmented implementations, making it more easier to integrate.
The new interface will look like this:
interface IERC6551Registry {
    event AccountCreated(
        address tokenContract,
        uint256 tokenId
    );

    function createAccount(
        address tokenContract,
        uint256 tokenId
    ) external returns (address);

    function getImplementation() external returns (address);

    function account(
        address tokenContract,
        uint256 tokenId
    ) external view returns (address);
}

interface IERC6551Account {
    receive() external payable;

    function token()
        external
        view
        returns (address tokenContract, uint256 tokenId);
}

Author: jay
Date: 2023-04-05T11:37:45.348Z

@dongshu2013 thanks so much for the comments! Awesome to see other folks exploring the potential impact of smart contract accounts tied to on-chain assets.



 dongshu2013:

The “executeCall” function is actually not related to this EIP


I agree that this looks a bit out of place in the proposal. However, including a standard execution function in the account interface enables the very desirable characteristic of having a standard interface for interacting with all token bound accounts regardless of the underlying implementation code. Without a standard execution interface you would need some form of interface detection to determine how to execute calls from different account implementations, which would get messy quickly and make the proposal more difficult to adopt. Definitely not married to the current execution function signature though, so open to suggestions there.



 dongshu2013:

The “owner” function is not necessary


You can definitely query the owner directly from the token contract given the token information. However, this function serves two purposes in the proposal. First, as above, it gives a standard interface for interacting with accounts. Second, it makes token bound accounts somewhat compatible with existing infrastructure built around ownable contracts. This may be useful for supporting token bound accounts in some legacy applications. If this proposal is expanded to support semi-fungible tokens, this interface may change and the current owner function would be removed in favor of something like isOwner(address) returns (bool).



 dongshu2013:

The chainId param is not necessary for functions/events at IERC6551Registry since you can always get it from block.chainId


This is true for single-chain applications, but including the chainId as a parameter allows wallet implementations wishing to implement cross-chain compatibility to do so. Hardcoding block.chainid removes that possibility.



 dongshu2013:

Another thoughts is that instead of putting implementation as a param of all functions of IERC6551Registry, is it better to extract it out as a general view function so all accounts registered by this registry are guaranteed to share same implementation


Having the ability to support any implementation is actually a very important characteristic of this proposal because it allows for client diversity in wallet implementations. If the registry provided a static wallet implementation, it would either be immutable and thus not able to adapt to improvements in the smart contract account ecosystem, or it would be upgradable and thus pose an unacceptable centralization risk. By allowing for arbitrary smart contract implementations that adhere to this standard to be bound to an NFT, this allows the ecosystem to continuously experiment with different approaches to token bound accounts in a fully decentralized manner.
Additionally, since the holder of an NFT has sovereignty over their asset, it would stand to reason that sovereignty would extend to choosing which wallet implementation they would like to use. By allowing arbitrary implementations, applications wishing to adopt this standard but support only a single implementation are free to do so. However, by restricting to a single implementation at the registry level, the reverse would not be true.
Hope this answers your questions! I think there are some really neat applications to be built around accounts bound to a canonical name, and would love to ensure that there are broadly adopted standards that can support this use case.

Author: dongshu2013
Date: 2023-04-05T18:15:48.483Z

How to define a unified function call interface is a common problem for all smart contract wallets. Since the Account Abstraction is getting its momentum, I believe it deserves its own EIP and we should not mix it with this one. e.g. The sample implementation of smart account for account abstraction is called execute and executeBatch. We will need a more comprehensive/unified API set(e.g. the batch one) for smart contract wallets instead of a simple executeCall function.


Removing owner function will not make it in-compatible with existing infrastructure since they can always implement their own owner function. We can suggest them to implement the owner function in the reference implementation but enforcing all implementations to have this function doesn’t make sense to me. Also ERC-4337 are actually intentionally avoiding the specific “owner” function and uses verifyUserOp() function to do the ownership verification.


A more common practice is to put chainId at the spec but not as explicit function params. Again, taking ERC-4337 as example, per the spec, the signature of UserOps should include chainId and the entry point contract, but it’s just in the spec and should not be reflected in the interface definition. For cross-chain application/wallets integration, it could be unified in a higher level(e.g. SDK) instead of hardcoding the chainId into the smart contract interfaces.


I don’t have a preference for this one but I think these two are equivalent. Putting implementation as the function params assumes that there is only one global registry contract for all implementations, while extracting it out as independent view function assumes each implementation will have its own registry contract(just like ERC20). I’m not sure which one is better, but my hunch is that the second approach will be easier for dApps who only care one implementation to integrate.

Author: jay
Date: 2023-04-05T18:38:24.517Z

ERC-6551 accounts are welcome to implement additional execution functions if they see fit to do so, the common interface does not restrict that. Agreed that there is a great opportunity to standardize the interface for these execution calls! I’m fine with execute as the common interface function.
Agreed that this is a long-tail edge case, as it would only apply to dapps that are no longer actively maintained (therefore will not support the proposal) and are also using owner calls as authorization.
Would love to hear any recommendations you have to ensure the possibility of cross-chain support without including the chain ID in the creation params!
With the current proposal, dapps wishing to support a single registry are welcome to do so at the client layer, or create their own registry wrapper contract which uses a single known implementation. The reverse is not true - if the proposal specified a single registry with a known implementation or multiple registries with a single implementation per registry, it would be impossible to build a single canonical registry. This exact tradeoff was explored at length during the development of this proposal, and it was determined that a single global registry contract would present the easiest path towards ecosystem adoption.

Author: dongshu2013
Date: 2023-04-05T22:41:15.738Z

1 & 2: One principle we should follow here is to minimize the required functions to cover the core functionalities and leave the rest to community, that’s why I insist we should remove the owner and executeCall function and leave it to some other EIPs. As I said, we can always put it in the reference implementation and encourage others to follow but there is no need to enforce it in the spec
3: I just realize that I don’t fully understand the “cross-chain compatibility” problem you are referring here? What exact problems are you trying to solve or avoid by specifying this chain id field in function params? (I’m expecting users will still use chain id when creating the account(since it’s required by the spec) but there is no need to put it in the function params, so I didn’t see why my proposal will undermine the cross-chain compatibility.)
4: For the registry part, I think you made a good point that a global registry is much easier to aggregate than a list of registry contracts if you want to index all implementations. I’m thinking how the ecosystem can benefit from a canonical registry besides easy indexing though, since I’m expecting there will be multiple registry contracts out there anyway even we unify the interface for all implementations.

Author: jay
Date: 2023-04-06T02:31:13.851Z

I definitely agree with you in principle here, but think providing a unified way for callers to execute transactions and query for the owner(s) of an account is important functionality that should be included in the interface. Especially since these are likely to be two of the most common calls executed against accounts. Without a standard interface defined in the proposal, how would you suggest that clients wishing to adopt this proposal should perform these calls across all accounts given varying implementations?


In the specific case of owner, having the function defined on the interface means that an external caller given only an account address doesn’t need to know that the address is a 6551 account in order to determine the account owner. I’m very open to other interfaces for this, but this seems like an attractive property and I’m curious how you would recommend handling this case without defining something on the interface?


Assuming NFT #1 has account 0xabc... on a chain with ID 1, the only way for NFT #1 to also use 0xabc... on any other chain is by passing the chain ID as a parameter to the account creation call on the registry contract, which is deployed to the same address on both chains. Without this, how could NFT #1 use their unique address across all chains?


The other major benefit of a canonical registry is that every NFT gets a canonical account address per implementation which can be used prior to contract creation. By allowing any implementation to be deployed via the canonical registry, the need for alternative registries is minimized.

Author: dongshu2013
Date: 2023-04-06T19:33:38.987Z

1& 2: Just to clarify, I didn’t say we don’t need to unify the interface, my point is that it’s out of the scope of this EIP since it should be a common interface for all smart contract wallets. Not sure if there is any other EIPs addressing the issue, if there is, we can follow that. It does make it easier for dApps to integrate 6551 accounts with these functions. However, in the long run, if the interface of other smart contract wallets is unified in another way, dApps will be have to maintain two set of interfaces(Ideally dApps should be able to integrate 6551 accounts just like all other smart contract wallets.) Defining the general interface of smart contract wallets is a bigger topic which we can discuss it in other threads.
3: Just curious, why do we need a bound account for NFT at chain 1 for chain 2? In my understanding, the ownership is guaranteed by the ERC721 contract so there is no way to guarantee the ownership at chain 2 if the NFT contract is not deployed at chain 2. Also if the NFT contract is deployed at chain 2 with a different address, then the bound account address will also be different.
4: Minimize the need for alternative registry makes sense to me. My concern is that if we cannot eliminate the need, eventually dApps will have to maintain a list of registry contracts, which brings us back to the original situation. However, again, I don’t have a strong preference for this one, so either one lgtm

Author: jay
Date: 2023-04-06T20:28:08.064Z

1&2: I agree! Would happily support a proposal for a cohesive execution interface across smart contract accounts. In the absence of a recommended existing proposal or the emergence of a new proposal for this purpose, I think these functions should remain in the interface for this proposal for now. It can always be updated prior to last call if an alternative proposal emerges. Account implementations can also be updated to support a new standard that emerges, so I don’t see a conflict here.
3: You’re correct that there is no explicit guarantee of ownership of an NFT across chains, so it would be up to wallet implementations to determine if they would like to support cross-chain execution. This proposal includes chain ID as a parameter to the account creation call in order to leave open this possibility. One potential use case here is allowing a given NFT to collect POAPs (which are limited to use on a single chain).
If an NFT contract is deployed at the same address across chains, they will be distinct tokens (as a token with the same ID across chains can have two separate owners) and should have distinct wallet addresses. This proposal essentially treats an account address as a meta-asset that an NFT can own across all chains if the chosen wallet implementation supports it.

Author: jay
Date: 2023-04-09T14:10:16.150Z

A few new updates to the proposal based on feedback that has been received so far:

The Registry now deploys ERC-1967 proxies instead of ERC-1167 proxies. This change increases compatibility with existing proxy detection infrastructure since ERC-1967 proxies can be detected by reading a storage slot rather than pattern matching against bytecode. This change also allows wallet implementations wishing to implement upgradability to do so without requiring an intermediate proxy.
The Registry contract is now deployed via the create2 factory at 0x4e59b44847b379578588920cA78FbF26c0B4956C. This allows for wider deployment compatibility with EVM chains that strictly enforce EIP-155, which breaks the deployment method that was used previously.
Added a salt parameter to the createAccount function, allowing tokens to have multiple token bound accounts per implementation
Added an initData parameter to the createAccount function, allowing for optional initialization of token bound accounts upon deployment
Added function nonce() external view returns (uint256) to the IERC6551Account interface to allow for easier tracking of state changes on the account. This will allow marketplaces to more easily implement scam prevention measures when fulfilling orders involving token bound accounts.

Author: tigerthedev
Date: 2023-04-11T05:11:24.239Z

Are there any pitfalls to using this for an account-bound token(a token which can’t be transferred). Since the token can’t be moved out of the wallet, in the scenario of the wallet getting compromised will it end up losing everything tied to the account itself?

Author: jay
Date: 2023-04-11T14:30:33.043Z

@tigerthedev good question! Token bound accounts will work out of the box with account-bound tokens, assuming that they conform to the ERC-721 standard. In general, token bound accounts are only as secure as the wallet holding the underlying token. In the case of a compromised wallet, all accounts for tokens stored in that wallet would also be compromised.

Author: tigerthedev
Date: 2023-04-12T07:23:09.652Z

got it @jay. Currently what I see most of the projects do is issue a new account-bound token when the wallet is compromised. So the owner needs to take care of moving all the tokens out of the account to the new account which is bound to the token.
I can see a lot of discussions around PFPs as true on-chain identities using ERC-6551. I am a bit confused here about how a transferable token can act as an identity. Will you be able to shed some light here?
article link here: ERC 6551 — Token Bound Accounts. Every NFT will have their own… | by Benny Giang | FUTURE PRIMITIVE | Mar, 2023 | Medium

Author: jan
Date: 2023-04-24T10:04:45.177Z

We’re looking to adopt EIP-6551 for mech and I started catching up on discussions here. 
We modeled the API of our execution interface after Safe where transactions are defined with the following parameters:
address to
uint256 value
bytes memory data
Enum.Operation operation  // 0 = call, 1 = delegatecall

Support for delegatecall seems important to us (e.g.: for batching via multi-send), but it also comes with the implication that we cannot prevent account owners from writing to their TBA’s storage. Account implementations that allow generic delegate calls are implicitly upgradable with storage-based proxies.
That’s why I would like to suggest reconsidering this change:

The Registry now deploys ERC-1967 proxies instead of ERC-1167 proxies. This change increases compatibility with existing proxy detection infrastructure since ERC-1967 proxies can be detected by reading a storage slot rather than pattern matching against bytecode. This change also allows wallet implementations wishing to implement upgradability to do so without requiring an intermediate proxy.

I feel ERC-1167 proxies are better suited for the following reasons:

Implementation immutability, even if an account allows for generic delegate calls
Lower tx execution cost (no storage read)
IMO, they are common enough so that we can assume good support from proxy detection infrastructure.

Due to the nature of TBAs being transferred between people, immutability of the account implementation seems paramount for establishing trust.

Author: wwhchung
Date: 2023-04-25T15:18:51.009Z

We actually started out with an 1167 version, with the option to support upgradeability via another upgradeable storage based proxy:
So, initially, it was:
Account = 1167 → Upgradeable Proxy → Implementation
But we debated on whether or not an intermediate proxy was good, or if we should just do 1967 natively.
Sounds like you’re recommending the 1167 → Upgradeable Proxy (even a 1967) → Implementation.  Are we on the same page?  If so, the changes seem pretty minor, we’re just offloading upgradeability to another intermediate proxy.  Seems like we’d be introducing a bit of call overhead though, no?

Author: jay
Date: 2023-04-25T15:50:48.019Z

Thanks for the comments @jan! Would love to ensure 6551 can support mech.
As @wwhchung mentioned, an earlier version of this proposal used ERC-1167 proxies. Part of the motivation for switching to ERC-1967 proxies was the concern that appending extra constant data to the proxy bytecode would cause problems in applications using naive approaches to identifying ERC-1167 proxies (e.g. using regex with an assumed bytecode length). Switching to ERC-1967 proxies sidesteps this issue. However, lack of proxy detection support might be less of a concern in practice.
The other motivation for switching to ERC-1967 proxies was to allow accounts wishing to implement static upgradability to do so without requiring an intermediate proxy. Arbitrary delegate calls allow the executor of the account to use any implementation, but don’t allow additional functions to be defined on the account which are accessible to non-executors. If an ERC-1167 proxy is used, implementing static upgradability would require a second upgradable proxy behind the ERC-1167 proxy. The shift to ERC-1967 was partially an attempt to reduce this redundancy.
You make a good point that the shift to ERC-1967 proxies forces all account implementations to use storage, which breaks compatibility with account implementations that allow for arbitrary delegate calls. Using ERC-1167 would allow for stateless account, while also allowing account implementations that wish to implement upgradability to do so with an intermediate proxy. This comes at the cost of some additional gas overhead for upgradable accounts, but that seems reasonable.
I think that trust in an account implementation can be established via either full immutability (as mech does) or via controls on storage access (so long as arbitrary delegate calls are disallowed). It seems that this decision should be made by each account implementation rather than being enforced at the proposal level.
I would support a return to ERC-1167 proxies with appended constant data in order to accommodate stateless accounts.

Author: jan
Date: 2023-04-26T07:25:24.837Z

I fully agree with your reasoning. ERC-1167 seems like the least common denominator. For additional proxies a bit of call overhead seems reasonable, as this also leaves the choice over the exact proxy pattern to account implementers.

Author: jan
Date: 2023-04-26T07:29:54.570Z

I now understand your concern about proxy detection better, seeing that you appended the static data to the 1167 proxy bytecode. This might indeed cause issues with proxy detection infra. In mech, we work around this by storing the static data at a different address rather than as a footer to the proxy bytecode.

Author: jay
Date: 2023-04-26T14:47:38.489Z

@jan This is a very neat approach, but it seems like it may incur significant gas fees both during account creation (as two contracts are being created instead of one) and per-transaction (as it introduces an additional cold call to the storage contract).
While lack of support for ERC-1167 proxies with appended data does pose some concern, I think the practical risk is minimal. It doesn’t seem like on-chain detection of ERC-1167 bytecode is a commonly used pattern, and off-chain detection logic can be easily updated to ignore any bytecode after the standard ERC-1167 proxy implementation.
I’ve tested the modified ERC-1167 proxy against Etherscan’s proxy verification tool, and it detects the implementation address without issue. I’ve also tested it against the original clone-factory repo specified in EIP-1167 with mixed results. ContractFactory.isClone returns true for the modified ERC-1167 implementation as expected, while ContractProbe.probe returns an incorrect implementation address (since the detection logic is length-based).
Given that the risk of using a slightly non-standard ERC-1167 proxy implementation seems to be low while the benefit of allowing stateless account implementations is significant, returning to the original ERC-1167 proxy implementation seems like a good path forward.

Author: jan
Date: 2023-04-26T15:19:32.155Z

Awesome, @jay! In that case we’ll use the ERC-1167 footer for mech. (And I’ll go right ahead and update evm-proxy-detection )

Author: jay
Date: 2023-04-26T15:42:56.632Z

@jan fantastic! TIL about evm-proxy-detection 
Do you have any other thoughts/feedback on the structure of the Registry or the Account interface?

Author: conner
Date: 2023-04-28T19:25:16.565Z

I have a preemptive concern on the convenience of nesting NFT-accounts. If I’m not mistaken, right now to move an NFT 3 layers deep my EOA calls my parent wallet (layer 1) which auth’s my EOA owns the NFT, then calls wallet of one of its NFTs (layer 2) which auths that layer 1 wallet owns the inner NFT, which then calls layer 3 wallet, etc.
This seems like a problem on two main fronts:

encoding the parent call for my EOA goes through N layers of executeCall(to, value, data) which is a pain for the frontend dev building with a nested feature
this adds meaningful gas overhead for (a) additional calldata for the transaction, (b) many internal transactions hopping down each layer in the ownership tree, (c) storage updates from nonce increments in each layer.

My proposed amendment is to add an additional function specifically designed for executing calls on nested NFTs.
function executeCallNested(address to, uint256 value, bytes data, uint256 depth) external payable returns (bytes memory result) {
    address rootOwner = owner();
    address tokenContract;
    uint256 tokenId;
    for (int i = 0; i < depth; i++) {
        (_, tokenContract, tokenId) = IERC6551Account(rootOwner).token();
        rootOwner = IERC721(tokenContract).ownerOf(tokenId);
    }
    require(msg.sender == rootOwner, "Not token owner");
    
    bool success;
    (success, result) = to.call{value: value}(data);

    if (!success) {
        assembly {
            revert(add(result, 32), mload(result))
        }
    }
}

This function takes in an additional depth parameter for how many parents up the contract must loop over to find the root owner and finally checked against msg.sender. This does validation work upfront that would be performed anyways in absence of this function, but with cheaper view calls, less net calldata, no cascading nonce incrementing, and an obvious interface for developers.
On a similar note, I think it would be more convenient to build gating logic on nested NFT with a view function that returns if an address is an eventual parent of this nested NFT. I don’t think this one should be on the Account spec, maybe as a part of the global registry?
function isEventualParent(address account, address tokenContract, uint256 tokenId) external view (returns bool) {
    address owner = IERC721(tokenContract).ownerOf(tokenId);
    while (owner != account) {
        // no guarantee owner of token is IERC6551Account
        // if not, catch and return false
        try IERC6551Account(owner).token() returns (uint256 _chainId, address _tokenContract, uint256 _tokenId) {
            owner = IERC721(_tokenContract).ownerOf(_tokenId);
        } catch {
            return false;
        }
    }
    return true;
}

This function will keep going up the ownership tree until it finds the account of interest or hits an address that does not implement the ERC (e.g. EOA, other SCW, address(0)), therefore erroring out which is caught and triggers a false return.

Author: jay
Date: 2023-04-28T22:57:07.343Z

@conner thanks for highlighting this!
You’re 100% correct that with the reference account implementation you would have to go through N layers of nested calls to transfer a nested NFT, which isn’t ideal from a gas cost or user experience perspective. The two solutions you’ve proposed are great, and should definitely be considered as a feature at the implementation level.
One edge case that may break these two solutions is accounts that have not been deployed yet. An NFT could be nested 3 levels deep, but if either of the two parent account contracts have not been deployed, the token method will be unavailable to call, causing a revert.
An alternative approach that accounts for pre-deploy accounts would be to pass in a proof of ownership in the form of an array of structs containing information about the parent accounts. The account contract could verify this proof based on the computed addresses of the accounts in the ownership chain. It might look something like this:
struct ERC6551AccountData {
    address implementation;
    uint256 chainId;
    address tokenContract;
    uint256 tokenId;
    uint256 salt;
}

function executeCallNested(
    address to,
    uint256 value,
    bytes calldata data,
    ERC6551AccountData[] calldata proof
) external payable returns (bytes memory result) {
    (, address tokenContract, uint256 tokenId) = this.token();

    for (uint256 i = 0; i < proof.length; i++) {
        require(proof.chainid == block.chainid, "Invalid token");

        address account = ERC6551Registry.account(
            proof.implementation,
            proof.chainId,
            proof.tokenContract,
            proof.tokenId,
            proof.salt
        );

        require(IERC721(tokenContract).ownerOf(tokenId) == account, "Invalid proof");

        tokenContract = proof.tokenContract;
        tokenId = proof.tokenId;
    }

    require(require(IERC721(tokenContract).ownerOf(tokenId) == msg.sender, "Not root owner"));

    bool success;
    (success, result) = to.call{value: value}(data);

    if (!success) {
        assembly {
            revert(add(result, 32), mload(result))
        }
    }
}

This could be further optimized by computing the account addresses locally without calling into the Registry contract.
I think both of these functions are great additions to account implementations, as they will improve the feasibility of executing nested calls and determining the root owner of a set of nested NFTs. However, I’m not sure whether the implementation of these functions should be mandated by the proposal. Perhaps these would be better implemented in a companion library that account implementation authors could include if they wanted to opt into these features.

Author: RobAnon
Date: 2023-05-01T20:01:31.993Z

So I’ve been building Financial NFT-based wallets for years now, and I’ll be honest, I’m a bit disappointed that Revest and my team weren’t a part of this EIP’s formulation. We have some stuff we’d love to contribute to it and some EIPs we’d love to branch off of it. Currently in the process of building our V2, were important in getting 6780 put together, and definitely don’t think there should be a standard surrounding the core tech we’ve developed that we aren’t a part of helping out with.
@jay I’d be very interested in getting connected. You can find me on Telegram as “RobAnon” - let’s get something put together, because having chain-wide standards for this sort of thing has been a long-time goal of ours.

Author: RobAnon
Date: 2023-05-01T20:03:49.579Z

Replying to this thread to make sure it’s included. We’re interested in working together with you on this EIP and either including naive support for ERC-1155, or developing an extension of this EIP in parallel to support it. Also want to discuss valuation mechanics, as those are crucial to the success of such a system.

Author: jay
Date: 2023-05-01T21:01:30.893Z

Hey @RobAnon! We would welcome your contributions to this proposal. It is still in draft specifically so that folks interested in this pattern can contribute before it goes to review.
The biggest difference between Revest’s product and this proposal seems to be that this proposal applies to all ERC-721 tokens, while Revest seems to only work with ERC-1155 FNFTs created using the protocol. Please correct me if I’m wrong about this.
The key challenge to supporting ERC-1155 tokens is the lack of a canonical owner for a given token. In the absence of a canonical owner, how would you recommend handling authorization at the account level? How does Revest handle this? If there is a canonical owner for each ERC-1155 token in a collection, these tokens can already be supported within the current structure of this proposal by adding an ownerOf method to the ERC-1155 contract (as ENS NameWrapper does).
For ERC-1155 tokens without a canonical token owner, given the lack of totalSupply support in the ERC-1155 standard, it doesn’t seem like there is a clean way to determine who is allowed to control the account without resorting to 1-of-n execution (anyone whose balance is greater than zero has owner permissions on the account). This makes it challenging to support every ERC-1155 token that exists, which would be ideal if ERC-1155 support is explicitly included in the proposal.
There are a few options to add ERC-1155 support that have been considered:

Remove the concept of a canonical owner address in favor of an isOwner(address) returns (bool) function which determines whether a given wallet is the owner of a token. The implementation would be responsible for determining if an address holds the correct balance of tokens to be considered an owner. This requires a change to the account interface, complicates the security model, and makes it difficult to optimize for the current use case which is ERC-721 tokens (which have a canonical owner).
Parameters under which ERC-1155 token ownership is canonical can be passed in the salt value to a custom account implementation. This could be min/max balance thresholds for example. The implementation could allow “claiming” of an account, causing the caller to become the canonical owner if they meet the balance threshold requirements. This would break the current behavior of automatic transfer of account to the new canonical owner when the underlying token is transferred, which seems less than ideal.
ERC-1155 token bound accounts could be considered unique per holder/token combination. This would mean that every unique holder of an ERC-1155 would have a corresponding token bound account address, with optional balance threshold enforcement at the implementation level. This can be accomplished by passing in the holder address as a part of the account salt. This means that there would not be a single canonical token bound account for a given ERC-1155 token, but rather many accounts, one per holder. Depending on the desired use case, this might be useful.
ERC-1155 tokens could be “wrapped” into an ERC-721, allowing for usage with the existing architecture without any proposal changes.

I’m sure there are other approaches here as well, and would love your thoughts on them drawing on your depth of experience at Revest. We’d love to reconsider ERC-1155 support as a part of this proposal, but understand that the significant differences in functionality between ERC-721 tokens and ERC-1155 tokens may necessitate a separate proposal.

Author: RobAnon
Date: 2023-05-01T21:24:05.491Z

Revest V2 is slated to support both 721 and 1155. It makes sense for us to ensure that positive changes we’ve introduced within it are reflected in the system that you build here. It also makes sense for you that we use this standard in production, as that’d propagate it further and help drive consolidation efforts.
We view this proposal in the context of a series of three proposals that should be published to describe a unified set of Financial NFT standards. I agree with you that 721 might make sense as its own standard, but to be completely honest, I think that even having an NFT type attached to this standard is likely unnecessary. 1155 singletons work well with the 721 system, and while they don’t support ownerOf, they would function just fine on their own. External to this, if you window the functionality to deposit/withdraw functions, 1155s work just fine even with their multiple owners. We use our internal supplyOf(address) and balanceOf(address) to determine what proportion of a given ERC20 an account owns. When you limit these wallets on a per-asset basis, it opens a lot of doors - that’s what we’re interested in discussing w/r/t 3 proposals.
Can you help me understand why you feel it is necessary to have an ownerOf function in the smart wallet?
With respect to supplyOf, EIP-5615 seeks to address this. Revest FNFTs already support this, as it is a necessity for determining subdivideability.

Author: 0xTraub
Date: 2023-05-01T21:54:41.163Z

I’m the dev working on Revest V2. A potential implementation for the system is that only the user who owns the entire supply of a given 1155 is able to access the smart wallet. It is functionally the equivalent of having a canonical owner like in 721 but being slightly more strict. It requires some extra work on behalf of the wallet deployer to ensure this but is certainly better than nothing and works in conjunction with EIP-5615

Author: jay
Date: 2023-05-01T22:44:14.628Z

@RobAnon Thanks for providing some extra context! Would love to ensure that Revest V2 can use this proposal.
To clarify: singleton ERC-1155 tokens will work out of the box with the structure of this proposal. All that’s necessary is a custom account implementation that can correctly determine the canonical owner given a token ID. Adding an ownerOf method to the ERC-1155 just means you can use existing account implementations designed for ERC-721 tokens without the need for custom account logic.
Having a single canonical owner is important for a few reasons. First, it allows for (partial) ERC-173 support and (full) ERC-5313 support, making token bound accounts compatible with existing tooling that uses owner() calls to determine smart contract ownership. This seems like a desirable characteristic. Secondly, it enables automatic transfer of account ownership when the underlying asset is transferred. These two traits seem very desirable in the context of ERC-721 tokens, but may be less important in the context of ERC-1155 tokens.
One goal of this proposal is to give every NFT a self-sovereign account, allowing NFTs to execute arbitrary on-chain actions just like any other account. Restricting the actions a wallet can take is the responsibility of the account implementation to enforce, not the registry.
One potential low-hanging-fruit proposal change which would allow ERC-1155 support within this proposal would be to specify that the return value of owner() may be the zero address for tokens that do not have a canonical owner. This would allow the development of ERC-1155 backed token bound accounts without changing the existing account interface. Revest’s custom implementation could use EIP-5615 balance calculation to determine execution permissions, while still allowing the canonical account owner to be queried for ERC-721 token account implementations.

Author: jay
Date: 2023-05-01T22:47:44.924Z

Great to have you here @0xTraub! This is definitely possible with the changes I mentioned in my previous post.
The challenge with using this method within an owner() function is that you need more information than just the token ID to determine the canonical owner of an ERC-1155 token. Specifically, you need to know which account to check the balance of. This data is unavailable within the context of the current owner() function for ERC-1155 tokens, whereas it is queryable using the ownerOf method for ERC-721 tokens.

Author: 0xTraub
Date: 2023-05-02T00:56:07.927Z

So my thought process is that if we know the user address and we know the id of the 1155 it is possible to use the system I described. We just do this
uint balance = balanceOf(address, id);
uint supply = supplyOf(id);
require(balance == supply, "restricted to owner");

In this case the owner is whomever owns 100% of the supply of the 1155 ID. It’s not idea especially if that 1155 is traded frequently but its certainly better than nothing and provides some notion of ownership.

Author: jay
Date: 2023-05-02T02:05:26.225Z

@0xTraub this is correct, and works out of the box with this proposal in cases where the user address is known (when authorizing execution of a transaction for example).
However, within the context of an external owner() call the user address is not known (for example, if an external contract/dapp is checking ownership of an unknown account).
The owner() call issue can be solved by either:

Storing the current owner of an account in storage and allowing callers to “claim” control of the contract if they meet certain balance thresholds (option #2 listed above). Importantly, the owner() method does not need to be used to authorize transactions.
Modifying the spec to specifically allow owner() to return zero if there is no canonical order

In either case, this seems to be a relatively low lift and allows for native ERC-1155 support within this proposal. These approaches might not be a perfect fit for Revest v2 if you have strict requirements around minting/burning ERC-1155 tokens, but if Revest uses centrally controlled NFT contracts already it might make sense to bake this logic into the ERC-1155 tokens themselves.
Would love to understand if these changes allowing for ERC-1155 support would make it possible for Revest v2 to adopt this proposal.

Author: wighawag
Date: 2023-05-03T09:03:21.949Z

Really cool!
I just had a quick look and thought about few things:

would be good for the account contract to have support for ERC-1271 so it can sign messages too
the account contract currently only support call it should also support create, create2, delegate_call

Author: jay
Date: 2023-05-03T13:48:54.474Z

@wighawag thanks for the feedback!
ERC-1271 support is required for all accounts already (see the Account Interface section).
I agree that the execution interface is a bit rough at the moment. The intent behind not mentioning delegate_call, create or create2 is to minimize the required interface for accounts. Account implementations that wish to add support for other calls can do so outside the scope of the proposal (e.g. via an executeDelegateCall method).
As smart contract accounts become a more popular pattern, I think there needs to be a separate EIP that proposes a standard execution interface for smart contract accounts. The closest I’ve seen so far is ERC-725X, but it’s been in draft for a very long time. We would support a proposal attempting to standardize this.
In the absence of a standard contract account execution interface, this proposal includes a minimal execution interface to ensure all 6551 accounts can be called in the same way regardless of implementation. Would welcome any feedback on how we could make this interface more flexible or more standardized.

Author: wwhchung
Date: 2023-05-03T22:58:38.228Z

There are also situations where we don’t want to support 1271 (or any real functionality).  Consider a wallet holding a bunch of soulbound tokens where I don’t want anyone else adding any other tokens to it.

Author: jay
Date: 2023-05-03T23:09:08.639Z

@wwhchung this is interesting! One thing I’ve been thinking of in this area is the concept of a null account - an account where the implementation address is set to the null address. This would give every NFT an address that can receive assets without allowing future withdrawals or executions. This would essentially provide a mechanic for permanently binding any onchain asset to any NFT. Curious to hear if this would fit your use case.

Author: 0xTraub
Date: 2023-05-04T17:33:18.050Z

@jay What would be the benefit of permanently binding a non-executable or withdrawable asset to an existing NFT? Having these emphemeral wallets binded to a revest FNFT makes sense because it allows you to gain at least some kind of utility from it during the length of the locking period.

Author: jay
Date: 2023-05-04T17:56:23.852Z

@0xTraub The use cases I’m thinking of are mostly around unique burn mechanics for NFTs. Imagine an NFT that evolves over time based on the type of tokens / value of tokens that you’ve sent to its null account. Obviously a null account burns every asset which means the assets are lost, but it burns them in such a way that they have provenance which could be valuable in some circumstances.
This seems like a nice little edge case that folks could get creative with, not something that is a primary use case of the proposal.

Author: 0xTraub
Date: 2023-05-04T18:00:02.326Z

@jay so it would have value based on the owner of the NFT that burned them? Not sure why that gives the NFT value. Also that would seem to have a potentially negative effect on the seller. If Alice burns an asset through this mechanism and then sells the NFT well they’re no longer able to reap the rewards of having burned the item, and it would be attributed to the person who buys it, which sounds like giving them applause for doing nothing at all. Not sure what the benefit here is for this. I’m not against it but sounds unnecesarry.

Author: jay
Date: 2023-05-04T18:04:26.424Z

@0xTraub This is fairly tightly scoped and would only be useful to NFT projects that would otherwise make use of burn mechanics. Definitely doesn’t apply to all NFTs. I don’t think it’s necessary either, but I do think it has creative applications in a similar way that burn mechanics have creative applications.

Author: sullof
Date: 2023-05-09T21:23:49.913Z

I like the idea but I am concerned about addressing potential MEV-related issues.
Imagine a situation where an account holding 1M ETH is linked to an NFT. A malicious owner could collude with miners to sell the NFT on OpenSea. When the NFT is sold, the miner could prioritize a transaction to transfer the 1M ETH elsewhere before the atomic swap occurs.
One possible solution for OpenSea is to verify the wallet balance before performing the atomic swap, using the balance as a parameter, and ensuring it remains unchanged during the swap. However, it seems unlikely that OpenSea would adopt this approach. Moreover, even if implemented, a malicious owner could still add numerous fake assets to the linked account, making it impossible to confirm the full balance without exceeding the gas limit.

Author: jay
Date: 2023-05-09T22:14:37.683Z

@sullof this is an important issue that affects all stateful NFTs, not just NFTs with token bound accounts. Ultimately marketplaces are responsible for ensuring the safety of their users when performing trustless NFT sales. Preventing fraud is beyond the scope of this proposal, as mentioned here.
With that said, we’re collaborating with several marketplace teams to ensure that trustless sales of NFTs with token bound accounts can be performed safely. To take Seaport as an example, this type of fraud can be prevented by passing in the address of a smart contract (called a zone in Seaport lingo) that can execute arbitrary validation logic after an order has been processed in order to determine whether the contents of the token bound account have been maliciously altered in any way. By looking at the nonce of the account, the zone can determine whether any transactions have been executed from it which would invalidate the order, without running into gas limits. Approval-based transfers are an obvious edge case here, but there are several validation/prevention strategies that can be used to address that as well. Marketplaces can also help to prevent fraud at the client level by showing the contents of the token bound account in the UI along with all active approvals.
While fraudulent attacks using stateful NFTs are definitely a concern, there are clear paths forward for marketplaces to prevent them.

Author: INastro
Date: 2023-05-10T15:59:50.533Z

Hello, I’m interested in the following line:
if (chainId != block.chainid) return address(0);

Why is chainId important?
What kind of attack does it protect against?
I can’t understand why we can’t omit it

Author: jay
Date: 2023-05-10T16:26:02.905Z

@INastro Great question! The proposal leaves open the possibility of cross-chain token bound accounts if implementations would like to support that. This would mean you could have an account on one chain bound to an NFT that exists on another chain. This line of code checks whether the NFT an account is bound to exists on the current chain, and returns the zero address if it is bound to an NFT on another chain.

Author: sullof
Date: 2023-05-13T21:11:19.844Z

I took a look at your reference implementation.
It would be good to add a test showing how you make a simple transfer of an ERC721 from a bound account to a recipient address. There is a transfer at the end of testOwnershipChain, but it is a complex case and a simpler case would help understand how to implement your proposal.

Author: sullof
Date: 2023-05-13T22:00:03.735Z

I figured it out 
But an example in the tests would be useful anyway.

Author: petermazzocco
Date: 2023-05-15T14:32:58.812Z

Really awesome and forward thinking idea! Very easy to read and love it, hoping to work on this for a test project in the future.

Author: jay
Date: 2023-05-15T20:49:16.804Z

sullof:

I figured it out


Glad you figured it out! Feel free to submit a PR to the reference repo to add tests for this case

Author: sullof
Date: 2023-05-15T21:56:13.003Z

I will think about it, but for me it would be probably easier to make an hardhat version of the entire repo.

Author: MidnightLightning
Date: 2023-05-16T00:05:27.233Z

This EIP seems to be a blend of a standard (a central Registry of smart-contract-wallets keyed to specific NFTs) and implementation (forcing them to be proxies). For clients interacting with this standard, they’d not have a need to know if the NFT-keyed-accounts are proxies or not, right?
The logic of a token-controlled smart-contract-wallet (how it gates access) seems like the bulk of the logic of this EIP (since the Registry is fairly simple as a lookup mapping)? Rather than having these wallets be forced to be one-to-one (one NFT gives access to one wallet), having it be flexible enough to also create multi-sig wallets (similar to a DAO that gives out membership “shares”, granting access to a specific set of NFT identifiers) would enable more use-cases, I believe?

Author: MidnightLightning
Date: 2023-05-16T00:14:29.562Z

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.

So is the intention to encourage many different platforms to launch ERC6551 Registries, each that generate different types of token-bound accounts? If a project wishes to add more roles (in addition to “Owner”, pulling in the “Approved” from the ERC721 contract, as well as other delegated roles), the expectation is they’d design and launch their own Registry with their project’s custom logic? So over time we’ll end up with each NFT having many different accounts scattered across different registries, and wallet/portfolio software that wants to enumerate all the things “owned” by a given NFT will need to have a cached/known list of Registry addresses to iterate through?

Author: sullof
Date: 2023-05-16T01:05:37.685Z

MidnightLightning:

So is the intention to encourage many different platforms to launch ERC6551 Registries, each that generate different types of token-bound accounts?


The registry is agnostic about the implementation, and you can use the same registry for any kind of account.

Author: sullof
Date: 2023-05-16T01:08:56.105Z

@jay I am implementing ERC-6551 in a project. Initially I wanted to use EIP-3652: Hierarchical NFT which solves a similar problem. I am currently evaluating the pros and cons of the twos. I would appreciate your opinion about it.

Author: k06a
Date: 2023-05-16T06:40:22.515Z

Sad that EIP-3652 was not even mentioned here, bit I believe this EIP is probably inspired by it.

Author: MidnightLightning
Date: 2023-05-16T11:41:05.880Z

sullof:

you can use the same registry for any kind of account.


Aha! Thanks for that tip; I delved more into the example code of the intended Registry contract. I was expecting the Registry to be a 1:1 mapping tool for each NFT to have one Token-Bound Account. But the current proposed code is the one master registry allows anyone to proxy any address for any NFT token. There’s no enumeration functions in the Registry, so no on-chain looping through all addresses generated for a specific NFT token. Off-chain, there’s an Event log of AccountCreated that can be used to find previously-generated addresses, but none of the parameters of that Event are Indexed, so off-chain iteration would need to grab ALL the events to find the ones for specific tokens?

Author: MidnightLightning
Date: 2023-05-16T12:00:28.011Z

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.

I realized that this EIP doesn’t actually require the “token” be ERC-721-compliant in order to be the “key” used to unlock a token-bound account (there’s no ERC-165-check or other introspection of the collection contract when deploying a token-bound account to ensure it’s ERC-721-compliant at all). The only aspects of the token that are used is an address and a uint256. Any protocol that is able to determine a unique “owner” for a distinct combination of an address and a uint256 would technically work with this Registry concept?
This could open the idea of token-bound account implementations that use custom token-ownership-determining logic, so collections that don’t use a specific token standard (e.g. historic collections that existed before the ERC-721 standard existed) could use an ERC-6551 Registry. Therefore, is it important to lean so heavily into referencing ERC-721 in the EIP writeup, as opposed to just referring to them as “Tokens” (and noting that any standard that is able to determine unique ownership from a uint256 is compatible, including ERC-721)?

Author: jay
Date: 2023-05-16T12:11:29.323Z

k06a:

Sad that EIP-3652 was not even mentioned here, bit I believe this EIP is probably inspired by it.


Great to have you here @k06a! We’ve been trying to reach you to chat about this proposal for some time.
Yes, this proposal takes a similar approach to the one that EIP-3652 takes, but uses a registry instead of embedding the account creation logic into the NFT contract. Unfortunately due to quirks of the EIP process (not being able to reference EIPs that have not made it to draft status) we were not able to mention 3652 specifically in the proposal document. We would love your contributions to this proposal as someone who has thought deeply about the concept of NFTs having accounts, and would be happy to have you as an author.

Author: jay
Date: 2023-05-16T12:21:57.851Z

MidnightLightning:

For clients interacting with this standard, they’d not have a need to know if the NFT-keyed-accounts are proxies or not, right?


Good question! By deploying accounts using 1167 proxies, it allows the bytecode for all token bound accounts to be knowable and computable, which makes generating deterministic deployment addresses easier. The alternative would be to have a single central implementation (not good) or require users to pass the entire bytecode of the contract they would like to deploy to the registry (which would be much more calldata and would require bytecode lookups).



 MidnightLightning:

I was expecting the Registry to be a 1:1 mapping tool for each NFT to have one Token-Bound Account. But the current proposed code is the one master registry allows anyone to proxy any address for any NFT token


This is correct. The goal is to have a single entry point for all clients to be able to query token bound account addresses and deploy them, which avoids the fragmentation of multiple registries. Using the current registry, any NFT can have an unlimited number of addresses using an unlimited number of implementations / salts. This gives NFTs the same “rights” when it comes to wallet creation as users of EOA wallets.



 MidnightLightning:

Off-chain, there’s an Event log of AccountCreated that can be used to find previously-generated addresses, but none of the parameters of that Event are Indexed, so off-chain iteration would need to grab ALL the events to find the ones for specific tokens?


This is a great catch! There has been some conversation around which of the arguments should be indexed, as there are more arguments than can be indexed in a single event. Would love your opinion here! Indexing on the events will be added based on feedback collected as folks review the EIP.



 MidnightLightning:

Any protocol that is able to determine a unique “owner” for a distinct combination of an address and a uint256 would technically work with this Registry concept?


This is correct! It would require a custom implementation if you’re using something other than ownerOf to determine the canonical owner, but the choice of how to determine the canonical owner based on an address and a uint256 is left to the implementation. This does open the door to support for other types of tokens than ERC-721. We’re working on an update to the proposal that tones down the focus on ERC-721 tokens and allows for more flexibility when determining the owner of the account.

Author: k06a
Date: 2023-05-16T13:16:47.266Z

What do you think of minimising proxy bytecode? Example here: Extract lib, add getters for proxies by k06a · Pull Request #4 · 1inch/ERC3652 · GitHub

Author: jay
Date: 2023-05-16T13:37:38.949Z

@k06a would love to explore this! I think it’s important that the first 45 bytes of the bytecode match the existing ERC-1167 proxy bytecode in order to allow for wide compatibility with existing proxy detection mechanisms, but would love to see the encoded data at the end of the proxy compressed further.
Did you have any initial thoughts around how you’d like to see the bytecode of the proxy changed?

Author: MidnightLightning
Date: 2023-05-16T13:55:27.164Z

jay:

Would love your opinion here! Indexing on the events will be added based on feedback collected as folks review the EIP.


My vote would be to index the token collection address, the token ID, and the implementation address. That would allow answering the queries of:

Which token-bound accounts does token X have?
What token-bound accounts belong to tokens in collection X?
What token-bound accounts use implementation X?

Which I think are the most common introspection queries that will be needed.
Queries that cannot be answered by those indexed values include:

What token-bound accounts exist that used nonce X to deploy?
What token does the token-bound wallet at address X belong to?
When was the token-bound wallet at address X created?

The first query I think would be rare to need/use. The other two can be answered using other blockchain cues, so don’t need to have event topics indexed to supply them.

Author: sullof
Date: 2023-05-16T21:01:10.757Z

I am working on that specific case, where the bound wallet is bound to a non-ERC721 contract.
I was thinking of an interface like:
interface IERC6551Owned {
   function ownerOf(uint id) external view returns (address);
}

In this case, the protocol would check if the contract supports the interfaceID IERC721 or IERC6551Owned

Author: blueflymedia
Date: 2023-05-16T21:23:17.864Z

Love this proposal! So, so many use cases. I’m excited that this and other proposals are really working towards optimizing not just the gas but also the work needed to be done by front end devs. I’m new to this whole process, how long does it usually take for these to be pushed through? Can’t wait to try it out.

Author: jay
Date: 2023-05-16T21:27:33.029Z

sullof:

I am working on that specific case, where the bound wallet is bound to a non-ERC721 contract.


Would love to hear more about the use case you’re working on!



 sullof:

In this case, the protocol would check if the contract supports the interfaceID IERC721 or IERC6551Owned.


I don’t think an interface check at the registry level is necessary. Implementations are welcome to check for these interfaces, but I don’t think the proposal should require implementations to do so.

Author: scotthconner
Date: 2023-05-16T21:46:52.188Z

I’m fascinated by ERC-6551.
I’ve been working on a very similar vision at Locksmith Wallet (locksmithwallet.com). Quick shill to please read my white paper if you are interested in this ERC.
It uses a specific ERC-1155 contract that enables account owners to mint their own private collection of NFTs. Major departures from the base ERC-6551 vision is:

Multiple NFTs per Account: Each Account can have its own collection of NFTs, with permissions. Can enable multi-sig schemes as well.
Multiple Storage Locations: Store your assets in an on-chain vault, inside of a liquidity pool, or somewhere else, and the account can keep track and maintain security and permissions.
Account owners can mint, burn, or “soulbind” tokens to specific addresses to embed in contracts or prevent phishing scams, signature risk, or re-selling/collateralization.

I’ve looked at the spec and I feel like I can make the VirtualKeyAddress and PostOffice contracts compatible with the IERC6551Account, as well as the Registry. In this way, I can offer my users a ERC-6551 compatible experience even though the project existed before the ERC.
A couple of questions I have (hopefully did not miss the details above):


Do I have to integrate with a specific registry, or is having my own considered to be ERC-6551 compatible enough?


Today, I assume that the EOA simply holds the NFT, and all of the assets (except gas, looking at you EIP-3074) are all on-chain. Does ERC-6551 prevent 4337 set-ups, and/or does it also require an EOA and gas (I imagine it would?)


Does the custom-made ERC-1155 for Locksmith (And all of its admin capabilities) somehow proclude it from being ERC-6551 compliant? It’s not entirely clear to me.


Why does IAccount return an address for the owner field? I would expect it to return an address and token ID, because isn’t the account owner an NFT holder, not a specific address? What design decision went into that choice?


Thanks for this awesome ERC and looking forward to contributing to the 6551 ecosystem going forward!

Author: sullof
Date: 2023-05-16T22:05:35.477Z

jay:

Would love to hear more about the use case you’re working on!


I am working on a vault system that add extra-protection to the owning NFT (if not already deployed) and the owned assets. Both ERC6551 and ERC3652 work for me. Basically the Vault deploys an utility NFT that is owned by the Vault and that will have a bound account. The advantage of this approach is that as long as the user trusts the vault, they can let the Vault managing the assets, but at any moment the account can be “ejected” transferring the corresponded utility NFT to the owner of the OwningNFT.
In another variant, the contract that owns the bound account is not an NFT.



 jay:

I don’t think an interface check at the registry level is necessary. Implementations are welcome to check for these interfaces, but I don’t think the proposal should require implementations to do so.


I agree. Also because applying IERC721 to the address and calling ownerOf would do the same.

Author: jay
Date: 2023-05-16T22:10:59.206Z

@scotthconner awesome to have you here, thanks for the comments and questions!

One of the goals of 6551 is to have a single registry that can handle all token bound account implementations. This has the benefit of giving the ecosystem a single entry point to query for addresses, and ensures that every address is deterministic so that it can receive assets prior to deployment. If there’s something about the existing registry that makes it incompatible with your use case, I’d love to better understand the issue and explore some potential solutions with you.
Any account (EOA or smart contract) can own the NFT, and is granted execution permissions. Token bound accounts can be 4337 compatible (allowing them to pay their own gas fees) and/or be directly callable (allowing the NFT owner to pay gas).
There are a few paths toward ERC-1155 support that are outlined earlier in this thread. It depends a little bit on how Locksmith 1155 tokens are structured (the biggest being whether they have a single owner per token ID or multiple). With a custom account implementation contract 1155 tokens can be handled in most cases.
This is to enable (partial) support for EIP-173 and (full) support for EIP-5313, which are used to determine contract ownership in some applications. Since returning token information from the owner function would break compatibility with these types of applications, an additional token method was included to query the this data. In the case of ERC-1155 tokens with multiple token owners the owner method is not useful. Changes to this function have been heavily requested, and we’re planning to change it to explicitly support use cases without a single canonical owner.

Author: scotthconner
Date: 2023-05-16T22:23:53.473Z

Thanks Jay.
I imagine the singleton registry is to facilitate the discovery of the account address so DApps have a clear way of interacting with the account. Is that right? If so, have we considered putting discovery on the NFT contract itself? I realize some of the goal is to make it compatible with all existing NFT contracts, so a registrar() interface wouldn’t be backwards compatible, but it would allow you to send someone an NFT and immediately be able to discover all the on-chain metadata required to interact with the account without succumbing to a centralized registry.
My implementation has a “PostOffice” contract which holds a registry of virtual account contract addresses mapped to individual ERC-1155 token IDs. In this way, I have my own unified account registry, that my application uses to facilitate many things under the hood for virtual accounts, and the UI. To be fully compatible, I wouldn’t necessarily want to adopt another registry, and it might require some indirection for me to properly integrate. I’ve also made the design decision to require deployment before usage, versus counterfactual addresses, so the creation workflow is a bit different. I could change this, but for simplicity I went with what I did.
In either case, I have my own registry. I imagine others will to. For me, I also have my own custom ERC-1155. This enables an account owner to create a fully managed collection of NFTs that have a parent/child relationship. In this way, any root key holder for a “Trust” as it is called can freely mint, copy, or burn any NFT in their collection, or create new ones. Because the analogy is each NFT is a “key”, they operate like physical keys would and so there could be multiple holders of a particular key/permission/account. So a virtual account could be actually owned, at a contract level, by an NFT, and anyone holding it is considered an owner, even if the trust owner has minted multiple copies of it.
Architecturally - everything else is very similar. You have an NFT contract (KeyVault.sol), a virtual account (VirtualKeyAddress.sol), and a registry (PostOffice.sol). There are other moving pieces but those likely aren’t relevant to the ERC.
My main interest in this ERC are two fold:

The mission to prove that NFT’s are more than JPEGs is gaining steam and folks are seeing the prospects of token gating increasingly complex contract functionality!
I’m actively wondering if this will solve my impending dapp integration problem. I want users to be able to easily interact with DApps, without those Apps necessarily doing a direct integration with Locksmith (as using the NFT keys as session keys allow for things like no-approval swaps. Will DApps be able to support 6551 accounts and thus transparently support projects like Locksmith if I add adapters for the interface?

Author: sullof
Date: 2023-05-17T03:43:06.382Z

While implementing ERC-6551, I was thinking of possible security issues.
Let’s say that someone creates a malicious NFT that when the user executes a safeTransferFrom with a delegated call attacks the executer. Since to transfer something from the bound account to another wallet, we should trigger executeCall in the ERC6551Account smart contract, the malicious NFT could make any kind of damage, including destroy the bound account calling a self-destruct.
Am I wrong? If not, I guess we must wait for the Duncan hard-fork before using it in production.

Author: scotthconner
Date: 2023-05-17T14:15:42.287Z

@sullof I think any calls coming out of the NFT contract from inside _beforeTokenTransfer or safeTransferFrom would have the NFT contract itself as the message sender.
Assuming authentication for account actions requires that the NFT is held by the message sender, the NFT contract itself might not be able to attack the tx.origin or the account itself. At least, that is the way I’m thinking about it.
To your point though, being able to use any NFT opens up a bunch of re-entrancy opportunities and undefined behavior. For instance, an ERC721 could be fully conformant on the interface, but allows the NFT contract owner to mint their own copies of the NFT or otherwise steal any virtual accounts attached to it.
From a security perspective, this is why at least for my smart wallet project I’ve enshrined a specific NFT contract that acts as the permissions for the virtual account. The same security holes plague generic ERC-20 coins and thus the token contract itself is a critical component of any on-chain account’s security model.
In this way, using an ERC-6551 account as conceived would mean that:

DYOR on which NFTs are considered secure to use for ERC-6551.
Making the wrong choice could compromise the entire account.
The threat model for the account is the entire NFT contract, the ERC-6551 implementation, and all subcomponents, combined.

Author: jay
Date: 2023-05-17T14:30:16.534Z

sullof:

Let’s say that someone creates a malicious NFT that when the user executes a safeTransferFrom with a delegated call attacks the executer.


@sullof In general, smart contract accounts should not execute delgatecall against untrusted contracts as that is a security risk. This is true for 6551 accounts as well as any other type of smart contract account. In the case of malicious NFTs held by a 6551 account, the account would use call instead of delegatecall to execute safeTransferFrom. There is little to no security risk in doing so, as the malicious contract would have no special permissions on the 6551 account to perform reentrancy attacks.



 scotthconner:

To your point though, being able to use any NFT opens up a bunch of re-entrancy opportunities and undefined behavior. For instance, an ERC721 could be fully conformant on the interface, but allows the NFT contract owner to mint their own copies of the NFT or otherwise steal any virtual accounts attached to it.


@scotthconner I don’t think there are reentrancy risks here, but you are correct that 6551 accounts are only as secure as the underlying NFT. If the underlying NFT contract allows the creator to arbitrarily change the owner of a given NFT, then the account bound to that NFT could be compromised. This is by design, and is a necessary trade off in order to allow every NFT to have it’s own account.

Author: sullof
Date: 2023-05-17T15:58:35.976Z

Thanks for the anwers.

Author: lughino
Date: 2023-05-17T18:43:20.281Z

Hi all,
I’m excited about this new EIP!
I’m a little bit confused about a use-case:
Let’s say I want to implement a game where NFTs holders can buy accessories like hats, glasses, etc.
When an NFT holder buys an accessory, the character of the NFT should change to wear that accessory.
Do I still need to change the metadata of the NFT for that to point to the correct image?
Sorry if it’s a stupid question, but I could not figure out if this EIP could help in this use case.

Author: jay
Date: 2023-05-17T19:00:54.685Z

Hi @lughino! This is something you can definitely build on top of ERC-6551, but it’s outside of the scope of the proposal. You would need to setup your metadata server to query the contents of the token bound account and modify the artwork of based on the contents.

Author: ashhanai
Date: 2023-05-18T12:06:55.648Z

This is a genuinely brilliant EIP proposal! I especially love the fact that it aims to support all existing NFTs.

Imagine a situation where an account holding 1M ETH is linked to an NFT. A malicious owner could collude with miners to sell the NFT on OpenSea. When the NFT is sold, the miner could prioritize a transaction to transfer the 1M ETH elsewhere before the atomic swap occurs.

Can EIP-5646 help here? The idea is to have a lockable token-bound account with an incremental nonce. Whenever the account is locked, the nonce will increase and update the EIP-5646s getStateFingerprint function. Marketplace/lending protocols can use the fingerprint in their offers. Of course, it would be ideal to propagate the information into the NFT of the account, which can be solved by implementing an auxiliary contract computing the fingerprint for the original NFT. Even without the auxiliary contract, this could be a semi-standard way to solve the issue of front-running withdrawals.
I used similar “account ownership via token” approach in a bundler. You can check it out here TokenBundler/src at develop · PWNFinance/TokenBundler · GitHub

Author: lughino
Date: 2023-05-18T12:36:19.351Z

Hi @jay !
Thanks for coming back to me so quickly 
That makes sense! I was under the impression that I could do it in IPFS, am I wrong?

Author: sullof
Date: 2023-05-18T17:12:35.662Z

You can implement the ERC6551Account the way you like, which gives you a lot of flexibility. In the end, if your user’s profile changes based on what they have in their wallet, if the wallet is a standard EOA, like MetaMask, or a bound account, it changes little. The logic to handle it will be outside the wallet. Think of ERC6551 as a different way of generating wallets. What people put in the wallet, and how this content is managed is out of the scope of this EIP.
Thus said, if the profile starts with an NFT owned by the user, then, IMO, adopting ERC6551 is better than ask user to use their MetaMask

Author: MidnightLightning
Date: 2023-05-18T22:05:51.874Z

Having an ERC721-style NFT with metadata that changes is possible, and depending on how you do it could use IPFS as the storage for the metadata file, but it could also be generated on-chain. All of that is out-of-scope for this specific EIP. If you want to start a separate thread on your questions about making that sort of “dynamic NFT” in general, I’d be glad to give a more thorough answer (I’ve helped develop several of those sort of assets).

Author: lughino
Date: 2023-05-19T17:39:36.855Z

Thanks @MidnightLightning !
I just created a new thread.

Author: sullof
Date: 2023-05-22T01:51:06.413Z

I am using the reference implementation of ERC-6551 in the Cruna Protocol, but I also played with ERC-3652 and the second produces much smaller proxies.
The ERC6551AccountProxy generates a bytecode of 964 bytes, while the ERC3652Proxy generates a bytecode of only 322 bytes, saving a significant amount of money on expensive chains like Ethereum.
It would be great if the two projects could merge and I would like to know what @jay and @k06a think of it. Since I will most likely merge the two approaches to optimize Cruna’s code, I would be happy to help if you decide to merge.

Author: jay
Date: 2023-05-22T04:34:49.731Z

@sullof I’m not sure that these numbers are correct, since proxies deployed by the ERC-6551 registry are only 141 bytes in length (45 bytes for the ERC-1167 proxy and 96 bytes for the static data appended to the bytecode). You can see an example of the deployed proxy bytecode here.
With that said, @k06a’s contribution to this proposal would be welcomed.

Author: sullof
Date: 2023-05-22T16:06:51.263Z

Uhm, the bytecode I get compiling reference/ERC6551AccountProxy.sol at main · erc6551/reference · GitHub is that long. I guess I am making some error. Thanks for pointing out to that. I will try to figure out what is wrong in my calculations.

Author: jay
Date: 2023-05-22T16:26:26.798Z

@sullof ah I see! That contract is part of an example upgradable account implementation. In this case, ERC6551AccountProxy is a singleton - only one copy of the contract needs to be deployed. The proxies created by the registry will point to the ERC6551AccountProxy contract as the implementation address. This means that the cost for creating the ERC6551AccountProxy contract only needs to be paid once instead of every time an account is created via the registry.
In the case of ERC-3652, it seems like the 322 byte proxy is deployed each time an account is created. This means that ERC-6551 account proxies will cost ~1/2 as much gas to deploy.

Author: sullof
Date: 2023-05-23T00:13:33.096Z

That sounds great. Thanks for explaining.

Author: neverlander
Date: 2023-05-28T12:41:11.008Z

Interesting proposal and discussion. Are there any apps out there implementing this standard (sans the Draft status)?

Author: sullof
Date: 2023-05-29T02:40:02.051Z

neverlander:

Interesting proposal and discussion. Are there any apps out there implementing this standard (sans the Draft status)?


I am implementing it in the Cruna Protocol
https://github.com/cruna-cc/cruna-protocol
@jay In the IERC6551Account interface, we have
function owner() external view returns (address);

In our implementation we allow the user to chose if using an immutable bound account or an upgradeable one that can evolve and in the future accept standard not created yet. In the second scenario, we get a conflict between the owner() function required by OwnableUpgradeable and the owner() function required by IERC6551Account, i.e., the owner of the smart contract and the owner of the account.
While I like the simplicity of using owner(), I think it would make our life easier if the name was different.
accountOwner() or tokenOwner() would be good alternatives. What do you think?

Author: sullof
Date: 2023-05-29T02:54:58.118Z

BTW, I know that in your reference implementation is the owner of the NFT that can upgrade the implementation. That is a good solution if the owner of an NFT is a developer and can upgrade his own implementation without damaging the storage. However, it adds so much friction for normal users and introduces many risks at many levels. By changing the function’s name, we skip all the conflicts without forcing a solution that can reveal itself as a problem in the future.

Author: MidnightLightning
Date: 2023-05-31T11:52:05.966Z

Nestability
Has thought been given on the ability to ‘chain together’ an ownership path? Namely, if Alice owns Token A, and Token A owns Token B, and Token B owns Token C, can Alice directly control the wallet holding Token C?

If Alice’s human-owned/-controlled address is Address Z, then with ERC6551, Token A is owned by Address Z.
Address Y exists as a stamped-out proxy by the ERC6551 Registry as owned by Token A, and Token B is owned by Address Y.
Similarly, Address X exists as a stamped-out proxy by the ERC6551 Registry as owned by Token B, and Token C is owned by Address X.

The issue arises that if Alice uses Address Z to try and take an action on Address X, Address X will reject it, as it would inspect who owns Token B (which is Address Y), and reject the mis-match.
To make this process nestable/chainable, the logic of the wallet contracts the ERC6551 Registry stamps out would need to have that logic. The EIP itself only enforces those wallet contracts have a specific interface (which includes an owner() function), which could do any additional logic. But I think having that logic in the provided example/reference implementation would be a large benefit, and let this EIP cover more use-cases (allow using ERC6551 instead of ERC6059, for example; the key difference that has been highlighted between those two standards).
The token-owned smart contract wallets already are required to be ERC165-compliant, so the owner() logic could easily inspect to see if the address it thinks is the owner of its key token is also a token-owned contract, it can simply return the result of the owner() function of that other contract.

Ownership Cycles
Using the knowledge that all other token-owned contracts are ERC165-compatible and therefore easily-identifiable as another token-owned contract means adding in a guard against creating trivial loops could be done by having the example token-owned contract implement ERC721-receivable (add an onERC721Received function) and using that hook to check the owner() chain to make sure its own address isn’t encountered. An onERC721Received hook wouldn’t catch all occurrences of cycles created, but seems like a good, lightweight way to catch a lot of accidental loop scenarios.

Author: stoicdev0
Date: 2023-05-31T12:30:57.133Z

Hey there. If you want the equipping relationship on chain you should definitely check ERC-6220: Composable NFTs utilizing Equippable Parts

Author: jay
Date: 2023-05-31T13:49:02.503Z

These are great comments, thank you @MidnightLightning!
There’s a good discussion on nested execution above - essentially a wallet can receive a “proof” of root ownership that consists of the account data for each token bound account above the one being used for execution. That chain of ownership can be verified by the account, allowing for direct execution against an account by a root owner.
This is true as long as all accounts in the ownership chain are deployed, but breaks if an account in the chain is not deployed (as you won’t be able to call in and look up the token information). Ownership Cycles also allow for interesting burn mechanics to be created, so I don’t think we’ll prevent them at the proposal level. Implementations are encouraged to implement mitigations against asset loss in this scenario.

Author: MidnightLightning
Date: 2023-05-31T14:31:19.132Z

Ah, I see that now, here, thanks.
The ERC721 standard defines several “extensions” within the EIP itself. For many of these proposed features, you’ve proposed applications implementing ERC6551 just figure out their own mitigations. Are you opposed to having ERC6551 have identified “extensions” that lay out best practices for these proposed additional, optional features beyond a base example implementation? Or is there any effort already creating a new EIP draft with these extensions on ERC6551?

Author: jay
Date: 2023-05-31T14:38:39.048Z

I think some extensions in the proposal are a great idea! Trying to keep the required interface fairly uncluttered, but I think going into detail on these points in the proposal and specifying some extension interfaces could be very helpful.

Author: sullof
Date: 2023-05-31T22:16:35.828Z

Did you see my comment above about the owner() function? Your opinion?

Author: jay
Date: 2023-05-31T22:33:02.766Z

Thanks for the reminder, I missed it. How does requiring the owner() method cause a conflict here? It should be fine fine to have two interfaces with an overlapping method, the logic of the method can be changed when the implementation does.

Author: sullof
Date: 2023-05-31T22:41:06.442Z

jay:

How does requiring the owner() method cause a conflict here?


Let’s say that someone writes a upgradeable ERC6551Account contract using OpenZeppelin UUPS proxy contract. Most likely, they will implement
function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner {}

using OwnableUpgradeable. But the onlyOwner modifier is calling owner() to get the owner of the smart contract. Instead, it will get the owner of the wallet. Depending on how it is implemented, it may break the owner() function in IERC6551Account, which would be a disaster.
The workaround is to not use Ownable and managing the owner of the smart contract in a non-standard way, which may open to other issues.
IMO, renaming the function is the only solution to avoid problems.

Author: jay
Date: 2023-05-31T23:03:51.632Z

If you’re using UUPS and upgrade to a new implementation with a different owner() implementation, the onlyOwner modifier will work as expected (since the old implementation of owner() that looks at token data is no longer part of the contract). This change would break compatibility with this proposal (as the account is no longer token bound), but shouldn’t cause any logic issues in the contract.

Author: hiddenintheworld
Date: 2023-06-01T04:12:52.579Z

You can check out my latest proposal ERC-8192, it could already do nested mapping and infinite dynamic mapping, we can have token A owning token B while token B owns token C, in which token A is owned by a wallet, token C(contract address, tokenId) → token B(contract address, tokenId) → token A(contract address, tokenId)-> wallet address. the ownerOf(tokenC) will be resolved to the correct wallet address that owns token A.

Author: 0xDigitalOil
Date: 2023-06-01T14:37:30.186Z

Curious as to why it was designed as an ERC instead of just a trustless immutable primitive service.

Author: sullof
Date: 2023-06-01T20:41:41.316Z

jay:

This change would break compatibility with this proposal (as the account is no longer token bound), but shouldn’t cause any logic issues in the contract.


I don’t think so. It breaks the compatibility with this proposal only if implementing Ownable we override IERC6551.owner() with Ownable.owner(), making the owner of the contract the owner of any account. That is exactly the problem I see here and why I suggest that the function that checks who is the owner of the account is different than owner().

Author: sullof
Date: 2023-06-01T20:48:38.601Z

Look at it from a different perspective.
I learned how to connect to the registry to create a token bound account to my Bored Ape. I choose a suggested implementation and create an account. Later I discover that my account is unable to receive ERC777, ERC1400, etc. and I look for how to upgrade the implementation. Since I am just a token user the probability that someone phishes me and let me upgrade to a malicious implementation becomes very high. If the contract instead is managed by a service that focuses on security, I can have the advantage of having an upgraded account without risks. But the second, right now, is made hard because of the function overlap.
Another case of fraud can be someone showing the source code of their ERC721Account where the final contract is extending a standard ERC721Account and implements Ownable. How many people would be able to see that even if the contract looks legit, it is a scam because it plays with the owner() function?
I see why you are resistant to this change, since you already are using this proposal in production at https://tokenbound.org/ but this is a big issue and if you do not solve it, sooner or later a new standard will be proposed to overcome it, as it always happened in the past.

Author: jay
Date: 2023-06-01T21:28:53.237Z

@sullof Ah I think I see what you mean. Are you saying that instead of the account implementation contract specifying the token holder as the owner, you’d like the account instance to be owned by a trusted central party that prevents the account from being upgraded to malicious implementations?
Enforcing that the owner of the account is the token holder does mean that implementations would not be able to inherit from the existing Ownable contract, which uses storage to determine the owner. This is by design, as 6551 accounts are supposed to be self-custodial, with the ownership rights belonging solely to the token holder. This means that implementations wishing to implement centralized access control as you’re mentioning should methods other than owner() to do so. One pattern (which we’ve used in our implementation) is to move centralized logic into a separate Ownable contract that the account implementation calls into to check upgrade permissions. This account instances are owned by the token, and centralized access controls are clearer to the user.
I’m not resistant to this change because of any tooling we’ve launched - the tooling is designed to support the proposal and will adapt with it, not the other way around. I’m resistant to this change because I think that token bound accounts should be solely owned by the NFT, not a centralized party. That is the essence of a token bound account. By using the owner() function to express that, existing ecosystem tooling that expects EIP-173 or EIP-5313 support can work out of the box with token bound accounts.
That said, open to modifying this if there is clear consensus that the owner() function causes more pain points than it provides benefits.

Author: sullof
Date: 2023-06-01T22:45:20.662Z

jay:

Are you saying that instead of the account implementation contract specifying the token holder as the owner, you’d like the account instance to be owned by a trusted central party that prevents the account from being upgraded to malicious implementations?


I’m arguing that the proposal should remain neutral on this matter. There are instances where smart contract control may be necessary. For instance, in my ERC6551 implementation within the Cruna protocol, the vault must have authority over the bound account, so it deploys a service NFT and subsequently create accounts tied to that NFT. Later, users can extract the safe-box from the vault by transferring the service NFT to themselves, with the option to re-inject it later. This flexibility is crucial to facilitate migrations between two versions of the vault.
That said, we’ve received inquiries about the possibility of making the bound account upgradeable without potential risks. In response, although our entire contract suite is intentionally non-upgradeable to maintain trustless design, we’ve decided to let users choose between an immutable account and an upgradeable one. In our upgradeable implementation (https://github.com/cruna-cc/cruna-protocol/blob/main/contracts/bound-account/ERC6551AccountUpgradeable.sol#L38), we avoid using Ownable due to the aforementioned conflict, and instead establish a _deployer variable in the constructor. As such, I’ve personally resolved the issue.
However, I recognize the potential for misuse, especially concerning the function overlap. There’s a real risk of individuals exploiting this to defraud others, so I believe this matter warrants resolution.

Author: sullof
Date: 2023-06-01T22:52:05.046Z

jay:

One pattern (which we’ve used in our implementation) is to move centralized logic into a separate Ownable contract that the account implementation calls into to check upgrade permissions.


That implies that you saw the issue and solved in your own case. I think that renaming that function would solve all the issues and make dev’s life easier and user’s life safer.

Author: sullof
Date: 2023-06-01T23:04:14.705Z

jay:

EIP-173 or EIP-5313


Sorry, for so many comments. But EIP173 and 5313 are both defining owner() for contract ownership,
/// @title ERC-173 Contract Ownership Standard
///  Note: the ERC-165 identifier for this interface is 0x7f5828d0
interface ERC173 /* is ERC165 */ {
    /// @dev This emits when ownership of a contract changes.    
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /// @notice Get the address of the owner    
    /// @return The address of the owner.
    function owner() view external returns(address);
	
    /// @notice Set the address of the new owner of the contract
    /// @dev Set _newOwner to address(0) to renounce any ownership.
    /// @param _newOwner The address of the new owner of the contract    
    function transferOwnership(address _newOwner) external;	
}

and
/// @title EIP-5313 Light Contract Ownership Standard
interface EIP5313 {
    /// @notice Get the address of the owner    
    /// @return The address of the owner
    function owner() view external returns(address);
}

while in 6551 you use owner() for account ownership:
    /// @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
    function owner() external view returns (address);

Author: jay
Date: 2023-06-01T23:39:37.972Z

sullof:

I’m arguing that the proposal should remain neutral on this matter.


Got it, thanks for clarifying!



 sullof:

However, I recognize the potential for misuse, especially concerning the function overlap.


I think you make a good point here - malicious (or unaware) developers could easily create implementations where the owner() function is provided by inheriting from Ownable, causing the implementation to be incompatible with this proposal at best and insecure at worst.



 sullof:

That implies that you saw the issue and solved in your own case.


I actually don’t see this as an issue - using owner() was a deliberate design decision, as was our approach of using a separate ownable contract in our implementation. I think that in principle the owner() method is the right way to expose ownership of the account to external callers - token bound accounts should be “owned” by the holder of an NFT in the same way that traditional accounts are owned by a certain wallet address (as this allows the account contracts to be self-sovereign).
I do think that the behavior of the owner() method should be clarified within the proposal to highlight the difference between token bound accounts and traditional ownable contracts. The owner() method also seems to cause issues beyond prohibiting use of Ownable, especially when it comes to tokens that do not have a canonical owner such as ERC-1155.
One way around this which I think holds promise is removing the owner() method from the required account interface and instead suggesting that accounts support ERC-5313, with some specific suggestions around how to determine the owner if an account implementation chooses to do so. Do you think this would mitigate your concerns here?

Author: sullof
Date: 2023-06-01T23:52:41.588Z

jay:

One way around this which I think holds promise is removing the owner() method from the required account interface and instead suggesting that accounts support ERC-5313, with some specific suggestions around how to determine the owner if an account implementation chooses to do so. Do you think this would mitigate your concerns here?


I am afraid that that would end up with dev using owner() to return the smart contract’s ownership, and implementing some non-standard, arbitrary way to return the owner of the account. The only scenario where that would work seamlessly is when the owner of the smart contract is the owner of the tokenId, which is unlikely.
I like the interface how it is now and believe that there is a need for a standard function to be called to know who is the owner of the account. But, to avoid conflicts and misuses, I suggest that you rename it.
accountOwner() looks like the most obvious name to me, but whatever you like is fine for me 
BTW, adding a specification for adherence to EIP-173 or EIP-5313 could be an extra. I think that for any user it would be good to know who owns the smart contract and can possibly modify it.

Author: urataps
Date: 2023-06-02T13:53:36.692Z

I like this proposal, it gives wallets a more clear sense of scarcity (owned assets) and authenticity (wallet-owner = NFT owner), and it enables various interesting utilities and mechanics to be introduced. However, I would like to point out the following things which seemed unclear to me:

What is the motivation behind the name “Registry”? For me it doesn’t fit in since nothing is actually registered there, it only deploys proxies with a given account implementation. I think the word “Factory” would be more suitable as it clearly outlines the creational design pattern that takes place. In my world, “Registry” would be used for some single-point-of-entry contract that tracks different account implementations registered by somebody.
I have concerns regarding the owner()method, besides the ones that @sullof mentioned. How should the account retrieve its current NFT owner if the token is on another chain? Perhaps we should not enforce this method at all, since the owner is also retrievable off-chain in this two-step process from the information returned by token(), i.e. get the tokenId and ask the owner from the tokenContract. In this way, the standard would get rid of the compatibility issues such as Cryptopunks whose contract do not have ownerOf(tokenId) method, at the expense of moving the ownership computation on client-side, which is not that bad. On the account side, we should worry about retrieving the NFT owner only if the implementation attaches some behavior it, such as transfer or upgrades.
Since there are a lot of use-cases for upgradeable or modular accounts requiring other type of proxies (UUPS, Beacon, Diamond), we would need to make a two-step delegate call ERC-1167  → OtherProxy → Implementation which introduces a call overhead and security risks. I believe we can introduce more flexibility if we require only the last three parameters ( chainId, tokenContract, tokenId) to be embedded at the end of the bytecode, since they are the only ones that are accessed afterwards in token(). I don’t see why salt has to be in the account bytecode, and I would leave specific details about the implementation, it could be also placed in a dedicated storage slot as in ERC-1967.

Overall, I believe addressing these concerns will further improve the proposal’s clarity, functionality, and compatibility with existing standards and contracts. I look forward to minting an NFT for my wallet =)

Author: sullof
Date: 2023-06-02T19:20:53.548Z

While I am not the author of this proposal and so I defer to them for an official response, I can share my thoughts on the points raised.



 urataps:

 I think the word “Factory” would be more suitable as it clearly outlines the creational design pattern that takes place. 


I concur with this sentiment. The proponent appears to conceive of the registry as a singular entry point, but in reality, it’s likely that multiple registries will be created for various purposes. Thus, referring to it as a Factory seems more fitting. To me, a Registry evokes an official index of audited or trustworthy factories.



 urataps:

 In this way, the standard would get rid of the compatibility issues such as Cryptopunks whose contract do not have ownerOf(tokenId) method, at the expense of moving the ownership computation on client-side, which is not that bad. 


I agree that omitting specific requirements on how the owner() function should be implemented may be advantageous, particularly as an account could support pre-ERC721 tokens. Nevertheless, I believe that a smart contract wallet should be designed to operate without necessitating a client-side component to verify whether a transaction originates from the token owner. Removing detailed specifications on the owner() function could pave the way for alternative approaches, such as those based on ZK proofs.



 urataps:

 Since there are a lot of use-cases for upgradeable or modular accounts requiring other types of proxies (UUPS, Beacon, Diamond), we would need to make a two-step delegate call ERC-1167 → OtherProxy → Implementation which introduces a call overhead and security risks. 


I am in agreement that the proposal may offer more flexibility in this regard, enabling the implementer to directly deploy the proxy of their choice, rather than mandating a specific type of proxy. The end result may be more costly, but in my view, this should not be a primary concern of the proposal.

Author: 0xDigitalOil
Date: 2023-06-03T08:46:43.029Z

I assume there will be a special case for Punks.
For NFTR we do a special check for Punks as so:
image1168×720 69.1 KB
Is there a plan for this in 6551?

Author: jay
Date: 2023-06-03T18:57:28.673Z

0xDigitalOil:

Curious as to why it was designed as an ERC instead of just a trustless immutable primitive service.


@0xDigitalOil great question! We think that the concept of every NFT having deterministic accounts is most powerful when it can become a standard pattern across the ecosystem. It’s also very beneficial to have a single entry point for all token bound accounts, as this maximizes the benefits of deterministic addressing. In our opinion the EIP process is the best way to accomplish this, as it allows everyone to contribute to the refinement of this pattern and enables broad support for it throughout the ecosystem once consensus has been achieved.



 sullof:

I like the interface how it is now and believe that there is a need for a standard function to be called to know who is the owner of the account. But, to avoid conflicts and misuses, I suggest that you rename it.


@sullof I think you make a good point here, will give some thought to potential alternative names. I think there’s an opportunity to solve several of the existing issues with the owner() function in one change here.



 urataps:

What is the motivation behind the name “Registry”?


@urataps The term “Registry” was chosen over “Factory” for two primary reasons. First, it frames it as a central entry point rather than one entry point among many. One goal of this proposal is to define a single, immutable, non-owned entry point that can be used for all token bound accounts. Having a single entry point makes it much simpler for this pattern to be implemented by applications and allows for the benefits of deterministic addressing to be most easily realized. Second, it focuses on the action of querying for an account address over the action of creating an account. This makes it clear that accounts for every NFT can be queried using the Registry.



 urataps:

How should the account retrieve its current NFT owner if the token is on another chain?


This is a good point - ownership data cannot (currently) be retrieved within the owner() function across chains. This limits the usefulness of this function in the scenario of a token bound account being deployed on a different chain than the token that controls it.



 urataps:

On the account side, we should worry about retrieving the NFT owner only if the implementation attaches some behavior it, such as transfer or upgrades.


The purpose of owner is primarily to allow external applications to query the owner of an account without having to introspect the code of the account. This is primarily useful when dealing with certain NFTs that don’t follow the ERC-721 spec (such as CryptoPunks or ERC-1155 tokens with canonical owners). It allows the implementation to determine ownership of the account, without the implementation details being leaked to the client. I think this is an important characteristic, but the current approach has some clear tradeoffs that warrant deeper consideration of how this is implemented.



 urataps:

Since there are a lot of use-cases for upgradeable or modular accounts requiring other type of proxies (UUPS, Beacon, Diamond), we would need to make a two-step delegate call ERC-1167  → OtherProxy → Implementation which introduces a call overhead and security risks.


You are correct regarding the additional gas overhead, but as far as I’m aware there are no additional security risks to having double-layer proxies as you describe since the implementation address of the account is constant. We use this pattern in our own implementation to enable upgradability (an ERC-1967 proxy behind an ERC-1167 proxy).
While nesting proxies does introduce additional gas costs, upgradable proxies will always cost more gas than immutable proxies since they read the implementation address from storage. Because upgradable proxies already come with additional gas overhead, the extra gas caused by a double-proxy is reasonable in my opinion. More flexibility will always come at the cost of higher gas fees. By defaulting to ERC-1167 proxies, the extra fees that come with a more complex proxy are opt-in rather than required for all users.
We’ve explored the use of native ERC-1967 proxies deployed by the registry previously in this thread and within the proposal. However, storage-based proxies come with a significant downside: they make it impossible for any stateless implementations to exist. This means that developers wishing to build stateless account implementations are not able to do so, and must accept the security risks that come with storage use despite their desire to avoid them. The reverse is not true - if proxies are deployed as ERC-1167 proxies, both stateless and stateful account implementations can be created.
Ultimately, ERC-1167 is the simplest form of proxy that is widely used and has broad ecosystem support. By opting to use the simplest possible proxy, this proposal gives maximum flexibility to developers wishing to build on top of it.



 sullof:

The proponent appears to conceive of the registry as a singular entry point, but in reality, it’s likely that multiple registries will be created for various purposes.


@sullof you are correct - the registry is designed to be a single entry point. The proposal specifically allows for the deployment of alternative registries (by specifying that accounts “should” be deployed via the registry rather than “must”), but highly encourages the use of the registry defined in the proposal to enable broad adoption of this pattern in the ecosystem with minimal integration effort. Applications that support this proposal must support the canonical Registry, and may support other registries if they choose to.



 sullof:

Nevertheless, I believe that a smart contract wallet should be designed to operate without necessitating a client-side component to verify whether a transaction originates from the token owner. Removing detailed specifications on the owner() function could pave the way for alternative approaches, such as those based on ZK proofs.


I agree with this, and would love to encourage the development of alternative authentication mechanisms for token bound accounts.



 sullof:

I am in agreement that the proposal may offer more flexibility in this regard, enabling the implementer to directly deploy the proxy of their choice, rather than mandating a specific type of proxy. The end result may be more costly, but in my view, this should not be a primary concern of the proposal.


The primary reason for enforcing a single proxy type at the registry level is not to minimize gas costs, it is to maximize the benefits of deterministic addressing while enabling the most developer flexibility. By deploying ERC-1167 proxies from the registry, it ensures that the bytecode of all token bound accounts is well known and easily computable by other smart contracts.
Allowing the registry to deploy arbitrary bytecode would mean that smart contracts wishing to locally compute the address of an account would need to load the entire bytecode of the implementation contract into memory. It would also make it more difficult for clients to query the registry for the account corresponding to a certain token, as they would also need to be aware of the account bytecode.
Since ERC-1167 is very simple and can serve as the proxy for smart contracts with any logic, this still allows developers full flexibility to implement account logic as they see fit (including using alternative proxy models). I’ve yet to see a use case that cannot be supported by the current Registry/proxy model, if you know of any I would love to explore them and adapt the model to enable them.



 0xDigitalOil:

I assume there will be a special case for Punks.
Is there a plan for this in 6551?


Yes, implementations wishing to support Punks and other NFTs that are not compatible with ERC-721 are welcome to do so.

Author: sullof
Date: 2023-06-03T19:45:36.329Z

Thanks for you long response.

Author: jay
Date: 2023-06-03T20:29:01.336Z

urataps:

I don’t see why salt has to be in the account bytecode


It’s important to include the salt value in order to allow the account contract (and any contracts that that introspect the account bytecode) to locally verify that the appended data is valid by computing the valid account address for the appended data and comparing it with the actual account address. While not currently used in the example implementation, having local access to the salt value used to create the account will be important for contracts that wish to use token bound account addresses as an authentication mechanism.

Author: sullof
Date: 2023-06-03T21:19:00.346Z

I agree that is important. It also allows a tokenId to owns more than one smart wallet, despite using the same implementation.
PS > I just realized that I left more comments here that on the couple of ERCs I am proposing. I must really like this one

Author: 0xDigitalOil
Date: 2023-06-06T10:59:35.099Z

Regarding the Punks question… ok, that makes sense!

Author: hiddenintheworld
Date: 2023-06-06T14:13:21.626Z

So it is a N-to-1 relationship

Author: jay
Date: 2023-06-07T19:04:57.982Z

sullof:

It also allows a tokenId to owns more than one smart wallet, despite using the same implementation.


Yep, thanks for mentioning this!



 sullof:

I just realized that I left more comments here that on the couple of ERCs I am proposing. I must really like this one 


Amazing! Thanks so much for all of your comments 



 hiddenintheworld:

So it is a N-to-1 relationship


Yes - every NFT can have an unlimited number of token bound accounts.

Author: enu-kuro
Date: 2023-06-11T10:04:58.074Z

I’m genuinely impressed by the concept of Token Bound Accounts, and I’m truly excited about developing something using this concept!
I’m considering a mechanism for NFT rental using Token Bound Accounts (TBAs). The advantage of TBAs is that developers can freely implement the account as they please. For instance, one can create an account that can validate possession via the isValidSignature method, but not transfer assets.
By adding a mechanism that allows asset transfers to a specific account after a certain period, we can create a structure for NFT rental. Users who wish to rent out their NFTs can transfer them to such a TBA and sell them on marketplaces like OpenSea to facilitate the rental process. The buyer, although unable to transfer the asset on the TBA, can validate possession using ERC-1271 signatures.
Once the predetermined period ends, the original NFT owner can move the NFT on the TBA back to their account, marking the end of the rental period.
By using this method, there’s no need to create wrapped NFTs, and we can fully leverage the existing ecosystem as is.
This freedom to implement accounts opens up numerous possibilities for utilizing TBAs. However, a significant challenge is that users cannot easily verify the implementation of a TBA. If the relationship between an NFT and a TBA is one-to-one, a one-time verification would suffice. But if it’s a one-to-many relationship, it’s not realistic for users to verify the implementation of each TBA.
Are there any possible solutions to these challenges?

Author: jay
Date: 2023-06-12T15:47:08.106Z

enu-kuro:

However, a significant challenge is that users cannot easily verify the implementation of a TBA.


Since token bound accounts are deployed as ERC-1167 proxies, users can easily verify the implementation for any token bound account. The implementation contract can be verified once, and the account instance contract can be verified by introspecting the proxy to determine the implementation address. On Etherscan, this can be done by clicking “is this a proxy?” on the token bound account contract.
Additionally, because token bound account addresses are deterministic, you can verify that an account is using a trusted implementation by computing the address for the account with that implementation and checking that it matches the address being used. This can be done prior to the deployment of the account contract.

Author: enu-kuro
Date: 2023-06-13T01:55:52.248Z

I mean that it’s unrealistic for average users to use Etherscan to verify each contract. But probably this is a UX issue, rather than TBA itself. I’ll consider how to improve the UX.

Author: sullof
Date: 2023-06-13T19:43:39.770Z

jay:

I think you make a good point here, will give some thought to potential alternative names. I think there’s an opportunity to solve several of the existing issues with the owner() function in one change here.


I wonder if there is any news about the new proposed name.

Author: jay
Date: 2023-06-16T16:53:55.306Z

@sullof For sure! Here are a few of the options we’ve been considering if the owner() function is to be re-named:

executor()
operator()
signer()
controller()
custodian()
steward()
manager()
admin()

The goal of these names is to reduce the emphasis on the holder of the token as the canonical owner of the account, since the token itself is the owner. The holder simply has temporary execution permissions.
However, as discussed earlier in this thread, the concept of a canonical controller makes it difficult for this pattern to be applied to tokens that lack a canonical owner. Additionally, there is currently no way to determine whether a given address has execution permissions on an account without assuming that the executor must be the owner. This assumption will likely be broken in practice quite quickly. As a result, it might make sense to replace owner() with a new method that can be queried to determine whether a given address has permissions on the account.
Here are a few options that are being considered:


is...(address) returns (bool) for any of the above names (e.g. isExecutor).
isValidSigner(address) returns (bool)
isAuthorized(address) returns (bool)
controlledBy(address) returns (bool)
managedBy(address) returns (bool)

Another option is to add a function that can be queried to determine a caller’s ability to execute a certain function on the account. This could look like:

isAuthorized(bytes4 sig, address caller) returns (bool)
canExecute(bytes4, address) returns (bool)
hasPermission(bytes4, address) returns (bool)

This could be even further extended to accept the full calldata bytecode as a parameter instead of just the method signature. Something like:

canExecute(bytes calldata data, address caller) returns (bool)

Additionally, each of the above functions which return a boolean could return a magic value instead, allowing for further extension of functionality at the account implementation level.
I don’t want to stray too far into mandating specific access control patterns here (e.g. role-based permissions) as I think those are out of scope for the proposal. However, access control is a central part of this proposal and I think some minimal pattern should included in the account interface.
Part of the challenge here is determining a term that can be used in place of owner to properly captures the distinction between the token (which “owns” the account) and the token holder (which “uses” the account).
I’m leaning towards the term signer, as I think it fits well with existing smart contract account terminology (e.g. “signer on a gnosis safe”). It’s also similar to the verbiage used in EIP-1271.
I would propose that the owner function be replaced by the function:

isValidSigner(address) returns (bytes4)

The proposal would specify that the token holder should be regarded as the default signer and that all valid signers should also have execution rights.
Would love any and all feedback on these potential interfaces

Author: sullof
Date: 2023-06-16T20:50:13.223Z

Thanks so much for the update.



 jay:

I’m leaning towards the term signer, as I think it fits well with existing smart contract account terminology (e.g. “signer on a gnosis safe”). It’s also similar to the verbiage used in EIP-1271.


I agree.
signer makes a lot of sense in the context of the account.
To keep the same approach of owner(), maybe you can go with signer() instead of the more verbose isValidSigner(). I am not sure if signer() creates any conflicts with existing EIPs, but I don’t recall any general EIP where that was used.

Author: urataps
Date: 2023-06-20T08:33:40.755Z

jay:

It’s important to include the salt value in order to allow the account contract (and any contracts that that introspect the account bytecode) to locally verify that the appended data is valid by computing the valid account address for the appended data and comparing it with the actual account address.


I understand the connection salt has with an account’s address and the idea of leveraging this for authentication is really cool, but then wouldn’t it make more sense to keep this private as some kind of key?
Also, since you mentioned that some smart contracts may use salt as authentication, not all of them, then this should not be a requirement (i.e. SHALL) but rather an option (i.e. MAY) for the implementer of the proposal. But we have to acknowledge that including or excluding the salt (or any data) from the bytecode changes the address computation using CREATE2.
So, I’m wondering, does the standard allow for some flexibility regarding the appended data and what can be introspected from the bytecode? I predict the answer is no, so let’s try to answer this follow-up question: Can we introspect arbitrary data in some other way?
I’m asking this because the account bytecode doesn’t actually know that implementation contains a smart contract account, as far as the ERC-1167 proxy is concerned, it is an arbitrary address. Therefore, I’m exploring the possibility of other token-bound approaches, or even a more universal NFT-owned smart contracts proposal, where the ability to introspect arbitrary data from the proxy bytecode is crucial, as well as its compatibility with TBAs.

Author: jay
Date: 2023-06-20T15:43:09.239Z

urataps:

I understand the connection salt has with an account’s address and the idea of leveraging this for authentication is really cool, but then wouldn’t it make more sense to keep this private as some kind of key?


While you can use the value of salt as input data to the authentication scheme of the account (e.g. for storing the threshold balance of ERC-1155 tokens required to use the account), the primary behavior of salt is to allow multiple account instances for the same account implementation per token. It doesn’t make sense to keep this data private, as it’s not a key. Storing the salt data allows for the account implementation to access it’s value (which enables it to be used within the authentication logic) and allows the account to re-compute its own address locally (which is useful for proving account authenticity when calling into other contracts). In short, salt isn’t designed to store private data.



 urataps:

Also, since you mentioned that some smart contracts may use salt as authentication, not all of them, then this should not be a requirement (i.e. SHALL) but rather an option (i.e. MAY) for the implementer of the proposal


salt is a required input value for all token bound accounts as it affects the account address computation. Users that don’t wish to customize the salt can default to a salt value of zero. Because salt is not intended to be used for authentication and is required for all accounts, I don’t think it makes sense to change the language here.



 urataps:

So, I’m wondering, does the standard allow for some flexibility regarding the appended data and what can be introspected from the bytecode?


Currently the appended data is determined by the registry and cannot be customized. This is to ensure that the bytecode for any token bound account is easily computable without relying on external data sources.



 urataps:

Can we introspect arbitrary data in some other way?


Using a custom implementation allows you to store any data you wish to using smart contract storage. This allows for easy querying of data. However, smart contract storage behind a proxy cannot be constant or immutable.



 urataps:

I’m asking this because the account bytecode doesn’t actually know that implementation contains a smart contract account, as far as the ERC-1167 proxy is concerned, it is an arbitrary address.


Correct, although querying whether a given implementation is a smart contract is trivial by extracting the implementation address from the account bytecode and querying it’s code length.



 urataps:

Therefore, I’m exploring the possibility of other token-bound approaches, or even a more universal NFT-owned smart contracts proposal, where the ability to introspect arbitrary data from the proxy bytecode is crucial, as well as its compatibility with TBAs.


I’d love to hear more about your use case here! What arbitrary data are you hoping to append to the account bytecode? I’d be open to including an optional extraData parameter in the account creation function that allows for additional arbitrary data to be embedded in the account contract if there is sufficient interest. The tradeoff would be making deterministic calculation of token bound account addresses more difficult for applications integrating with ERC-6551.

Author: urataps
Date: 2023-06-20T20:24:06.353Z

jay:

I’d love to hear more about your use case here! What arbitrary data are you hoping to append to the account bytecode?


Let’s take a token bound ERC20 for example, where the NFT gives access to owner methods (e.g. configuration, minting, etc.). In this case the implementation contract would need to introspect name and symbol that would be passed to the registry at the moment of deployment.
Let’s imagine an auction contract where the fee per transaction is set at deployment and cannot be changed, whoever owns the bound NFT will receive the revenue from fees. In this case, the implementation contract should be able to introspect the fee from the proxy bytecode.
Essentially, I believe we should be able to introspect any type of immutable data configurable at deployment, which in a traditional scenario would represent Solidity immutable variables.
Btw, by “introspect” I mean performing Bytecode.codeAt(address(this), start, end) as in the reference implementation, where address(this) would return the EIP-1167 proxy address in a delegatecall, hence we get the proxy bytecode.
That being said, I still believe salt is not a requirement (but an option) for the proxy bytecode, it could be part of extraData in the context of this proposal. On the other hand, having chainId, tokenId, tokenContract is a must.

Author: jay
Date: 2023-06-20T21:45:18.959Z

urataps:

Let’s take a token bound ERC20 for example, where the NFT gives access to owner methods (e.g. configuration, minting, etc.).


I see. This use case is pretty far outside the scope of this proposal, but should be easy to accomplish without any proposal changes by separating the account and ERC20 contracts. You can create an ERC20 contract and set an existing token bound account address as the owner. That way the ownership of the ERC20 contract is permanently bound to the NFT’s account, but neither the token bound account or the ERC20 contract need to be aware of each other. This also removes the need to append additional data to the token bound account bytecode within the proposal.



 urataps:

Let’s imagine an auction contract where the fee per transaction is set at deployment and cannot be changed, whoever owns the bound NFT will receive the revenue from fees.


This could also be easily accomplished with an external auction contract that uses immutable storage. There is no need to include ERC20 or auction logic inside of a token bound account.



 urataps:

That being said, I still believe salt is not a requirement (but an option) for the proxy bytecode


salt is required as a parameter in order for the registry to have the ability to create multiple accounts per instance/NFT combination.

Author: urataps
Date: 2023-06-21T06:27:57.359Z

jay:

salt is required as a parameter in order for the registry to have the ability to create multiple accounts per instance/NFT combination.


I see, then that is a reasonable cause to include it in the bytecode.



 jay:

You can create an ERC20 contract and set an existing token bound account address as the owner. That way the ownership of the ERC20 contract is permanently bound to the NFT’s account, but neither the token bound account or the ERC20 contract need to be aware of each other.


You’re right, I failed to see the bigger picture here. Essentially, we can introduce token bound functionality to any smart contract by making certain interactions to be performed via a token bound account.
Still, I wonder if any other folks can find use-cases where the extraData would make sense.

Author: MrCurious
Date: 2023-06-30T00:04:14.793Z

Hi @Jay,
Thankyou for this wonderful new ERC and all your replies.
I’m considering implementing this ERC in a project I’m starting, but I’m worried that the interface could change. Is there some expected finalization date? Forgive my lack of experience with the process. Thankyou.

Author: vcavallo
Date: 2023-07-02T02:39:52.298Z

I came here intending to write this same comment.
A project I’m working on could really benefit from TBAs and I’m eager to use 6551, but I’m not familiar enough with the ERC process to know where this proposal stands.
What’s it waiting for? Is it “safe” to implement it today?

Author: MidnightLightning
Date: 2023-07-02T03:21:47.114Z

vcavallo:

but I’m not familiar enough with the ERC process to know where this proposal stands.


The current version of this EIP is in “Draft” status. As the tooltip on the “draft” status on that page indicates, EIPs in draft form might still have significant changes made before finalizing, so it’s not recommended to rely on its current form just yet. The EIP process itself is laid out in EIP-1, which indicates the next step for this EIP is the original author flagging it as ready for peer review, which moves it to the “Review” stage.

Author: SpiderAres
Date: 2023-07-02T03:23:51.764Z

NFT "leader-follower" Protocol EIPs


Abstract

This proposal can be regarded as an UPGRADED VERSION of the ERC-721 protocol, which aims to realize the convenient and continuous infinite synthesis and iteration of multi-chain NFT assets. This NFT protocol will empower cross-chain NFT assets to combine arbitrarily, combine transactions, and publish on multiple chains. 

Motivation

Now, an important factor that restricts the diversification of the gamefi track and NFT marketplace is the “ecological fragmentation”. It is true that th…
  

My options!

Author: MidnightLightning
Date: 2023-07-02T13:55:52.311Z

SpiderAres:

My options!


Just posting a link to a competing EIP idea isn’t all that helpful in furthering discussion. If you think the proposal in this thread can be improved in specific ways, can you call out what those specifically are? The EIP idea you’re promoting is for very different use-cases (cross-chain interactions) and therefore I don’t really see how it adds “options” to this proposal?

Author: sullof
Date: 2023-07-02T18:53:04.391Z

There is a discussion going on about the name of the function to get the owner of the bound account. That must be solved before move from Draft to another state. That, of course, is a problem for everyone is implementing this EIP.
@jay I am assuming that you are going with isValidSigner. Any news about it? I haven’t seen any PR on the repo.

Author: SpiderAres
Date: 2023-07-10T03:24:01.486Z

I am mainly concerned about usage scenarios. For example, whether this standard can collect assets in multiple chains. As I said in the article, I have an nft on eth. Can I use this to hold other nfts on polygan?  ?  And can this nft be held by multiple parties and jointly promoted?

Author: Mani-T
Date: 2023-07-10T08:37:49.387Z

The goal of ERC-6551 is to give each ERC-721 token a token-bound account with full Ether account functionality, which makes NFTs no longer just static assets, but with more utility and functionality, opening up a world of new possibilities. The realization of this concept may lead to a variety of new applications and innovations that will allow NFT to play an even greater role in the digital asset and blockchain space.

Author: MidnightLightning
Date: 2023-07-10T12:36:40.765Z

SpiderAres:

I have an NFT on [Mainnet]. Can I use this to hold other NFTs on [Polygon]?
And can this NFT be held by multiple parties and jointly promoted?


ERC-6551 does not cover those two scenarios. However it does serve as a base logic layer that both those ideas could be built on top of as separate EIPs/applications.

Author: sullof
Date: 2023-07-10T21:31:29.536Z

SpiderAres:

Can I use this to hold other nfts on polygan? ? And can this nft be held by multiple parties and jointly promoted?


ERC6551 specifies a way to associate smart-contract wallets to any NFT. That works only on the chain where the account is deployed. To manage assets on different chains you should use a bridge like Wormhole, but that is out of the scope of this proposal. If you like to explore that, you can consider using GitHub - ndujaLabs/wormhole-tunnel
About the second question, you can do that letting a multi-signature wallet hold the NFT that holds the bound-account.

Author: fulldecent
Date: 2023-07-18T20:39:25.303Z

The ERC-6551 DRAFT looks really complicated and does not clearly demonstrate why such a complicated use case is required.
For just one example: the first use case is trustless counterfactual sending of NFTs. But then later it talks about upgradable contracts, which is the opposite of trustless.
The many levels of complexity that are added by this contract are not sufficiently motivated by motivation section text in the draft.

Author: jay
Date: 2023-07-18T22:07:51.747Z

@fulldecent appreciate the comments! Can you expand on your complexity concerns?
This proposal specifies a canonical registry from which to deploy smart contract accounts owned by NFTs. Each account is an ERC-1167 proxy to a contract which implements an interface defined in the proposal. Given the stated motivation of giving every NFT an account, how would you recommend simplifying the proposal to achieve this goal?
The proposal does not mention or rely on upgradable contracts (although there has been some discussion about them in this thread). Can you provide some more details about your concerns here?

Author: tmoindustries
Date: 2023-07-23T00:07:31.414Z

Has the team considered, looked at or spoke to the Charged Particles team?  CP has been doing this for a couple years and has lots of trial and error and I think there is a lot to be learned from them as well as possible collab.

Author: 0xDigitalOil
Date: 2023-07-24T19:34:59.791Z

jay:

I agree that desired protections against this type of attack should be enforced at the account implementation layer. However, I’m hesitant to recommend owner-only deployments as the default configuration for a wallet implementation, since there are many potential positive use cases for permissionless deployments that could be inhibited.


Can you give some examples of what these applications are?

Author: 0xDigitalOil
Date: 2023-07-25T20:34:36.054Z

Is this extract from the ERC spec not contradictory?
    /// 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.

If the ERC specifies that TBAs must allow execution of arbitrary calls by NFT owner but may limit these calls, then arbitrary calls are not allowed, no?
In addition…
Suggestion: change of verbiage.
Having the TBA return as owner the account that owns the NFT goes against the spirit of “the NFT owns the TBA”.
To mitigate this, I suggest changing the function name to tokenOwner.
    /// @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
    function owner() external view returns (address);

In addition, is this factually correct?
The specification proposed above allows ERC-721 tokens to have multiple token bound accounts, one per implementation address.

If you change the salt, given the same implementation you get a different TBA, no?
Lastly, regarding this mentioned asset rug mitigation:
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.

What is there to stop a third party approved operator from moving the assets thus avoiding increment of the nonce?

Author: sullof
Date: 2023-07-27T01:34:58.054Z

0xDigitalOil:

To mitigate this, I suggest changing the function name to tokenOwner.




I made the same proposal a while ago and you can read a long discussion starting from ERC-6551: Non-fungible Token Bound Accounts - #125 by sullof
A lot of comments after you can see @jay saying that they are working on renaming the function.
However, it would be very helpful if the name of function is defined as soon as possible.

Author: MJ_Tseng
Date: 2023-07-31T19:37:36.992Z

We were pleasantly surprised to see A3S being mentioned in the discussion. We’ve been diligently working on A3S Protocol since 2022 and our product V1.0 has successfully achieved the NFT-gated contract address feature. This empowers the bulk transfer of on-chain assets and off-chain interaction records, enhancing the liquidity of addresses.
We’re thrilled that ERC6551 has become a trending topic. It has always been our mission to make the concepts and innovations of account abstraction more widely known and understood by the community.
Regarding @jay’s analysis, we find some of the points agreeable and would like to share our perspective as well:
Firstly, the functionalities of A3S and ERC6551 align closely, both are trying to let an NFT have ability to control assets, the slightly differences is that ERC6551 focuses on NFT’s ID can own accounts/wallets while A3S focuses on NFT itself being accounts/wallets. However, technically speaking, we can achieve same outcomes with minor code modifications.
Secondly, concerning “centrally owned and upgradable”, our initial version of product is actually a monetised application, so evidently we need it to be upgradeable to meet the demands of the market and users, it’s a common practice for projects rapidly evolving and iterating like us. We will renounce ownership and achieve full decentralisation at any time when we achieve our desired goals.
We have taken the first step by integrating crypto assets into NFTs and our long-term goal is to improve user-friendly interactions across diverse scenarios in crypto world. Hope we could engage in meaningful discussions with your team and join forces on EIP promotion and adoption.

Author: jay
Date: 2023-08-01T02:12:17.527Z

Thank you to everyone who has commented on this proposal so far! We have submitted an update to the proposal which aims to address many points of feedback received so far. Here is a summary of the proposal changes:


The owner() method has been removed from the required account interface
While this method was originally included to ensure compatibility applications that utilize EIP-173, several compelling reasons to remove it have been voiced in this thread:

Requiring account implementations to return a single owner address makes it difficult for this proposal to support semi-fungible tokens
The return value of owner() is redundant, as ownership of a token can be queried based on the return value of token()
The owner() method may cause unwanted bugs since it overlaps with often used Ownable contract implementations
Cross-chain account implementations cannot easily query for a token owner if the token exists on another chain

Many thanks to @dongshu2013, @RobAnon, @0xTraub, @urataps, and @sullof among others for their dialogue on this point.


An isValidSigner(address signer, bytes context) method has been added to the account interface
This method allows external callers to determine whether a given account is authorized to act on behalf of a token bound account, replacing the owner() method as the primary means of querying account authorization. This method gives account implementations greater control of their authorization models, and is better suited to supporting semi-fungible token bound accounts.


The nonce() method has been re-named to state()
This change better reflects the goal of the function (allowing external contracts to introspect account state) and disambiguates it from the common understanding of an account nonce (an incrementing value used for replay protection). It also ensures that this function does not overlap with existing account implementations, enabling account implementations to more easily adopt this proposal.


The executeCall(address to, uint256 value, bytes data) method has been removed from the account interface in favor of a variable execution interface to be exposed by account implementations
Allowing accounts to define their own execution interface allows this proposal to be compatible with both existing execution interfaces (e.g. Safe, EIP-725x, Kernel, etc) and upcoming execution interface standards (e.g. EIP-6900) without requiring account implementations to support multiple execution paths.


The recommended execution interface now supports delegatecall, create, and create2 operations
This change allows account implementations to support any type of state change operation and ensures forward compatibility with future operation types. Thank you @wighawag for recommending this change.


The AccountCreated events emitted by the Registry are now indexed by implementation, tokenContract, and tokenId
This change enables easier indexing and querying of previously created token bound accounts. Thank you @MidnightLightning for the thoughtful reasoning here.


We are also excited to welcome several new co-authors who have contributed significantly to the development of this proposal and the explorations that preceded it:

@jan and @auryn from Gnosis Guild
@k06a and ZumZoom from 1inch
@RobAnon and @0xTraub from Revest

This proposal has been moved to the Review stage (pending PR approval). We hope to minimize breaking changes to the interfaces defined in this proposal as we move forward toward finalization.

Author: 0xDigitalOil
Date: 2023-08-01T15:41:48.403Z

0xDigitalOil:

Lastly, regarding this mentioned asset rug mitigation:
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.

What is there to stop a third party approved operator from moving the assets thus avoiding increment of the nonce?


Noticed that in the updated EIP text this concern wasn’t addressed. Any thoughts on this @jay ?

Author: 0xDigitalOil
Date: 2023-08-01T15:45:13.817Z

0xDigitalOil:

In addition, is this factually correct?
The specification proposed above allows ERC-721 tokens to have multiple token bound accounts, one per implementation address.



This was wasn’t addressed either, unless I’m misinterpreting the original text.
You could have the same implementation address, but if different salts are used, you get different resulting TBAs for the same implementation address, no?

Author: jay
Date: 2023-08-01T17:31:35.290Z

0xDigitalOil:




 0xDigitalOil:

What is there to stop a third party approved operator from moving the assets thus avoiding increment of the nonce?


Noticed that in the updated EIP text this concern wasn’t addressed. Any thoughts on this @jay ?


We’re working with marketplaces to determine the best approach to handling active approvals during trustless token transfers. This was part of the motivation behind renaming the nonce() function to state(), which allows more fine-grained representations of account state that can be used by marketplaces. However, preventing fraud at the marketplace level is outside the scope of this proposal.



 0xDigitalOil:




 0xDigitalOil:

In addition, is this factually correct?
The specification proposed above allows ERC-721 tokens to have multiple token bound accounts, one per implementation address.



This was wasn’t addressed either, unless I’m misinterpreting the original text.


Thanks for catching this! It is no longer correct, I’ve updated the proposal to reflect that.

Author: jay
Date: 2023-08-01T17:37:35.805Z

0xDigitalOil:

Is this extract from the ERC spec not contradictory?
    /// 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.

If the ERC specifies that TBAs must allow execution of arbitrary calls by NFT owner but may limit these calls, then arbitrary calls are not allowed, no?


The “by default” clause here was intended to avoid contradiction, but this might be better phrased as “should” in the first sentence. Would be curious to hear others thoughts on this phrasing!



 0xDigitalOil:

Can you give some examples of what these applications are?


One common example is the collection creator wishing to deploy accounts on behalf of their holders in order to subsidize the gas costs associated with deployment.

Author: 0xDigitalOil
Date: 2023-08-01T17:50:48.778Z

Thanks for all your answers @jay

Author: DaveAppleton
Date: 2023-08-03T07:37:46.164Z

I don’t believe that working with marketplaces will address this due to the abundance of assets with different approval  / operator methods - some of which may be unique to the tokens being used.
Many of these tokens may be specific to the NFT community.
We could consider instead a custom pre programmed 4byte list that is excluded from sending transaction which could include approval and operator functions for ERC20 / 721 /1155 / 777 etc

Author: bear2525
Date: 2023-08-06T15:35:23.631Z

Any indication when this might move to review? Are major changes still in the works?

Author: jay
Date: 2023-08-06T17:38:57.898Z

DaveAppleton:

I don’t believe that working with marketplaces will address this due to the abundance of assets with different approval / operator methods - some of which may be unique to the tokens being used.


I agree that the variety of token approval methods makes this a challenging problem to solve. However I don’t think this proposal should specify any token-specific mitigations as they could easily break compatibility with existing applications and may constrain forward compatibility with new token standards.



 DaveAppleton:

We could consider instead a custom pre programmed 4byte list that is excluded from sending transaction which could include approval and operator functions for ERC20 / 721 /1155 / 777 etc


A 4byte blocklist could address this problem, but would come at the expense of compatibility with the existing ecosystem (e.g. it would prevent tokens held within a token bound account from being sold on marketplaces).



 bear2525:

Any indication when this might move to review? Are major changes still in the works?


The PR that moves this proposal to review has been merged.

Author: 0xDigitalOil
Date: 2023-08-17T16:02:46.310Z

I think it would be useful for the TokenBound repo to provide an additional base account implementation that allows for delegation.
I believe a common usecase for 6551 will be to have a grail NFT used as your main identity in a hardware wallet, but delegate all TBA functionality to a hot wallet.
This way allowing use of your main identity NFT’s TBA as your main wallet for all interactions on Ethereum.
Any plans for this?

Author: 0xDigitalOil
Date: 2023-08-17T17:42:14.663Z

This solves it, no?
image1796×1024 111 KB

Author: jay
Date: 2023-08-18T16:32:18.053Z

0xDigitalOil:

I think it would be useful for the TokenBound repo to provide an additional base account implementation that allows for delegation.


Would be great to have this as an example in the reference repo! Feel free to open a PR 



 0xDigitalOil:

This solves it, no?


This method does give accounts more flexibility when implementing alternative authorization mechanisms such as delegation.

Author: cleanunicorn
Date: 2023-08-23T11:36:39.501Z

I have a few suggestions or questions regarding the current spec.
1. Regarding the functionality of isValidSigner which is currently defined as:
    /**
     * @dev Returns a magic value indicating whether a given signer is authorized to act on behalf of the account
     *
     * MUST return the bytes4 magic value 0x523e3260 if the given signer is valid
     *
     * By default, the holder of the non-fungible token the account is bound to MUST be considered a valid
     * signer
     *
     * Accounts MAY implement additional authorization logic which invalidates the holder as a
     * signer or grants signing permissions to other non-holder accounts
     *
     * @param  signer     The address to check signing authorization for
     * @param  context    Additional data used to determine whether the signer is valid
     * @return magicValue Magic value indicating whether the signer is valid
     */
    function isValidSigner(address signer, bytes calldata context)

I believe it should be amended to specify this method SHOULD NOT revert in case the signer is not valid.
I saw that the reference implementation does not revert in case the signature is invalid, but it is important to specify in this case (as well as other cases) the unhappy path the execution takes.
Consider adding (or I can make a PR) with the following specification:
     * MUST return the bytes4 magic value 0x523e3260 if the given signer is valid
     * SHOULD NOT revert in case the given signer is invalid
     * SHOULD return 0x00000000 in case the given signer is invalid

I want to prevent different types of implementations which would require a smart contract interacting with ERC6551Account implementations where it expects that some calls fail (revert) and some return bytes4(0).
2. Add event to signal correct execution.
The interface IERC6551Executable does not emit any event in case of correct execution.
Consider adding an event which logs the correct execution as well as the returned values.
A possible option could look like:
event Execute(
    address indexed to,
    uint256 value,
    bytes data,
    uint256 operation,
    bytes response
);

Additionally, the reference implementation should be updated to emit this event.
3. Gas optimization in case of reverted execution.
Current reference implementation of ERC6551Account execute is:
    function execute(
        address to,
        uint256 value,
        bytes calldata data,
        uint256 operation
    ) external payable returns (bytes memory result) {
        require(_isValidSigner(msg.sender), "Invalid signer");
        require(operation == 0, "Only call operations are supported");

        ++state;

        bool success;
        (success, result) = to.call{value: value}(data);

        if (!success) {
            assembly {
                revert(add(result, 32), mload(result))
            }
        }
    }

If the execution is reverted, the whole contract state is also reverted, thus the state storage variable remains unchanged. By moving the ++state at the end of the method, you reduce the gas used in case the execution is reverted.
However, it is important to note that if the external call eventually calls back into ERC6551Account, the state will not be changed (yet).
This gas optimization might not be a good idea, considering the state does not change until the full execution finalizes. This is something up for discussion. When should the state be updated?
4. Updating the state in case ether is received
This is something that is also up for discussion. Should the contract state be updated in case ether is received?
Thus, it is worth considering if the execution of receive() should update the state and in what conditions.
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.

Considering the Security Considerations, specifically, if a trade would be made invalid by changing the contract state, a simple call to receive() (with 0 ether or even 1 wei) would update and invalidate the Account state.

I am happy to think about these suggestions further, discuss them or even open PRs if any of them go through.

Author: urataps
Date: 2023-09-08T11:06:46.084Z

Though I’m not an author of this proposal, I would like to express my opinion regarding the proposed suggestions, as I consider them insightful and on point.

I agree with specifying that isValidSigner SHOULD NOT revert when the signer is invalid. This will make it easier for contracts that interact with ERC6551Account to handle errors gracefully.
I fully support the inclusion of an Execute event. This will allow for better off-chain monitoring and make it easier to trace successful interactions with the contract.
Your point about gas optimization is valid only in cases when the external call reverts, which usually can be predicted by wallets or other providers before the execution. I think reducing gas for reverting execution paths, at the expense of potential re-entrancy is not worth it.
The point about updating the contract state when ether is received is interesting and I believe needs to be considered by the authors.

Author: 0xPetra
Date: 2023-09-09T00:04:22.661Z

Can the NFT wallet’s private key be used to sign something?
Specifically, could you encrypt a string and only let the owner decrypt it using the NFT’s private key?

Author: Torof
Date: 2023-09-10T02:17:23.566Z

Smart contracts do not have private keys. A NFT cannot own a private key. ERC6551 uses the owner of the NFT (an EOA) as a signer to allow executing code with/from the TBA.

Author: sullof
Date: 2023-09-29T22:29:43.791Z

@jay I made a PR on the ERC6551 reference implementation.
The examples in the repo, despite being basic, are correct, audited and safe. They can be used as a solid base to build more advanced contracts. In order to allow so, the functions must be virtual, so that they can be overridden, and public to be called as super (like supportsInterfaceId).
This PR simply add virtual to the functions’ signature.

github.com/erc6551/reference








Making examples' functions virtual


main ← cruna-cc:making-example-functions-virtual



          opened 10:27PM - 29 Sep 23 UTC




            sullof
          



+30
-21






The examples in the repo, despite being basic, are correct and safe and can be u…sed as a solid base to build more advanced contracts. In order to do so, the functions must be virtual, so that they can be overridden.

@jaydenwindle This PR adds `virtual` to the functions' signatures, making them also `public` so they can be called as super.






PS > I also made a version of the reference implementation for the npm registry at

github.com




GitHub - cruna-cc/erc6551: An adaptation of erc6551/reference for the npm...
An adaptation of erc6551/reference for the npm registry







published as erc6551 on npm, which has already virtualized the functions, since we use the upgradeable example in one of our projects and needed it.

Author: jay
Date: 2023-09-30T16:36:58.605Z

cleanunicorn:

I believe it should be amended to specify this method SHOULD NOT revert in case the signer is not valid.


Good call - might even be worth specifying that it must not revert, as try-catching external calls is a pain.



 cleanunicorn:

The interface IERC6551Executable does not emit any event in case of correct execution.
Consider adding an event which logs the correct execution as well as the returned values.


This is also a good suggestion. An earlier version of the proposal included an event, but it was removed in favor of letting accounts define their own events (since some accounts already define an execution event such as those supporting ERC-4337). I think it’s worth adding back in.



 cleanunicorn:

If the execution is reverted, the whole contract state is also reverted, thus the state storage variable remains unchanged. By moving the ++state at the end of the method, you reduce the gas used in case the execution is reverted.
However, it is important to note that if the external call eventually calls back into ERC6551Account, the state will not be changed (yet).


In order to prevent footguns in the case of reentrancy, the account should update the state prior to the external call. While there may be some gas savings in the case of a reverting call, I don’t know if it makes sense to optimize for the unhappy path while introducing complexity to the happy path.



 cleanunicorn:

This is something that is also up for discussion. Should the contract state be updated in case ether is received?


This is a good question, I think the behavior of state could be made more explicit in the proposal. Since the goal of state is to enable marketplaces to protect against fraud, the thinking is that state is only updated when assets leave the account or an external call is made from the account. In that case, state would not be updated when ether is received.



 sullof:

In order to allow so, the functions must be virtual, so that they can be overridden, and public to be called as super (like supportsInterfaceId).


Thanks for the PR! Merged

Author: arrans
Date: 2023-10-14T13:52:16.247Z

Overall I’m supportive of this EIP becoming standard and commend the authors on their work. I have concerns that I don’t believe to be insurmountable but should at least be addressed before acceptance.
Primarily, I think that Fraud Prevention should be fleshed out before acceptance. Although I agree that a full treatment is beyond the scope of the EIP, at a minimum it’s important to establish a thorough argument for it being possible—I think exploration of asset commitments is the best option for this.
My rationale is that if prevention was found to be more difficult than anticipated, the viability of mass adoption of this ERC would be jeopardised. Brief outlines as currently exist are insufficient as they can provide a false sense of surety— for example, the first recommendation (to commit to account state) is trivially bypassed as transfer of token-owned ERC20/721/1155 assets only change the state of different contracts.
Consequently, the recommendation to use account state to prevent fraud should, in my opinion, be updated to explain why it MUST NOT be used.
A minor point, probably for a follow-up EIP, is consideration of a default, minimally viable implementation for air drops. Counterfactual Accounts and Account Ambiguity can combine for undesirable effects if token owners don’t know the implementation address to access one of their transitively owned assets (or even that such ownership exists). A default implementation would ease discovery of transitive ownership at scale.

Author: sullof
Date: 2023-10-18T02:31:03.418Z

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.

Author: jay
Date: 2023-10-18T18:12:46.165Z

Thanks for the comments @arrans!



 arrans:

I think that Fraud Prevention should be fleshed out


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.



 arrans:

the first recommendation (to commit to account state) is trivially bypassed as transfer of token-owned ERC20/721/1155 assets only change the state of different contracts.


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.



 arrans:

A minor point, probably for a follow-up EIP, is consideration of a default, minimally viable implementation for air drops… A default implementation would ease discovery of transitive ownership at scale.


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



 sullof:

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.


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.