ERC-6551: Non-fungible Token Bound Accounts

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.

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

1 Like

Hi @jay !
Thanks for coming back to me so quickly :slight_smile:

That makes sense! I was under the impression that I could do it in IPFS, am I wrong?

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

1 Like

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

1 Like

Thanks @MidnightLightning !
I just created a new thread.

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.

@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.

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.

@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.

1 Like

That sounds great. Thanks for explaining.

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

@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?

1 Like

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.

1 Like

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.

2 Likes

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

1 Like

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.

1 Like

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?

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.

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