---
eip: 8286
title: Modular Accounts for Frame Transactions
description: EIP-8141 native account abstraction validation extension for ERC-7579 modular accounts
author: Chiranjeev Mishra (@chiranjeev13), Pedro Gomes (@pedrouid), Alex Forshtat (@forshtat)
discussions-to: https://ethereum-magicians.org/t/erc-8286-modular-accounts-for-frame-transactions/28695
status: Draft
type: Standards Track
category: ERC
created: 2026-06-04
requires: 7579, 8141
---

## Abstract

[ERC-7579](./eip-7579.md) standardizes a core module system for modular smart accounts: module types, the module lifecycle, installation, and account configuration. This proposal extends that system to [EIP-8141](./eip-8141.md) native account abstraction by defining the validation flow for frame transactions. It reuses ERC-7579's module structure unchanged: a validator returns an *approval mode*, and the account applies it through EIP-8141's `APPROVE` instruction during a `VERIFY` frame. Keeping the module system in ERC-7579 and layering account-abstraction-specific validation flows as extensions lets a single module ecosystem serve accounts across multiple account abstraction implementations, without forking modules or vendor lock-in.

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

### Relationship to ERC-7579

This standard is an extension to [ERC-7579](./eip-7579.md) and does not redefine its module system. A compliant account and its modules MUST conform to ERC-7579 for:

- module types and their identifiers,
- the module lifecycle interface (`onInstall`, `onUninstall`, `isModuleType`),
- module installation and configuration (`installModule`, `uninstallModule`, `isModuleInstalled`), and
- account configuration (`accountId`, `supportsModule`, `supportsExecutionMode`).

ERC-7579's execution interface (`execute`, `executeFromExecutor`) is not required, see [Execution](#execution).

This standard adds only what EIP-8141 native account abstraction requires: the approval-mode validation flow and the interfaces defined below. It is the EIP-8141 analogue of ERC-7579's own validation flow. Where ERC-7579 defines `validateUserOp` for its [ERC-4337](./eip-4337.md) target, this standard defines `validateFrame`.

### Approval Mode

This standard defines the **approval mode**: a `uint8` bearing the value of the [EIP-8141](./eip-8141.md) `APPROVE` instruction's `scope` operand. It is the result of validation as defined in this standard — a validator returns an approval mode to the account, and the account applies it by executing the `APPROVE` instruction with that value during a `VERIFY` frame.

The bit semantics are equivalent to those of the EIP-8141 scope operand. The two least significant bits form a bitmask:

- bit `0` (payment): the account approves paying the transaction's total gas cost.
- bit `1` (execution): the account approves subsequent frames acting on its behalf as `sender`.

This yields the following modes, which an account MUST interpret consistently with [EIP-8141](./eip-8141.md)'s `APPROVE` scope operand:

| Mode  | Name                            | Payment | Execution |
|-------|---------------------------------|---------|-----------|
| `0x0` | `APPROVE_NONE`                  | no      | no        |
| `0x1` | `APPROVE_PAYMENT`               | yes     | no        |
| `0x2` | `APPROVE_EXECUTION`             | no      | yes       |
| `0x3` | `APPROVE_EXECUTION_AND_PAYMENT` | yes     | yes       |

`APPROVE_SCOPE_MASK` is defined as `0x3`. A frame's *allowed scope* is `frame.flags & APPROVE_SCOPE_MASK`, equivalently `FRAMEPARAM(frameIndex, 0x06)`.

Accounts are NOT REQUIRED to grant every mode. The account MUST NOT approve a mode outside the allowed scope of the executing `VERIFY` frame, and `APPROVE_EXECUTION` is only valid when the frame's resolved target is `tx.sender`. If validation does not succeed, the account MUST NOT call `APPROVE`, which leaves the mode at `APPROVE_NONE` and invalidates the transaction.

An account MUST declare which approval modes it supports through `supportsApprovalMode` (see below), and MUST NOT call `APPROVE` with a mode it does not support.

### EIP-8141 Frame Validator

ERC-7579 defines `validateUserOp` validators and assigns module type id `1` to them.

An **EIP-8141 frame validator** is a module that determines the approval mode a transaction is entitled to.
This standard assigns frame validators a new module type id: `11` <!-- TBD -->.
A module MAY additionally be an ERC-7579 validator (type id `1`) and serve both targets.

When the account's code executes in a `VERIFY` frame, the account selects a frame validator and calls it. The validator returns an approval mode, which the account then applies via `APPROVE`.

In place of ERC-7579's `validateUserOp` validation function, an EIP-8141 validator MUST implement ERC-7579's `IERC7579Module` interface and the `IFrameValidator` interface below:

```solidity
interface IFrameValidator is IERC7579Module {
    /**
     * @dev Validates the active VERIFY frame and returns the approval mode the
     *      validator authorizes for the transaction.
     * @param sigHash      the canonical transaction signing hash, i.e. TXPARAM(0x08).
     * @param frameIndex   the index of the executing VERIFY frame, i.e. TXPARAM(0x0A).
     * @param allowedScope the approval mode permitted by the frame flags, i.e.
     *                      FRAMEPARAM(frameIndex, 0x06).
     * @param data         validator-specific calldata supplied by the account, e.g. a
     *                      signature envelope or policy parameters.
     *
     * MUST NOT modify state; the validator is invoked within the STATICCALL context of
     * a VERIFY frame.
     * MUST return APPROVE_NONE (0x0) if validation fails.
     * MAY revert to indicate failures not related to the core validation logic directly (e.g. decoding errors).
     * The returned approval mode SHOULD be a subset of `allowedScope`; the account is
     * the final authority and MUST mask the result with `allowedScope` before approving.
     */
    function validateFrame(
        bytes32 sigHash,
        uint256 frameIndex,
        uint8 allowedScope,
        bytes calldata data
    ) external view returns (uint8 approvalMode);
}
```

The account's behavior when running in a `VERIFY` frame targeting itself is:

1. Read the allowed scope `allowedScope = FRAMEPARAM(frameIndex, 0x06)`.
2. Select an installed validator. This standard does not dictate the selection mechanism (see [Validator Selection](#validator-selection)). The account MUST verify the selected module is an installed frame validator (module type id `11` <!-- TBD -->).
3. Call `validateFrame` on the selected validator with all correctly specified input parameters, and the validator-specific calldata, then mask the result: `approvalMode = approvalMode & allowedScope`.
4. If `approvalMode` has the execution bit (bit `1`) set, the account MUST check the modes presented by the transaction's `SENDER` frames (see [`SENDER` Frame Execution Modes](#sender-frame-execution-modes)) against the execution modes it supports (`supportsExecutionMode`, inherited from `IERC7579AccountConfig`). If `supportsExecutionMode` returns `false` for any presented mode, the account MUST clear the execution bit, leaving `approvalMode = approvalMode & APPROVE_PAYMENT`.
5. If the resulting `approvalMode` is `APPROVE_NONE`, the account MUST NOT call `APPROVE`; the frame reverts and the transaction is invalid.
6. Otherwise the account MUST call `APPROVE(approvalMode)`.

The raw `signature` bytes of [EIP-8141](./eip-8141.md) `tx.signatures` are intentionally not accessible from the EVM. A validator therefore obtains its authorization material either by reading the metadata of an already protocol-validated signature via the `SIGPARAM` instruction (for the natively supported `SECP256K1` and `P256` schemes), or by receiving a self-contained signature envelope in `data` and verifying it itself (for module-defined schemes such as passkeys, multisig, or BLS).

Because [EIP-8141](./eip-8141.md) permits only a frame's resolved target (or code it `DELEGATECALL`s into) to call `APPROVE`, a validator reached via `STATICCALL` cannot approve on its own. The account remains the sole caller of `APPROVE`.

### `SENDER` Frame Execution Modes

This standard defines four execution modes describing the shape of a transaction's `SENDER` frames. They extend ERC-7579's mode encoding with two new `callType` values; the `execType` values keep their ERC-7579 meanings, which apply to frames verbatim:

- `callType` `0x02` <!-- TBD -->: the transaction has exactly one `SENDER` frame.
- `callType` `0x03` <!-- TBD -->: the transaction has multiple `SENDER` frames.
- `execType` `0x00` (revert-all): the frame belongs to an atomic-batch group (all-or-nothing).
- `execType` `0x01` (try): the frame is not part of an atomic batch; a revert discards only that frame.

The mode constants, encoded per ERC-7579's mode layout (`callType` in byte 0, `execType` in byte 1, all remaining bytes zero):

| Constant                   | `callType` | `execType` | Meaning                                      |
|----------------------------|------------|------------|----------------------------------------------|
| `FRAME_MODE_SINGLE`        | `0x02`     | `0x01`     | one standalone `SENDER` frame                |
| `FRAME_MODE_SINGLE_ATOMIC` | `0x02`     | `0x00`     | one `SENDER` frame inside an atomic batch    |
| `FRAME_MODE_BATCH`         | `0x03`     | `0x01`     | multiple `SENDER` frames, reverting independently |
| `FRAME_MODE_BATCH_ATOMIC`  | `0x03`     | `0x00`     | multiple `SENDER` frames in an atomic batch  |

A transaction *presents* a mode for each of its `SENDER` frames: `callType` is `0x02` if the transaction has exactly one `SENDER` frame and `0x03` otherwise; `execType` is `0x00` if the frame belongs to an atomic batch and `0x01` otherwise. A transaction may present several modes at once — for example, an atomic group alongside an ungrouped frame presents both `FRAME_MODE_BATCH_ATOMIC` and `FRAME_MODE_BATCH`.

The account supports a transaction's `SENDER` frames only if `supportsExecutionMode` returns `true` for every presented mode.

These modes are declarations for `supportsExecutionMode` and the validation-time check in step 4 above; they are never passed to `execute`, and the new `callType` values define no `execute` dispatch behavior.

### Account Interface

A compliant account MUST implement the following interface. It extends ERC-7579's `IERC7579AccountConfig` and `IERC7579ModuleConfig`, so module installation and approval-mode capability are exposed through a single account interface alongside the EIP-8141 validation flow. It adds no execution interface; ERC-7579's applies unchanged where the account dispatches execution (see [Execution](#execution)):

```solidity
interface IERC8286FrameAccount is IERC7579AccountConfig, IERC7579ModuleConfig {
    /**
     * @dev Validates the active VERIFY frame and approves the transaction.
     *      Intended to be the call encoded in a VERIFY frame's `data`. The account
     *      selects an installed validator, obtains an approval mode from it, and masks
     *      that mode with the frame's allowed scope. It then checks the result against the
     *      approval modes it supports (`supportsApprovalMode`) and, when the execution
     *      bit is set, checks the transaction's SENDER frames against the execution modes
     *      it supports (`supportsExecutionMode`), before calling APPROVE with the result
     *      (see "EIP-8141 Frame Validator").
     * @param data validator selection and validator-specific calldata.
     * @return approvalMode the approval mode granted (also applied via APPROVE).
     *
     * MUST revert if the executing frame's mode is not VERIFY.
     * MUST NOT modify state other than via the APPROVE instruction; it executes within
     * the STATICCALL context of a VERIFY frame.
     * MUST NOT call APPROVE with a mode outside the frame's allowed scope.
     * MUST clear the execution bit if the transaction's SENDER frames present an
     * execution mode the account does not support (`supportsExecutionMode`, see
     * "SENDER Frame Execution Modes").
     * MUST NOT call APPROVE if validation fails (leaving the mode at APPROVE_NONE).
     */
    function verify(bytes calldata data) external returns (uint8 approvalMode);

    /**
     * @dev Returns whether the account supports a given approval mode.
     * @param approvalMode the approval mode (see "Approval Mode" above), a uint8
     *        bitmask in the range 0x0..0x3.
     *
     * MUST return true if the account is capable of granting this mode during
     * validation and false otherwise.
     */
    function supportsApprovalMode(uint8 approvalMode) external view returns (bool);
}
```

### Execution

An [EIP-8141](./eip-8141.md) frame transaction executes operations over two routes:

- **Protocol-dispatched**: a `SENDER` frame calls its target directly with `caller = tx.sender`. No account code takes part in the call, so the account has no execution-time enforcement point. Any policy over `SENDER` frames MUST be enforced at validation time, by the validator inspecting them before approving (see [Security Considerations](#security-considerations)).
- **Contract-dispatched**: `DEFAULT` frames are expected to target a dispatching contract — typically the `tx.sender` account — invoking ERC-7579's `execute` or `executeFromExecutor` with `caller = ENTRY_POINT`. The account's code performs the calls itself, and ERC-7579 execution semantics — execution modes, `supportsExecutionMode`, hooks — apply unchanged.

Note that this distinction is not enforced by the protocol and the `SENDER` frame's target may be set to `tx.sender` as well.

This standard therefore defines no new execution interface: ERC-7579 already covers contract-dispatched `DEFAULT` frame execution, and no interface can cover `SENDER` frames.

An account MAY omit the `execute` and `executeFromExecutor` functions, supporting only the protocol-dispatched route.

An account that accepts `execute` calls in frame context MUST NOT authorize them by `msg.sender == ENTRY_POINT` alone. In EIP-8141 the `ENTRY_POINT` address as caller carries no authorization — unlike an [ERC-4337](./eip-4337.md) EntryPoint, which calls the account only after validating its user operation. The account MUST authenticate the call itself, by confirming that its own `VERIFY` frame in this transaction has succeeded (`FRAMEPARAM(i, 0x05)`), or by applying its ordinary ERC-7579 access control.

## Rationale

### Extension to ERC-7579 rather than a standalone standard

The value of a modular account standard is a portable module ecosystem: a validator, executor, or hook written once should work across accounts and wallets. Defining an independent module system for EIP-8141 would fork that ecosystem, forcing modules and tooling to choose between account abstraction implementations. Instead, this standard reuses ERC-7579's module system unchanged and adds only the EIP-8141 validation flow.

This follows the upgrade path ERC-7579 describes for itself: as modular accounts are built on account abstraction implementations other than its original [ERC-4337](./eip-4337.md) target, the implementation-specific validation flow is moved into a separate, optional extension. This proposal is that extension for EIP-8141. The intended end state is ERC-7579 as the implementation-agnostic core module system, with per-implementation validation extensions layered on top.

### Approval mode as the validation result

ERC-7579 standardizes a compact encoding for the execution side (its execution mode), letting an account express execution behavior in a single value. EIP-8141 provides the protocol-native counterpart for the validation side: the `APPROVE` scope. This standard surfaces validation results as that scope so the account never has to translate between a foreign validation-data encoding and the protocol's own approval semantics. `supportsApprovalMode` complements ERC-7579's `supportsExecutionMode`: the former declares which approval scopes the account can grant, the latter which execution shapes it will honor.

### No new execution interface

ERC-7579's `execute` is the point where an account enforces execution policy: it decodes the mode, checks the call type against `supportsExecutionMode`, and reverts if unsupported. Under EIP-8141 that flow can be reused on the contract-dispatched route: a `DEFAULT` frame calls the account's `execute`, the account performs and checks the calls exactly as it would under [ERC-4337](./eip-4337.md).

On the protocol-dispatched route there is nothing to attach an interface to: the protocol calls a `SENDER` frame's target directly — no account code is involved in making the call — and the only gate is the transaction-scoped `sender_approved` flag set once by `APPROVE_EXECUTION`.

For `SENDER` frames the enforcement point therefore moves entirely to the validation frames: a validator inspects the transaction's `SENDER` frames during the `VERIFY` frame and only approves if they satisfy policy. `supportsExecutionMode` accordingly answers two kinds of query: for ordinary ERC-7579 modes it reports what the account's `execute` can process, and for the frame modes defined in "`SENDER` Frame Execution Modes" it declares which frame shapes the account's validators are willing to authorize — a policy enforced at validation time, not a capability checked at execution time.

#### Frame modes are policy declarations

The frame modes cover only the single/batch and atomic/non-atomic dimensions because the other ERC-7579 mode dimensions have no `SENDER`-frame counterpart: a `SENDER` frame is a plain protocol-orchestrated `CALL`, so there is no delegatecall analogue (account-level delegation exists only via [EIP-7702](./eip-7702.md) at the frame level), no staticcall analogue (the `STATICCALL` context in EIP-8141 is the `VERIFY` frame, not execution), and no room for custom `modeSelector`/`modePayload` semantics (the protocol owns dispatch).

The frame modes also describe a **policy** choice rather than a **capability**. In [ERC-4337](./eip-4337.md) an account must implement code to handle a batch, so `supportsExecutionMode` reports whether that code exists. Under EIP-8141 the protocol performs batching, atomicity, and dispatch with no account code, so every account is structurally capable of all of them; what an account or validator declares and enforces is which shapes it is *willing to authorize* (for example, refusing to approve a multi-frame transaction), not which it is able to perform.

### Validator Selection

One recommended approach for accounts to select the validator module, where [EIP-8250](./eip-8250.md) keyed nonces are available, is to derive the validator from the keyed nonce, for example by interpreting the validator's address as encoded in `TXPARAM(0x0B)` (`nonce_keys[0]`); because keyed nonces are committed by the canonical signing hash (`TXPARAM(0x08)`), this binds validator selection to the signed transaction.

## Security Considerations

### Execution is unconditional once approved

Granting `APPROVE_EXECUTION` (or `APPROVE_EXECUTION_AND_PAYMENT`) is a one-shot, transaction-wide authorization. After a `VERIFY` frame sets `sender_approved`, **every** subsequent `SENDER` frame in the transaction executes with `caller = tx.sender` and is not checked again. EIP-8141 consults no allow-list, target restriction, or execution-mode support on the account, and no account code mediates those calls. A validator that returns `APPROVE_EXECUTION` therefore authorizes arbitrary calls (arbitrary targets, values, and calldata) on the account's behalf.

Because EIP-8141 provides no execution-time fallback for `SENDER` frames, all execution policy MUST be enforced at validation time. This includes both the account's declared `supportsExecutionMode` set and any validator-level constraint (allow-list, spend limit, target restriction). Before granting `APPROVE_EXECUTION`, the account and its validator MUST inspect the transaction's `SENDER` frames (their resolved target, value, and calldata) and MUST NOT approve if any frame presents an unsupported execution mode or falls outside policy. Frame contents are available during validation through EIP-8141's introspection instructions (`FRAMEPARAM`, `FRAMEDATALOAD`, `FRAMEDATACOPY`); an account MAY surface them to the validator directly (see "EIP-8141 Frame Validator"). Validators SHOULD grant the narrowest sufficient approval mode rather than `APPROVE_EXECUTION_AND_PAYMENT` by default.

### `DEFAULT` frames and the `ENTRY_POINT` caller

An [EIP-8141](./eip-8141.md) `DEFAULT` frame executes its target with `caller = ENTRY_POINT` and requires no prior approval, so any frame transaction can invoke the account's code with that caller. EIP-8141 gates `APPROVE` on the executing frame's resolved target and flags, not on its mode; only a `VERIFY` frame, however, runs under `STATICCALL` restrictions and invalidates the whole transaction when it reverts. An account MUST NOT take `msg.sender == ENTRY_POINT` as evidence that it is executing in a `VERIFY` frame: it MUST check the frame's mode via `FRAMEPARAM` before approving, as required of `verify` (see "Account Interface"). Without that check, a `DEFAULT` frame targeting the account runs the validation flow without `STATICCALL` protection, and a failed validation reverts only that frame while the rest of the transaction proceeds.

The same caution applies to execution entrypoints: a `DEFAULT` frame can call the account's `execute` with `caller = ENTRY_POINT` in a transaction the account never validated, so `execute` MUST NOT be authorized by caller identity alone (see [Execution](#execution)).

### Validator trust

An installed validator is fully trusted with respect to the account: because approval is unconditional, a malicious or buggy validator that approves execution can drain or take over the account. Accounts MUST apply the same authorization control to installing a validator as to any other account-critical operation.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
