EIP-7779 Interoperable delegated accounts
No call decision · Account abstraction · EIP status Draft · created 2024-10-02
Defines a standard interface for looking up account information and storage bases and for cleaning up before leaving, so that an EOA delegated to a smart account via EIP-7702 can re-delegate without storage collisions when it moves wallets.
Why it is needed
Once EIP-7702 applies, an existing EOA (externally owned account) can delegate itself to a smart account implementation and use features such as batch execution and gas sponsorship. The problem is that the delegated account uses storage tied to its own address. If the user switches wallets and re-delegates to a different implementation, the storage used by the previous wallet stays in place, and if the new implementation uses the same slots, a collision occurs that can lead to unexpected behavior such as account lockup or security vulnerabilities. Until now an EOA worked with only a cryptographic key pair and moved freely between wallets, so moving wallets is already familiar to users. To preserve this freedom after delegation, a common convention is needed so that an account can check in a standard way which storage slots it has used and clean them up before moving.
What changes
Today
- An EOA works with only a key pair, so there is no onchain state to move when you change wallets
- A 7702 delegated account uses storage slots unique to each implementation, but there is no standard way to look them up
- When re-delegating, you have to rely only on hash probability to avoid collisions
After EIP-7779
- accountStorageBases() lets you look up the full history of storage bases the account has used so far
- accountId() lets you check information such as the name and version of the currently delegated implementation
- The optional onRedelegation() lets you clean up existing storage as much as possible before re-delegating
Who is affected
- Users: Even after 7702 delegation, you can move wallets freely without worrying about storage collisions
- Wallet developers: When taking over an account that another wallet has delegated, you can use the standard interface to verify its storage and request cleanup
- Smart account implementation teams: To comply with the standard, an account must implement both interfaces, compute storage bases with keccak256, and record its base during initialization
In depth
accountId(): query the account namespace
A view function that returns the account's namespace as a string. The namespace can include the account version, the account name, the wallet vendor name, and so on, and the original example is "v0.1.2.7702Account.WalletProjectA". The standard does not standardize the format of the namespace itself. As a usage example, it cites a wallet that shows a prompt such as "Move from Wallet A to Wallet B?" during redelegation and gets the Wallet A information from this function. The IInteroperableDelegatedAccount interface, which contains accountId() and accountStorageBases(), MUST be implemented to comply with the standard.
accountStorageBases(): query the storage base history
Returns the list of base storage slots the account has used. To reduce collisions, slot computation MUST use keccak256(), and the preimage of the hash (name, version, or a combination) is left to the account. The list is stored as a bytes32[] at the slot keccak(keccak('InteroperableDelegatedAccount.ERC.Storage')-1) & ~0xff. Because it is an append-only array, a newly delegated account must add its own base without overwriting existing values. The addition happens during account initialization and can be skipped if an identical entry already exists. An account that uses an external storage contract returns a value made by repeating the ERC number at the front and appending the storage contract address. If the address is 0xAAAA...AAAA, the value is 0x777977797779777977797779AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA. If a colliding base is found, the account can run additional on-chain or off-chain verification and then decide whether to delegate, and if the collision point holds a suspicious value aimed at the user (such as a shadow signer), the wallet can revert the delegation.
onRedelegation(): cleanup hook before redelegation
A function called before delegation is switched to a different implementation. It is contained in the IRedelegableDelegatedAccount interface, and implementing it is optional (MAY). A new wallet that is taking over an already delegated EOA can trigger the call, and the existing account runs wallet-specific preparation logic, such as returning the necessary storage to its pre-initialization state. It must verify through msg.sender that the caller is the legitimate owner, and an account that uses its own verification method can also handle this as a self-call. The standard does not enforce specific behavior and does not require storage to be cleared completely. Some accounts use many data types that are hard to empty, such as mappings, so the function is designed as a best-effort function that leaves the state as clean as possible for the new wallet to use.
Design rationale: verifying storage without relying on hash probability
Some argue that if each wallet uses a different base slot, the collision probability is low enough because of the nature of hashing, so no check is needed, but this standard takes the opposite position. Just as developers in the diamond pattern (ERC-2535) verify a facet's storage directly instead of relying only on hash probability, the standard holds that it is better to scan the account's storage before delegation, at least the region the new implementation will use. So accountStorageBases() is designed to return the full history of storage slots the account has used, not only the base of the current implementation, and this history serves as the material for the pre-delegation storage scan.
Security considerations
Calls to onRedelegation() must have a security mechanism that properly authenticates the owner. The function also must not reset replay protection (prevention of reuse) values, because that can create vulnerabilities such as signature replay attacks. After storage is cleared, the initialization function must be protected so that an arbitrary party cannot call it again. Also, because this standard is an ERC, enforcing the rules does not guarantee that real implementations follow them. Some accounts may pretend to support the standard, so it is recommended to verify that an implementation is a known safe one, and where needed the authenticity of the information can be checked off-chain.
Status: Draft, discussed on a call only
This proposal is in Draft status, so the interface and detailed rules may change. On the core developer side it was discussed in AMA #14 on 08.29, but no decision was made. Because it is an ERC, it is not included through a hard fork. It becomes established as wallets and account implementations adopt it.