--- eip: 8402 title: Advanced Account Controls description: Extend native AA with policies, locking, and nonce-free transactions author: Chris Hunter (@chunter-cb) , Pedro Gomes (@pedrouid) discussions-to: https://ethereum-magicians.org/t/composable-native-account-abstraction/29526 status: Draft type: Standards Track category: Core created: 2026-08-27 requires: 8398, 8401 --- ## Abstract This proposal extends the portable Keystore from [EIP-8401](./eip-8401.md) and the native account-abstraction transaction from [EIP-8398](./eip-8398.md) with three optional controls: policy-gated actors, delayed account locking, and nonce-free transactions. It assigns extension points reserved by those proposals while preserving the base transaction envelope and account address. ## Motivation EIP-8401 provides portable actors and account configuration. EIP-8398 uses that authority model for a native account-abstraction transaction with custom authentication, batching, sponsorship, and keyed sequential replay protection. Some deployments also need narrower session-key permissions, stronger protection against authority changes, and unordered transaction submission. Those controls add storage, validation branches, and operational risk. Defining them as an extension keeps the Keystore and basic native transaction small while allowing chains and wallets to adopt the advanced behavior without a second transaction format. ## 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. An implementation of this proposal MUST implement EIP-8401 and EIP-8398. Unless changed below, every base Keystore, validation, execution, gas-accounting, and RPC rule remains unchanged. ### Scope Extensions This proposal assigns one EIP-8398 scope bit: | Bit | Value | Name | Authority | |-----|-------|------|-----------| | 1 | `0x02` | `POLICY` | May originate an AA transaction, with every call gated to the actor's policy manager | The EIP-8398 meanings of `SENDER`, `NONCE`, `SELF_PAYER`, and `SPONSOR_PAYER`, and the EIP-8401 meaning of admin (`scope == 0x00`), remain unchanged. For `sender_auth`, an actor is eligible when it is admin or carries `SENDER` or `POLICY`. Whenever `POLICY` is set, the policy gate applies even if `SENDER` is also set. EIP-8398's `NONCE` scope continues to govern sequential nonce channels. ### Policy-Gated Actors The Keystore adds two slots keyed by `(account, actorId)`: ```text policy_manager(account, actorId) -> address policy_commitment(account, actorId) -> bytes32 ``` It also adds account change type `5`, `SetActorPolicy`, whose payload is: ```text abi.encode(bytes32 actorId, address manager, bytes32 commitment) ``` `SetActorPolicy` MUST require an existing actor whose scope includes `POLICY`. It stores `manager` and `commitment` verbatim. Removing `POLICY` through `AuthorizeActor`, or applying `RevokeActor`, MUST clear both slots. An initial actor with `POLICY` starts with a zero manager and zero commitment because EIP-8401 initial actors contain no extension data. For a transaction authenticated by a policy-bearing actor, the protocol snapshots `policy_manager(sender, actorId)` before call execution. Every `call.to` in every phase MUST equal that snapshot. A mismatched call is not dispatched and fails with: ```solidity error ActorPolicyViolation(bytes32 actorId, address target); ``` This is an execution failure, not a transaction-validity failure. Existing EIP-8398 phase atomicity and nonce-consumption rules apply. The manager contract is responsible for interpreting `policy_commitment` and enforcing application-specific limits. ### Account Locking This proposal assigns account-state flag bits: | Value | Name | Meaning | |-------|------|---------| | `0x04` | `LOCKED` | Actor configuration and delegation are frozen | | `0x08` | `UNLOCK_INITIATED` | The lock delay is running | The account state additionally stores a `uint16 unlock_delay` while no unlock is pending, or a `uint48 unlocks_at` while `UNLOCK_INITIATED` is set. Two local account-change types are added: | change_type | Name | Payload | |-------------|------|---------| | `3` | `Lock` | `abi.encode(uint16 unlockDelay)` | | `4` | `Unlock` | empty | `Lock` and `Unlock` MUST use EIP-8401's Local channel, MUST be admin-authorized, and MUST each be the sole change in its signed batch. `Lock` is valid only when the account is unlocked and `unlockDelay != 0`. It sets `LOCKED`, clears `UNLOCK_INITIATED`, and stores the delay. `Unlock` is valid only when `LOCKED` is set and no unlock is pending. It sets `UNLOCK_INITIATED` and stores `unlocks_at = block.timestamp + unlock_delay`. While an account is effectively locked, actor authorization, actor revocation, policy changes, account import, and code delegation MUST be rejected through both the native transaction and EVM paths. `Unlock` and `IncrementLocalEpoch` remain permitted. Once `block.timestamp >= unlocks_at`, the lock is no longer effective and the next account operation MUST clear both lock flags and the lock time. Implementations MUST emit: ```solidity event AccountLocked(address indexed account, uint16 unlockDelay); event AccountUnlockInitiated(address indexed account, uint48 unlocksAt); ``` ### Nonce-Free Transactions EIP-8398 defines keyed sequential transaction nonces from `0` through `NONCE_KEY_MAX - 1` and reserves `NONCE_KEY_MAX`. This proposal assigns that reserved value to the nonce-free mode. Nonce authorization is: | Actor | Permitted modes | |-------|-----------------| | Admin | Keyed sequential and nonce-free | | Restricted without `NONCE` | Nonce-free only | | Restricted with `NONCE` | Keyed sequential and nonce-free | When `nonce_key == NONCE_KEY_MAX`, `nonce_sequence` MUST be zero and `valid_before` MUST be non-zero. No nonce slot is read or incremented. The logical transaction identifier is: ```text REPLAY_ID_TYPE = 0x7901 replay_id = keccak256(REPLAY_ID_TYPE || rlp([ chain_id, resolved_sender, valid_after, valid_before, account_changes, calls, metadata, payer ])) ``` The protocol MUST reject a nonce-free transaction whose normalized `valid_before` is farther than `NONCE_FREE_EXPIRY_WINDOW` in the future. It MUST maintain a consensus-state replay set keyed by `(sender, replay_id)` until `valid_before` elapses. The replay set MUST use a fixed-capacity circular buffer of `REPLAY_BUFFER_CAPACITY`; if all entries are live and the buffer is full, the transaction is invalid. Block builders MUST NOT include two transactions with the same `(sender, replay_id)`. A pending nonce-free transaction with the same tuple is a replacement candidate under the node's normal fee-bump rules. Keyed transactions are replacement candidates when `(sender, nonce_key, nonce_sequence)` matches. ### Validation and Execution Deltas EIP-8398 processing changes as follows: 1. Accept `POLICY` as a sender initiation grant and apply the policy gate during call execution. 2. Reject configuration and delegation changes while the sender is effectively locked. 3. When `nonce_key == NONCE_KEY_MAX`, apply the nonce-free authorization and replay rules above instead of EIP-8398's sequential nonce check. 4. Skip nonce-sequence increment for `NONCE_KEY_MAX`. 5. Charge for the nonce-free replay-set reads and writes instead of EIP-8398's sequential nonce storage operation. ### Constants | Name | Value | Meaning | |------|-------|---------| | `POLICY` | `0x02` | Policy-gated actor scope | | `LOCKED` | `0x04` | Account-state lock flag | | `UNLOCK_INITIATED` | `0x08` | Account-state pending-unlock flag | | `NONCE_KEY_MAX` | `2^256 - 1` | Nonce-free transaction mode | | `REPLAY_ID_TYPE` | `0x7901` | Replay-identifier domain separator | | `NONCE_FREE_EXPIRY_WINDOW` | chain parameter | Maximum nonce-free validity window | | `REPLAY_BUFFER_CAPACITY` | chain parameter | Consensus replay-buffer capacity | `NONCE_FREE_EXPIRY_WINDOW` and `REPLAY_BUFFER_CAPACITY` MUST be fixed under the chain's consensus rules. Capacity MUST be sufficient for the maximum accepted nonce-free throughput over the full expiry window. ## Rationale ### Why Keep the EIP-8398 Envelope? The base transaction already contains the authentication, batching, sponsorship, validity, and nonce fields needed by these features. Assigning reserved values avoids a second transaction type and lets base transactions remain byte-compatible with extension-aware clients. ### Why Separate Policies and Locking? Policies introduce trusted manager contracts and additional execution gates. Locking introduces delayed state transitions and can temporarily prevent recovery. Both are useful controls, but neither is required for native account abstraction. ### Why Separate Nonce-Free Transactions? EIP-8398 keyed channels retain ordinary sequential replay protection. Nonce-free replay buffers remove sequence coordination entirely, but add consensus state, authorization rules, and mempool complexity that the base standard does not need. ## Backwards Compatibility An EIP-8398 transaction using any keyed sequential nonce and no actors using `POLICY` behaves unchanged. The EIP-8401 account representation and initial-actor commitment, transaction type, and signature payload remain unchanged. Chains that implement only EIP-8401 and EIP-8398 continue to reject `nonce_key == NONCE_KEY_MAX` and treat `POLICY` as a non-authorizing reserved bit. ## Security Considerations **Policy managers**: A policy-bearing actor can reach only its configured manager, but that manager is trusted to enforce the commitment correctly. Accounts SHOULD use audited, non-upgradeable managers. Setting the account's own wallet code as manager is safe only if that code explicitly enforces the policy. **Policy lifecycle**: Revocation and removal of `POLICY` MUST clear extension state so later reauthorization cannot resurrect stale permissions. **Account lock**: A long delay can prevent legitimate recovery, while a short delay provides little protection. Wallets MUST present the delay clearly and retain an admin path capable of initiating unlock. `IncrementLocalEpoch` remains available while locked so outstanding local configuration signatures can be invalidated. **Nonce-free replay**: The replay buffer and maximum validity window are consensus parameters. An undersized buffer can reject otherwise valid traffic; inconsistent parameters can cause consensus failure. The logical `replay_id`, rather than the full signed transaction hash, MUST key replay protection so re-signing or fee replacement cannot create a second includable transaction with identical effects. **Nonce-free authority**: A restricted actor without `NONCE` may use nonce-free transactions but cannot consume an account's ordered transaction lanes. Implementations MUST apply this distinction after authentication. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).