EIP.tools

EIP.tools

⚠️ DraftStandards Track: ERC

ERC-8186: Deterministic Accounts for Identifiers

Smart accounts at pre-computable addresses for ERC-8185 identifiers, with arbitrary execution by the owner

Authors
Created2026-03-05
Discussion Linkhttps://ethereum-magicians.org/t/erc-8186-identity-account/27905
Requires

Markdown

https://raw.githubusercontent.com/ethereum/ERCs/re...
Pull Request#1582PR open

EIP-GPT summary

Contents
AbstractMotivationSpecificationOverviewAccount Factory InterfaceDeterministic Address ComputationIdentity Account InterfaceOptional Reclaim ExtensionFunding PathsAccount LifecycleOwnership Change BehaviorRationaleexecute over withdrawMinimal interfaceFactory-bound reclaimBeacon proxy patternAnyone can call deployAccountCanonical factory selectionSeparate from the registryRelationship to ERC-6551 (Token Bound Accounts)Backwards CompatibilityReference ImplementationSecurity ConsiderationsAccount upgradeabilityArbitrary execution and reentrancyFunds sent to never-claimed identifiersReclaim authorityCross-chain deploymentsCounterfactual address griefingFunds go to current ownerNative ETH edge casesCopyright

Abstract

This ERC defines deterministic smart accounts for ERC-8185 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 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 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:

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:

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

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) and onERC1155Received / onERC1155BatchReceived (ERC-1155).

Accounts MAY implement ERC-1271, 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: 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'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. 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 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 token and does not modify any existing standards. Accounts also interoperate with ERC-721 and ERC-1155 safe transfers when they implement the receiver hooks described under Funding Paths.

Reference Implementation

A minimal reference implementation is provided in assets/erc-8186:

The implementation has no external dependencies and uses EIP-1167 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). 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 and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth