This proposal defines a new EIP-2718 transaction type and an onchain system contract that together provide account abstraction — batching, gas sponsorship, and authentication using any cryptographic system. Accounts configure signing keys with account-specified verifiers in the system contract; the protocol validates transactions natively for well-known algorithms, and via sandboxed pure-function contracts for any other scheme.
Motivation
Requiring code execution to validate transactions leads to wasted compute, denial-of-service vectors, and compensatory systems (tracing, staking, reputation). This proposal uses pure-function verifiers with known state inputs — a node can determine validity from state alone, and new signature schemes are deployed as EVM contracts without protocol changes.
This provides a native path to post-quantum secure authentication.
The system contract is accessible via the EVM, so wallet code can layer additional logic — recovery, multisig, spending limits, session keys — on top. The protocol handles authentication, gas payment, and calldata delivery to be interpreted by the account.
EOAs set delegation to a smart wallet implementation, then register keys
EOA key authorized by default; revocable
New Accounts (No EOA)
Created via account_initialization with CREATE2 address derivation; runtime bytecode placed at address, keys + verifiers configured, call execution handles initialization
Wallet-defined
Verifiers
Each key is associated with a verifier — a contract at a known address that performs signature verification. The verifier address is stored in key_config and identifies the signature algorithm. On 8130 chains, the protocol recognizes well-known verifier addresses and uses native implementations; unknown verifiers are executed in a sandboxed environment (see Sandbox Verifiers).
Well-Known Verifiers
Verifier
Address
Algorithm
Public Key Size
Signature Size
keyId Derivation
K1
K1_VERIFIER
secp256k1 (ECDSA)
33/65 bytes
65 bytes
keccak256(x \|\| y)[12:]
P256
P256_VERIFIER
secp256r1 / P-256 (raw)
33/65 bytes
64 bytes
keccak256(x \|\| y)[12:]
WebAuthn P256
WEBAUTHN_P256_VERIFIER
P-256 + WebAuthn framing
33/65 bytes
Variable
keccak256(x \|\| y)[12:]
BLS
BLS_VERIFIER
BLS12-381
48 bytes
96 bytes
keccak256(pubkey)[12:]
DELEGATE
DELEGATE_VERIFIER
Delegated validation
20 bytes (address)
Nested signature
address (direct)
All verifiers implement the same IAuthVerifier.verify() interface (see Verifier Contracts).
DELEGATE: Delegates validation to another account’s configuration. The keyId is the delegated account’s address directly — a transparent pointer. Only 1 hop is permitted (see DELEGATE).
K1 / DELEGATE mutual exclusion: Because K1 keyId is an Ethereum address, a K1 key and a DELEGATE entry resolve to the same keyId when they reference the same address. They occupy the same storage slot and are therefore mutually exclusive — an account can register a K1 key for address or a DELEGATE to account, but not both. K1 pins trust to a specific private key; DELEGATE trusts any key currently authorized on the target account.
Sandbox Verifiers
Any contract can serve as a verifier if its bytecode passes a sandbox validation scan — enabling permissionless addition of new signature algorithms without protocol changes. Sandbox verifiers must be pure functions: no state access, no external calls except allowlisted precompiles. Their bytecode includes a standardized header declaring a gas_limit, which is charged as intrinsic gas. See Appendix: Sandbox Verifier Bytecode for the header format and opcode rules.
Account Configuration
Each account can authorize a set of keys through the Account Configuration Contract at ACCOUNT_CONFIG_ADDRESS. This contract handles key storage, account creation, key change sequencing, and delegates signature verification to onchain Verifier Contracts.
Keys are identified by their keyId, a 20-byte content-derived identifier (see Verifiers for per-type derivation). Only msg.sender can modify their own key configuration within EVM execution. Keys can also be modified via portable key changes (see Cross-Chain Key Changes).
Default behavior: The EOA key is implicitly authorized by default but can be disabled by revoking it on the contract.
Storage Layout
Each key occupies a key_config slot (packed: verifier address, key_policy bitfield with revoked and requireSponsor flags) plus additional slots for the public key. A per-chain key change sequence counter supports Cross-Chain Key Changes. See Appendix: Storage Layout for the full slot derivation.
The protocol validates signatures by reading key_config and public key slots directly — see Validation for the full flow. Key enumeration is performed off-chain via KeyAuthorized / KeyRevoked event logs. No key count is enforced on-chain — gas costs naturally bound key creation.
2D Nonce Storage
Nonce state is managed by a precompile at NONCE_MANAGER_ADDRESS, isolating high-frequency nonce writes from the Account Configuration Contract’s key storage (see Why a Nonce Precompile?). The protocol reads and increments nonce slots directly during AA transaction processing; the precompile exposes a read-only getNonce() interface to the EVM. See Appendix: Storage Layout for slot derivation.
Verifier Contracts
Each key’s verifier address in key_config determines which contract performs verification. All verifiers implement IAuthVerifier.verify() (see Reference Implementation). The Account Configuration Contract calls verifiers directly via staticcall for applyKeyChange() authorizer verification and EVM-initiated signature checks.
On 8130 chains, the protocol does not call verifier contracts for AA transaction validation — it reads key storage directly and uses native implementations for well-known verifiers or sandbox execution for unknown ones. Verifier contracts serve EVM callers only.
The DELEGATE_VERIFIER is an exception to the pure function model — it reads the delegated account’s key data from the Account Configuration Contract and chains to the nested verifier, enforcing a 1-hop limit.
Sending account address. Required (non-empty) for configured key signatures. Empty for EOA signatures—address recovered via ecrecover. The presence or absence of from is the sole distinguisher between EOA and configured key signatures.
nonce_key
2D nonce channel key (uint192) for parallel transaction processing
nonce_sequence
Must equal current sequence for (from, nonce_key). Incremented after inclusion regardless of execution outcome
expiry
Unix timestamp (seconds since epoch). Transaction invalid when block.timestamp > expiry. A value of 0 means no expiry
Empty: No key changes. Non-empty: Array of signed key change operations. See Cross-Chain Key Changes
committed_calldata
Empty: No committed call. Non-empty: Calldata (bytes) delivered to from via ENTRY_POINT_ADDRESS. Committed independently — its state changes persist even if calldata execution reverts. See Call Execution
calldata
Empty: No call. Non-empty: Calldata (bytes) delivered to from via ENTRY_POINT_ADDRESS. If execution reverts, its state changes are discarded. See Call Execution
All types read key_config (1 SLOAD) for authorization, policy checks, and verifier address. Non-K1 types require additional SLOADs to read the public key.
payer_cost: Determined by the payer mode (see Payer Modes):
Payer Mode
Gas
Rationale
Self-pay
0
key_policy.requireSponsor checked from sender validation SLOAD (no additional read)
Delegate payer
2,100
1 cold SLOAD (delegate key verification)
K1 sponsor
3,000
ecrecover (no storage read)
Verifier sponsor
sender_key_cost for payer’s verifier
Reads payer’s key_config + public key, runs verification
Component
Value
tx_payload_cost
Standard per-byte cost over the entire RLP-serialized transaction: 16 gas per non-zero byte, 4 gas per zero byte, consistent with EIP-2028. Ensures all transaction fields (account_initialization, key_changes, authorization_list, sender_auth, committed_calldata, calldata, etc.) are charged for data availability
nonce_key_cost
22,100 gas for first use of a nonce_key (cold SLOAD + SSTORE set), 5,000 gas for existing keys (cold SLOAD + warm SSTORE reset)
bytecode_cost
0 if account_initialization empty. Otherwise: 32,000 (deployment base) + code deposit cost (200 gas per deployed byte). Byte costs for bytecode are covered by tx_payload_cost
key_changes_cost
Per applied entry: authorizer signature verification cost (based on authorizer’s verifier, using the sender_key_cost table above) + num_operations × 20,000 per SSTORE for key struct slots. Per skipped entry (already applied): 2,100 (SLOAD to check sequence). 0 if key_changes empty
Signature Format
Signature format is determined by the from field:
EOA signature (from empty): Raw 65-byte ECDSA signature (r || s || v). The sender address is recovered via ecrecover. This is the only format that uses key recovery.
All configured key signatures begin with keyId (20 bytes). The protocol reads key_config for the keyId, which yields the verifier address and determines how to parse the remaining signature_data. No auth type byte is needed — the verifier address in storage defines the algorithm.
Validation
Parse keyId: First 20 bytes of configured key signature. For EOA (from empty): ecrecover derives the sender address directly — skip remaining steps.
Read key_config for (from, keyId) (1 SLOAD): yields verifier and key_policy. Verify authorized — non-zero verifier + not revoked. For keyId == from with empty slot: EOA default (valid with K1_VERIFIER); revoked set = blocked.
Read public key from storage. Verify signature: for well-known verifiers, use native implementation; for unknown verifiers, sandbox execution with declared gas limit. Reject if verification fails.
DELEGATE
For DELEGATE_VERIFIER, the protocol reads the delegated account’s address from the publicKey field (stored as 20 bytes), then parses nested_signature as nested_keyId (20) || nested_sig_data. It reads the nested key’s key_config from the delegated account and validates using the nested verifier. Only 1 hop is permitted; nested DELEGATE_VERIFIER results in an immediate mempool drop.
Example (Account B delegates to Account A, which has a P256 key):
Delegate payer: The payer has a DELEGATE key (verifier == DELEGATE_VERIFIER) for keyId == from, granting standing authorization for the sender to use their ETH for gas. The on-chain DELEGATE key replaces a per-transaction payer signature. The sender always commits to a specific payer_address (no open delegate mode).
Verifier sponsor: The payer authenticates using any key registered on their own Account Configuration. The protocol reads the payer’s key_config for the given keyId and verifies via the stored verifier — same flow as sender validation.
Combined with per-key requireSponsor (see Storage Layout), this enables a “gas piggy bank” pattern: keys that cannot self-pay draw gas from a dedicated funding account. One gas account can fund multiple accounts by registering a DELEGATE key for each, fully isolating gas spend from the accounts’ own ETH balances.
Account Initialization
New smart contract accounts can be created with pre-configured keys in a single transaction using the account_initialization field. The bytecode is the runtime code placed directly at the account address — it is not executed during deployment. The account’s initialization logic runs when calldata is delivered to the account via the execution phase that follows:
account_initialization = rlp([
user_salt, // bytes32: User-chosen uniqueness factor
bytecode, // bytes: Runtime bytecode placed directly at the account address
initial_keys // Array of [verifier, public_key] pairs
])
Address Derivation
Addresses are derived using the CREATE2 address formula with the Account Configuration Contract (ACCOUNT_CONFIG_ADDRESS) as the deployer. The initial_keys are sorted by keyId before hashing to ensure address derivation is order-independent (the same set of keys always produces the same address regardless of the order specified):
The keys_commitment uses keyId || verifier (40 bytes) per key — consistent with how the Account Configuration Contract identifies keys.
DEPLOYMENT_HEADER(n) is a fixed 14-byte EVM loader that returns the trailing bytecode (see Appendix: Deployment Header for the full opcode sequence). On non-8130 chains, createAccount() constructs deployment_code and passes it as init_code to CREATE2. On 8130 chains, the protocol constructs the same deployment_code for address derivation but places bytecode directly (no execution). Both paths produce the same address — callers only provide bytecode; the header is never user-facing.
Users can receive funds at counterfactual addresses before account creation.
Validation (Account Initialization)
When account_initialization is non-empty:
Parse [user_salt, bytecode, initial_keys]
For each key in initial_keys, derive keyId. For compressed public keys, decompress to the uncompressed point before derivation.
Reject if any duplicate keyId values exist
Sort by keyId: sorted_keys = sort(initial_keys, by: keyId)
Key registration and key changes are applied before code placement so that the account’s initialization logic (executed via call execution) can read its own key configuration from the Account Config contract.
Cross-Chain Key Changes
The key_changes field enables cross-chain portable key management. Key change operations include a chain_id field where 0 means valid on any chain, allowing them to be replayed across chains to synchronize key state.
Key Change Format
key_changes = [
rlp([
chain_id, // uint64: 0 = valid on any chain
sequence, // uint64: monotonic ordering
operations, // Array of key operations
authorizer_auth // Signature from a key valid at this sequence
]),
... // max MAX_KEY_CHANGES entries per transaction
]
operation = rlp([
op_type, // 0x01 = authorizeKey, 0x02 = revokeKey
verifier, // address: verifier contract address
public_key // bytes: public key material
])
Each entry represents a set of key operations authorized at a specific sequence number. The authorizer_auth must be valid against the account’s key configuration at the point after all previous entries in the list have been applied.
The sequence number is scoped to a 2D channel defined by the chain_id to prevent cross-chain sequence de-sync. A chain_id of 0 uses the multichain sequence channel, while a specific chain_id uses that chain’s local sequence channel.
Key Change Signature Payload
Key change signatures use domain separation via KEY_CHANGE_TYPE. The chain_id field controls replay scope: 0 means the key change is valid on any chain, otherwise it is only valid on the specified chain.
The authorizer_auth follows the same Signature Format as sender_auth (EOA or configured key), validated against the account’s key state at that point in the sequence.
Key Change Paths
Keys can be modified through three paths:
Portable: key_changes (tx field)
Portable: applyKeyChange() (EVM)
Local: authorizeKey() / revokeKey() (EVM)
Authorization
Signed operation (any verifier)
Signed operation (any verifier)
msg.sender during execution
Portability
Cross-chain (chain_id 0) or chain-specific
Cross-chain (chain_id 0) or chain-specific
Chain-local only
Sequence
Increments channel’s key_change_sequence
Increments channel’s key_change_sequence
Does NOT affect key_change_sequence
When processed
Before code deployment (8130 only)
During EVM execution (any chain)
During EVM execution
Both portable paths share the same signed operations and key_change_sequence counters. applyKeyChange() verifies the authorizer via IAuthVerifier(verifier).verify(...) — anyone (including relayers) can call it; authorization comes from the cryptographic signature, not the caller.
Local key changes (authorizeKey() / revokeKey()) require msg.sender and do not increment key_change_sequence. Portable key changes may overwrite local changes at the same keyIds.
Execution
Call Execution
Both committed_calldata and calldata are delivered to from as individual calls:
Parameter
Value
to
from
tx.origin
from
msg.sender
ENTRY_POINT_ADDRESS
msg.value
0
data
committed_calldata or calldata
Execution proceeds in two phases:
Both phases share a single gas pool from gas_limit. committed_calldata executes first; calldata receives the remainder.
Committed phase: If committed_calldata is non-empty, execute a call to from. This call is committed independently — its state changes persist regardless of whether the calldata phase succeeds or reverts. If committed_calldata reverts, its state changes are discarded and execution continues to the next phase. For accounts without code, the call succeeds with no effect.
Calldata phase: If calldata is non-empty, execute a call to from. If execution reverts, its state changes are discarded. For accounts without code, the call succeeds with no effect.
The wallet fully interprets both payloads — batching, multicall, or any other execution pattern is handled by the wallet’s code, not the protocol. This two-phase design enables robust token gas payments: the wallet transfers tokens to a sponsor in committed_calldata, while the user’s operations execute in calldata. The sponsor’s payment survives even if calldata reverts.
Transaction Context
During AA transaction execution, accounts can query the Account Configuration Contract for the current transaction’s authorization context:
Payer: getCurrentPayer() returns the gas payer address (from for self-pay, recovered payer for sponsored)
Signer: getCurrentSigner() returns (keyId, keyConfig, publicKey) of the key that authorized the transaction
The protocol injects this context using EIP-1153 transient storage (TSTORE) on the Account Configuration Contract before call execution. Only two values are written:
Slot
Value
Size
keccak256("context.payer")
Payer address
20 bytes
keccak256("context.signer")
Signer keyId
20 bytes
getCurrentPayer() reads payer via TLOAD. getCurrentSigner() reads keyId via TLOAD, then looks up (keyConfig, publicKey) from persistent key storage. Transient storage is automatically cleared at transaction end.
Non-8130 chains: These functions return zero/default values since no protocol writes to transient storage.
Portability
The system is split into storage and verification layers with different portability characteristics:
Component
8130 chains
Non-8130 chains
Account Configuration Contract
Protocol reads storage directly for validation; EVM interface available
Standard contract (ERC-4337 compatible factory)
Verifier Contracts
Protocol uses native implementations for well-known verifiers; sandbox for unknown
Same onchain contracts callable by account config contract and wallets
Nonce Manager
Precompile at NONCE_MANAGER_ADDRESS
Not applicable; nonce management by existing systems (e.g., ERC-4337 EntryPoint)
The Account Configuration Contract is identical Solidity bytecode on every chain (deployed via CREATE2). Verifier contracts are also deployed at deterministic addresses. See Why Verifier Contracts? for the design rationale.
Resolve sender: if from set, use it; if empty, ecrecover from sender_auth
Determine effective key state:
a. If account_initialization non-empty: verify address derivation, code_size(from) == 0, use initial_keys
b. Else: read from Account Config storage
If key_changes non-empty: simulate applying operations in sequence, skip already-applied entries
Validate sender_auth against resulting key state (see Validation)
Resolve payer from payer_auth:
Empty: reject if key_policy.requireSponsor set. Payer is from.
20 bytes: Delegate payer. Verify payer has DELEGATE key for sender. Payer is payer_address.
0x01 prefix: K1 sponsor. Recover payer via ecrecover over the trailing 65-byte signature. Verify sender signed <payer_address> or 0x00.
0x02 prefix: Verifier sponsor. Parse payer_address || keyId || sig_data. Read payer’s key_config, verify via stored verifier. Verify sender signed <payer_address> or 0x00.
Register initial_keys in Account Config storage (if account_initialization non-empty)
Apply key_changes to Account Config storage (if non-empty)
Place bytecode at from (if account_initialization non-empty; code is placed directly, not executed)
Write transaction context to Account Config transient storage via TSTORE (payer address, signer keyId)
Execute committed_calldata via ENTRY_POINT_ADDRESS if non-empty (committed independently)
Execute calldata via ENTRY_POINT_ADDRESS if non-empty
Steps 1–7 are protocol-level direct state operations with no EVM execution. Steps 8–9 are the EVM execution phase. Following the precedent of EIP-7702, the protocol-level state changes applied in steps 1–7 MUST NOT be reverted if the EVM execution in steps 8–9 reverts. committed_calldata in step 8 is committed independently — its state changes MUST NOT be reverted if step 9 reverts. For steps 4–6, the protocol SHOULD inject log entries into the transaction receipt (e.g., KeyAuthorized, AccountCreated) matching the events defined in the IAccountConfig interface, following the protocol-injected log pattern established by EIP-7708. This ensures indexers observe the same events regardless of whether keys are registered via protocol (8130) or via EVM calls (non-8130 chains).
RPC Extensions
eth_getTransactionCount: Extended with optional nonceKey parameter (uint192) to query 2D nonce channels. Reads from the Nonce Manager precompile at NONCE_MANAGER_ADDRESS.
eth_getTransactionReceipt: Should include a payer field with the gas payer address.
Allowed opcodes: Stack operations, arithmetic, bitwise, KECCAK256, memory operations, CALLDATALOAD/SIZE/COPY, RETURN, REVERT, jumps, and STATICCALL to allowlisted precompile addresses only.
Forbidden opcodes: CALL, DELEGATECALL, CALLCODE, SLOAD, SSTORE, TLOAD, TSTORE, all external state reads (BALANCE, EXTCODESIZE, etc.), CREATE, CREATE2, SELFDESTRUCT, LOG0–LOG4.
STATICCALL is allowed in bytecode but runtime-filtered: the target address must be an allowlisted precompile. This enables verifiers to use existing precompiles (modexp, SHA-256, ecrecover, etc.) as building blocks while maintaining the pure function guarantee.
Appendix: Deployment Header
The DEPLOYMENT_HEADER(n) is a 14-byte EVM loader that copies trailing bytecode into memory and returns it. The header encodes bytecode length n into its PUSH2 instructions:
DEPLOYMENT_HEADER(n) = [
0x61, (n >> 8) & 0xFF, n & 0xFF, // PUSH2 n (bytecode length)
0x60, 0x0E, // PUSH1 14 (offset: bytecode starts after 14-byte header)
0x60, 0x00, // PUSH1 0 (memory destination)
0x39, // CODECOPY (copy bytecode from code[14..] to memory[0..])
0x61, (n >> 8) & 0xFF, n & 0xFF, // PUSH2 n (bytecode length)
0x60, 0x00, // PUSH1 0 (memory offset)
0xF3 // RETURN (return bytecode from memory)
]
Rationale
Why Verifier Contracts?
Key storage and signature verification have fundamentally different upgrade characteristics. Storage is mechanical — (verifier, public_key) in deterministic slots works on any EVM chain. But if verification logic were bundled into the storage contract, it would freeze at deploy time with no permissionless upgrade path.
Separating them means new algorithms are deployed as new verifier contracts without touching the storage layer. The uniform IAuthVerifier.verify() interface eliminates registration, wallet callbacks, and special casing — the verifier address in key_config is the sole identifier for authentication logic. The storage contract is pure Solidity (SLOAD/SSTORE-dominated, no gas benefit from a precompile).
Storage layout is consensus-critical: Because the protocol reads storage slots directly on 8130 chains, the keccak-derived slot layout becomes a consensus rule. The layout is intentionally simple (keccak-derived, fixed-size slots per key) to minimize the likelihood of future changes — the same commitment that EIP-7702 makes with its delegation designator format.
Why a Nonce Precompile?
Nonce state is isolated in a dedicated precompile (NONCE_MANAGER_ADDRESS) rather than stored alongside key configurations in the Account Configuration Contract. This separation is motivated by their fundamentally different access patterns and portability requirements:
Property
Key Config
Nonces
Write frequency
Rare (key rotation)
Every AA transaction
Read frequency
Every validation
Every validation
Growth
Rare (gas-bounded)
Unbounded (nonce channels)
EVM writes
Yes (authorizeKey, revokeKey, etc.)
No (protocol-only increments)
Portability
Required (for non 8130 chains)
Not required (8130-only)
Why a precompile instead of a system contract? Unlike the Account Configuration Contract — which must be a full Solidity contract for cross-chain portability and EVM-writable key management — the Nonce Manager has no EVM-writable state and no portability requirement. Nonce increments are exclusively protocol-level operations.
Modularity: The precompile is minimal — a single read function backed by protocol-managed storage. This clean separation means nonce logic can evolve independently, and the precompile could potentially be reused by other transaction types or systems.
Why CREATE2 for Account Initialization?
Account initialization uses the CREATE2 address formula with ACCOUNT_CONFIG_ADDRESS as the deployer address for cross-chain portability:
Deterministic addresses: Same user_salt + bytecode + initial_keys produces the same address on any chain
Pre-deployment funding: Users can receive funds at counterfactual addresses before account creation
Portability: Same deployment_code produces the same address on both 8130 and non-8130 chains (see Address Derivation)
Front-running prevention: initial_keys in the salt prevents attackers from deploying with different keys (see Account Initialization)
Smart Wallet Migration Path
Existing ERC-4337 smart accounts migrate to native AA without redeployment:
Register keys: Call authorizeKey() on the Account Configuration Contract to authorize existing signing keys (K1, P256, etc.)
Upgrade wallet logic: Update contract to delegate isValidSignature to the appropriate verifier contract, and call getCurrentSigner() during execution to identify which key authorized the transaction
Backwards compatible: Wallet can still accept ERC-4337 UserOps via EntryPoint alongside native AA transactions
Why Verifier Addresses Instead of Auth Type Bytes?
Earlier designs used a 1-byte auth_type to identify signature algorithms. This created a protocol-managed registry of algorithms that required hard forks to extend. The verifier address model replaces this with a permissionless system:
Extensibility: New algorithms are deployed as verifier contracts — permissionless, no protocol change needed.
Uniformity: All configured key signatures use the same keyId || signature_data format. The verifier address in storage determines parsing.
Consistency: The Account Configuration Contract calls all verifiers through IAuthVerifier.verify(), whether ecrecover, P256, BLS, or a post-quantum scheme.
The two-phase split guarantees sponsor payment survives regardless of what happens in calldata. This enables robust permissioned sponsorship in exchange for gas payments without protocol-level token awareness. The wallet fully interprets both payloads — batching, multicall, or any other execution pattern is the wallet’s responsibility, not the protocol’s.
Why Key Policy?
The key_config slot packs verifier (20 bytes) and key_policy (1 byte) into the same 32-byte storage slot, read in a single SLOAD during sender validation — zero additional storage cost for policy enforcement.
The revoked flag enables cross-chain portable EOA key revocation via the existing revokeKey key change mechanism. The requireSponsor flag provides protocol-enforced session key semantics — a key that can authenticate but cannot access the account’s ETH for gas. This restriction is enforced at the protocol layer before any EVM execution. Wallet code can layer additional restrictions (allowed targets, spending caps) by checking getCurrentSigner() during execution, but the ETH-access guarantee comes from the protocol. This limits the blast radius of a compromised session key to whatever the wallet permits, never the account’s raw ETH balance.
Future Signature Algorithms
The verifier model is designed for permissionless extensibility. New algorithms (post-quantum schemes like ML-DSA, ZK-proof-based auth, exotic curves) are deployed as sandbox verifier contracts — no protocol change required. Because sandbox verifiers are pure functions with declared gas limits, they fit the protocol’s validation model without reintroducing arbitrary EVM execution. Well-known verifiers (K1, P256, BLS) use native implementations for optimal gas; any future algorithm that becomes widely adopted can be promoted to a well-known verifier via protocol upgrade.
Backwards Compatibility
No breaking changes. Existing EOAs and smart contracts function unchanged. Adoption is opt-in:
EOAs continue sending standard transactions
ERC-4337 infrastructure continues operating
Accounts gain AA capabilities by configuring keys and deploying code (via EIP-7702 or account_initialization) to interpret calldata delivered by the protocol
Read-only. The protocol manages nonce storage directly; there are no state-modifying functions.
Security Considerations
Enshrined Validation: Well-known verifiers use native implementations before any EVM execution. Sandbox verifiers execute as pure functions with declared gas limits and no state access — deterministic results eliminate invalidation-based DoS without requiring a reputation system. Failed validation rejects transactions before mempool entry.
Replay Protection: Transactions include chain_id, 2D nonce, and expiry.
Key Management: Only msg.sender can modify keys via local EVM calls; portable key changes require signature authorization. EOA key is implicitly authorized by default; revocable via revokeKey (cross-chain portable). Accounts SHOULD have at least one configured key before revoking the EOA key. The requireSponsor key policy limits session key blast radius by preventing gas access. MAX_KEY_CHANGES bounds per-transaction processing cost; KEY_CHANGE_TYPE domain separation prevents authorizer signatures from being reused as transaction signatures.
Delegation: See DELEGATE for hop limits and nested signature rules. The DELEGATE verifier enforces a 1-hop limit at both protocol and contract level.
Payer Security: AA_TX_TYPE vs AA_PAYER_TYPE domain separation prevents signature reuse between sender and payer roles. The sender’s signed marker commits to a payer mode. Revoking a DELEGATE key immediately invalidates all pending transactions using that payer.
Signature Size Limits: Signatures exceeding MAX_SIGNATURE_SIZE MUST be rejected to prevent DoS via oversized signatures.
Account Initialization Security: initial_keys are salt-committed, preventing front-running. Permissionless deployment via createAccount() is safe — even if front-run, the account is created with the owner’s keys. Wallet bytecode should be inert when uninitialized.