--- eip: 8186 title: Deterministic Accounts for Identifiers description: Smart accounts at pre-computable addresses for ERC-8185 identifiers, with arbitrary execution by the owner author: Carl Barrdahl (@carlbarrdahl) discussions-to: https://ethereum-magicians.org/t/erc-8186-identity-account/27905 status: Draft type: Standards Track category: ERC created: 2026-03-05 requires: 165, 8185 --- ## Abstract This ERC defines deterministic smart accounts for [ERC-8185](./eip-8185.md) identifiers, letting anyone send value to an identity before its owner has ever interacted with Ethereum. An account factory derives one account address per identifier via CREATE2, so under a given factory deployment every identifier has a pre-computable address that can accumulate ETH, [ERC-20](./eip-20.md) tokens, and NFTs before the identifier is claimed. Once the identifier is claimed, the registered owner controls the account through a single `execute` entrypoint that forwards arbitrary calls — giving the identity full participation in on-chain protocols rather than a fixed withdrawal function. Account addresses remain stable across implementation upgrades, and an optional extension lets the factory bind a fixed reclaim authority at deployment, so funds at accounts whose identifiers are never claimed can be recovered. ## Motivation [ERC-8185](./eip-8185.md) maps off-chain identifiers to Ethereum addresses, but only after the entity has registered. The most compelling use cases require sending value *before* the entity has engaged with Ethereum — an address that exists before the entity does. Once the entity claims their identifier, they need more than token withdrawal. They may need to interact with DeFi protocols, claim airdrops, or perform any other on-chain action through their identity's address. A fixed `withdraw` function would require upgrades every time a new integration pattern emerges. This ERC defines a minimal smart account: one `execute` entrypoint for the registered owner, replacing fixed withdrawal hooks with arbitrary calls. ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. ### Overview The system consists of two components: 1. **Account Factory** — deploys one account proxy per identifier at a deterministic address via CREATE2. 2. **Identity Account** — per-identifier proxy; `execute` is callable by the registered owner. ### Account Factory Interface A compliant account factory MUST implement: ```solidity interface IAccountFactory { /// @notice MUST be emitted when an account proxy is deployed for an identifier. event AccountDeployed(bytes32 indexed id, address account); /// @notice Returns the deterministic address for the account of `id`. /// @dev The returned address MUST be deterministic and computable off-chain /// from the factory address, the proxy initcode, and the identifier. /// The returned address MUST NOT change if the account implementation /// is upgraded. function predictAddress(bytes32 id) external view returns (address); /// @notice Deploys the account proxy for `id` if not yet deployed. /// @dev If the account for `id` is already deployed, MUST return its /// address without reverting (idempotent). /// MUST use CREATE2 with `id` as the salt. /// MUST emit AccountDeployed only when an account is actually deployed. /// Anyone MAY call this function — it is not access-controlled. function deployAccount(bytes32 id) external returns (address account); } ``` ### Deterministic Address Computation The account address for an identifier MUST be deterministic and MUST be computable off-chain using the CREATE2 formula: ``` address = keccak256(0xff ++ factoryAddress ++ salt ++ keccak256(initcode))[12:] ``` Where `salt` is the `bytes32` identifier from ERC-8185 and `initcode` is the proxy creation code with constructor arguments. The same identifier yields **different** addresses under **different** factories. This ERC defines the address derivation rule, not a canonical factory deployment. The account address MUST remain stable across implementation upgrades. A beacon proxy pattern satisfies this: upgrading the beacon does not change the proxy's initcode or address. Deployments targeting broad interoperability SHOULD publish or govern a canonical factory address per chain. ### Identity Account Interface Each per-identifier account proxy MUST implement: ```solidity interface IIdentityAccount { /// @notice Forward an arbitrary call from this account's address. /// @dev MUST allow if `ownerOf(id) == msg.sender`. /// MUST revert otherwise, unless the optional reclaim extension /// defined by this ERC authorizes the caller. /// MUST revert if the inner call fails. function execute(address target, bytes calldata data, uint256 value) external returns (bytes memory); /// @notice The identifier this account is bound to. function id() external view returns (bytes32); /// @notice The ERC-8185 registry this account resolves ownership against. function registry() external view returns (address); } ``` Accounts MUST resolve ownership by calling `ownerOf(bytes32)` on the ERC-8185 registry bound at deployment. Given an arbitrary account address, protocols and indexers MUST be able to query which identifier and registry the account is bound to via `id()` and `registry()`. Accounts MUST implement `supportsInterface` ([ERC-165](./eip-165.md)) and MUST return `true` for the `IIdentityAccount` interface ID and for the interface ID of each supported extension interface. ### Optional Reclaim Extension Deployments that need to recover funds from accounts whose identifiers are never claimed — for example a funding platform redirecting unclaimed grants to a public goods pool — MAY implement: ```solidity interface IReclaimableIdentityAccount { /// @notice The address authorized for `execute` while the identifier is unclaimed. function reclaimTo() external view returns (address); /// @notice The timestamp after which the reclaim authority becomes active. function reclaimableAfter() external view returns (uint256); } ``` If an account implements `IReclaimableIdentityAccount`: 1. The reclaim configuration MUST be established by the factory when the account is deployed, and MUST be identical regardless of who calls `deployAccount`. Accounts MUST NOT expose any function that sets or changes `reclaimTo` or `reclaimableAfter` after deployment. 2. `execute` MUST additionally allow when `ownerOf(id) == address(0)`, `reclaimTo() != address(0)`, `msg.sender == reclaimTo()`, and `block.timestamp > reclaimableAfter()`. 3. The reclaim path MUST NOT apply while the identifier is claimed. It applies whenever the identifier is unclaimed, both before first claim and after revocation. 4. `reclaimableAfter` SHOULD leave a substantial window between deployment and reclaim activation (months rather than hours), so the entity has a meaningful opportunity to claim its identifier. A factory MAY disable reclaim by configuring `reclaimTo()` as `address(0)`. ### Funding Paths The account SHOULD include a `receive()` function to accept native ETH. To accept safe transfers of NFTs, account implementations SHOULD implement `onERC721Received` ([ERC-721](./eip-721.md)) and `onERC1155Received` / `onERC1155BatchReceived` ([ERC-1155](./eip-1155.md)). Accounts MAY implement [ERC-1271](./eip-1271.md), treating a signature as valid when it is produced by the current registered owner; this enables marketplace listings and permit-style approvals from the account address. ### Account Lifecycle 1. **Before deployment**: The account address is computable by anyone. Funds sent to this address accumulate without requiring proxy deployment. 2. **Deployment**: Anyone may call `deployAccount(id)` — including the registered owner after claiming their identifier. 3. **Unclaimed**: No caller is authorized for `execute` — unless the account implements `IReclaimableIdentityAccount` and reclaim is active, in which case `reclaimTo` may call `execute`. 4. **After claim**: The registered owner may deploy the account (if not yet deployed) and call `execute`. The reclaim path, if present, MUST NOT apply while claimed. 5. **After revocation**: The identifier becomes unclaimed again. The reclaim path, if present, MAY apply again subject to the extension rules. ### Ownership Change Behavior If ownership of an identifier changes (revoke and re-claim by a different entity), accumulated funds at the account address become controllable by the *current* registrant. The account address is stable; the controller is determined by the registry at execution time. Because accounts resolve ownership through `ownerOf`, which follows ERC-8185 alias links, funds sent to the derived account of an alias identifier are controllable by the owner of the primary identifier. ## Rationale ### `execute` over `withdraw` A fixed `withdraw` cannot track every protocol; `execute` lets the owner encode any call through the account's address. ### Minimal interface The standard interface contains a single state-changing function with a single authorization rule: the registered owner may call `execute`. The two view functions, `id()` and `registry()`, exist only so that protocols and indexers can introspect an account's binding. This follows the same principle as [ERC-6551](./eip-6551.md): standardize the account primitive, let implementations extend. ### Factory-bound reclaim A per-account `setReclaim` function — permissionless, first-come-first-served — was considered and rejected: anyone could deploy an unconfigured account, name themselves the reclaimer, and capture funds accumulated at unclaimed accounts. An account-level setter cannot identify a rightful reclaimer. Binding the configuration to the factory removes the race entirely: every account a factory deploys carries the same reclaim policy no matter who triggers deployment, so a third party deploying an account gains nothing. Because the factory address is an input to the CREATE2 derivation, the policy is encoded in the account address itself — a sender who funds a factory's `predictAddress(id)` is verifiably opting into that factory's reclaim policy. A platform can therefore deploy a factory whose unclaimed funds flow to, for example, a public goods pool, and disclose exactly that through the address alone. Factory-bound reclaim is one policy for all senders. Senders who instead want their *own* deposit back if the identifier is never claimed need a depositor-side escrow that holds each deposit until claim. Such an escrow composes externally — it needs only `ownerOf` from the registry and `predictAddress` from the factory — and is deferred to a future ERC. ### Beacon proxy pattern Using a beacon proxy (rather than a minimal proxy or a direct deployment) allows upgrading the account logic for all identifiers simultaneously without changing any account address. The account address depends only on the factory address, the beacon address, and the identifier salt — not on the implementation address. ### Anyone can call `deployAccount` Account deployment is permissionless because the account address must be usable before the entity has interacted with Ethereum. The deterministic address ensures the result is the same regardless of who deploys. After claiming an identifier, the registered owner may deploy their own account to access accumulated funds. `deployAccount` is idempotent rather than reverting when the account already exists: a deploy-and-configure batch cannot be griefed by a front-run deployment that would otherwise make the whole batch revert. This mirrors [ERC-6551](./eip-6551.md)'s `createAccount`. ### Canonical factory selection This ERC does not fix a factory address. Factory selection is a deployment concern; ecosystems that want one canonical deposit address per identifier should coordinate on a canonical factory per chain. ### Separate from the registry This ERC is architecturally independent from [ERC-8185](./eip-8185.md). The registry holds no funds and has no opinion about how money flows. Protocols that only need identity resolution depend on the registry alone; the account module can be upgraded, replaced, or embedded into the registry without affecting the registry interface. ### Relationship to ERC-6551 (Token Bound Accounts) [ERC-6551](./eip-6551.md) solves a structurally similar problem — "thing X owns a smart account" — for NFTs. This ERC does not use ERC-6551 because: the registry is not an NFT (identifier ownership is revocable, non-transferable, and exists before anyone claims it); ERC-6551 accounts are bound to a fixed implementation at creation time (this ERC requires beacon upgradeability); and ERC-6551's interface is larger than needed (`token()`, `state()`, `isValidSigner()`, `operation` parameter). Implementations may expose ERC-6551 interfaces in addition to `IIdentityAccount`. ## Backwards Compatibility This ERC is compatible with any [ERC-20](./eip-20.md) token and does not modify any existing standards. Accounts also interoperate with [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) safe transfers when they implement the receiver hooks described under [Funding Paths](#funding-paths). ## Reference Implementation A minimal reference implementation is provided in [assets/erc-8186](../assets/eip-8186/): - [`AccountFactory.sol`](../assets/eip-8186/AccountFactory.sol) — factory using [EIP-1167](./eip-1167.md) minimal proxies with idempotent `deployAccount`; reclaim policy fixed in the constructor - [`IReclaimableIdentityAccount.sol`](../assets/eip-8186/IReclaimableIdentityAccount.sol) — optional factory-bound reclaim extension interface - [`IdentityAccount.sol`](../assets/eip-8186/IdentityAccount.sol) — account with owner `execute` + factory-bound reclaim + `receive` + ERC-721/ERC-1155 receiver hooks + ERC-165 introspection The implementation has no external dependencies and uses [EIP-1167](./eip-1167.md) clones for simplicity. EIP-1167 fixes the implementation at factory deployment time; upgradeable deployments must use a beacon proxy so account addresses remain stable across implementation upgrades. ## Security Considerations ### Account upgradeability The beacon owner can upgrade the account implementation for all proxies simultaneously. This is a critical trust assumption — a malicious upgrade could add functions that bypass ownership checks. The beacon owner key should be held by a multisig or governance contract with appropriate controls. ### Arbitrary execution and reentrancy `execute` grants arbitrary CALL capability to the owner. The owner can approve tokens, call untrusted contracts, and trigger callbacks. Because a compliant account holds no mutable authorization state of its own — ownership is read from the registry at execution time, and any reclaim configuration is fixed at deployment — a reentrancy guard on `execute` is unnecessary for such implementations. Implementations that add mutable state should reassess and guard `execute` if re-entering it could observe or corrupt that state. ### Funds sent to never-claimed identifiers Under the base standard, funds transferred directly to the account of an identifier that is never claimed are irrecoverable: no caller is ever authorized for `execute`. Under a factory implementing the reclaim extension, such funds become controllable by `reclaimTo` once `reclaimableAfter` passes — the policy is fixed by the factory and encoded in the account address (see [Factory-bound reclaim](#factory-bound-reclaim)). Senders need to understand which factory derived the address they are funding and what its reclaim policy is. Senders who need to recover their own deposit should use an escrow that releases the deposit only once the identifier is claimed, rather than transferring directly to the counterfactual address. ### Reclaim authority If an account supports `IReclaimableIdentityAccount`, `reclaimTo` has full `execute` power while reclaim is active and is equally privileged to an owner. Because the configuration is factory-bound, an adversary who deploys an account early merely starts the reclaim clock sooner — the destination cannot be redirected. Claiming the identifier always suspends the reclaim path; entities should claim before `reclaimableAfter` if they intend to keep accumulated funds. ### Cross-chain deployments Deterministic deployment of the same factory (same factory address and initcode) yields the same account address on every chain, but ERC-8185 ownership is resolved per-chain. Two consequences: an identifier claimed on a chain with a weaker verifier or registry admin controls the same account address on that chain; and funds sent to the address on a chain where the factory (or registry) is not deployed may be unrecoverable. Protocols need to resolve ownership on the chain where they send funds. ### Counterfactual address griefing Account addresses are public and computable by anyone. An adversary could pre-deploy account proxies for identifiers at a gas cost, without any effect on correctness — the address and accumulated funds are unaffected. This is a nuisance, not a security issue. ### Funds go to current owner After revocation and re-claim by another entity, funds accumulated before revocation become controllable by the new claimant. This is by design — the account is tied to the identifier, not to any particular owner. Entities should withdraw funds before revoking their claim. ### Native ETH edge cases Native ETH can arrive at the account address before the proxy is deployed (via coinbase transactions or other mechanisms). The `receive()` function handles ETH arriving after deployment. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).