We are excited to share the following proposal for introducing the next generation of authentication features to the Monad protocol: accounts that can add, rotate and retire authenticators in place, without changing their address. This enables post-quantum accounts, social recovery, and user-friendly schemes such as passkeys natively.
We welcome discussion and feedback from the community.
Flexible and Upgradeable Account Authentication
Status: Draft (v1, design and architecture, concrete implementation spec deferred)
Authors: Kushal Babel, Jan Camenisch
Contents
- Abstract
- Motivation
- Goals and Scope
- Design Constraints
- Account Model
- Verification Model
- Address Derivation
- Configuration Supply and Transaction Shape
- Reconfiguration, Rotation and Recovery
- Account Lookup
- Components Touched
- Backward Compatibility
1. Abstract
Separate an account’s authentication from how its address is derived. An account holds an authentication configuration (a set of authenticators and a policy over them) stored in account state and mutable over the account’s lifetime. The address is fixed at creation and does not change when authenticators are added, removed, rotated, or upgraded.
This gives recovery from a lost or compromised key without moving assets, in-place scheme upgrade (including post-quantum), and protocol-enforced multi-factor and scoped-access policies. Existing accounts and transactions are unaffected; addresses stay 20 bytes.
2. Motivation
Today an account identifier is the last 20 bytes of the hash of a secp256k1 public key. Binding the identifier to one key and one scheme means:
- compromise of the key compromises every asset under the account;
- a broken scheme exposes every account using it;
- a lost key makes the account unrecoverable; and
- security cannot be upgraded in place: it is weakest at creation, with no path to strengthen it without abandoning the address.
Multi-factor, staged, and scoped authentication is therefore pushed off-chain or into smart-contract accounts (bundlers, paymasters, per-wallet code).
3. Goals and Scope
3.1 In scope
- Separate the authentication mechanism from address derivation.
- Support multiple authenticators per account, with add and remove.
- Support in-place upgrade of authenticators, including post-quantum schemes.
- Support a minimal declarative policy over authenticators (including threshold).
- Preserve existing types for accounts, addresses and transactions.
3.2 Out of scope
- Stateful policies such as per-period spending limits applicable to specific authenticators.
- User-supplied custom authentication mechanisms (except for the policy they express).
- Automatic expiry or time-to-live for keys or policies.
4. Design Constraints
These are why the design does not simply adopt an existing proposal from EIPs like frame transactions (EIP-8141) or EIP-8130.
- Want simplicity, frame transactions are not simple.
- Authentication must be decidable cheaply at inclusion time (before EVM execution) and must not read or write shared state which would serialise parallel transactions. Reading only the sender’s own record is fine.
- EVM backward compatibility. Addresses stay 20 bytes; CALL/CREATE/CREATE2 and tooling unchanged. A no-config account behaves as a legacy EOA. The configuration adds one field to the account record (§5.2): a state-trie change. Since the config has public keys in it, and the address is a hash of it, nobody can squat on your account address.
4.1 Relation to Account Abstraction
This proposal deliberately abstracts only the authentication layer, and unlike account abstraction, is not concerned with bringing programmability to EOAs. In that sense, this proposal is complementary to account abstraction mechanisms like EIP-7702, which still cannot retire the underlying ECDSA key.
This allows the design to be relatively simple, and efficient, and provide a uniform, efficient authentication layer rather than relying on fragmented auth implementations inside individual smart contracts.
5. Account Model
5.1 Authentication configuration
An account’s authentication is a record in account state:
AuthConfig {
version,
next_id, // monotonic; authenticator ids are assigned from here
config_version, // count of applied reconfigurations
authenticators: [ { id, scheme_id, pubkey_or_params }, … ],
signing_policy: <policy>, // may sign transactions
reconfiguration_policy: <policy>, // may change the configuration
}
Each authenticator names a scheme_id (initially: ECDSA on secp256k1, ECDSA on P-256, webauthn on P-256 (passkeys), EDDSA on Ed25519, ML-DSA (a post-quantum scheme), and a ZK-OAuth verifier) and carries that scheme’s public key or parameters; for post-quantum security of a non-PQ scheme, an authenticator may instead store a hash of the public key. A policy is either a single authenticator or a threshold over (recursive) sub-policies: policy := ID(i) | THRESHOLD(k, [p1, ..., pn]); a leaf ID(i) holds when authenticator i signs, and THRESHOLD(k, […]) holds when at least k of the listed sub-policies are satisfied. OR is THRESHOLD(1, …), AND is THRESHOLD(n, …), and k-of-n is the general form. The policy expresses how many and which authenticators must sign. An account with no explicit AuthConfig (a legacy EOA account) is treated as a single implicit secp256k1 authenticator under a default signing and reconfiguration policy that permits the public key to sign and reconfigure for the account.
For Monad’s deferred execution, the account record also holds at most one pending configuration together with its activation block.
5.2 Storage
The account record gains a field, auth_config_root, alongside nonce, balance, storage_root, code_hash. An empty value denotes a legacy account. The configuration is per-account state: reads and writes during verification touch only the sender’s subtree and do not contend with other accounts.
6. Verification Model
As signature schemes are few and slow-moving, the protocol ships a roster of verification algorithms, each having a fixed-gas cost. An authenticator refers to one of the verification algorithms. Adding a scheme requires a fork / governance.
Policy is declarative data stored in the configuration; the protocol reads the declared rule and evaluates it. Additionally, the result of evaluation (which authenticators signed, i.e. the authentication level) could be exposed to execution via a new opcode (an execution-environment value), so a called contract can read it and branch on how the transaction was authenticated.
A transaction carries one or more (authenticator_id, signature / proofs) pairs; each signature is verified against its authenticator, and the valid ids form the satisfied set. The policy is evaluated over that set: THRESHOLD(k, S) holds when the satisfied set intersects S in at least k elements. Each scheme has a fixed gas cost, and a transaction pays the sum over the authenticators it supplies. The policy evaluated for a transaction is always the signing policy; the reconfiguration policy is evaluated only inside the AuthConfigManager precompile (§9.1).
7. Address Derivation
7.1 Existing accounts
An account with no configuration authenticates as today: recover the secp256k1 public key from the signature and require the recovered address to equal the account address. To upgrade, the AuthConfigManager precompile is called with the new configuration (§9.1). The address does not change.
7.2 New accounts
The address is a hash of the genesis configuration: address = keccak256(domain_sep ‖ version ‖ genesis_config)[-20:]. Similar to the CREATE2 pattern. The address is fixed at creation and is recomputable by anyone who knows the genesis configuration, so an account can be funded before it is first used. The live configuration may then diverge from the genesis configuration through rotation (§9) without changing the address. New accounts can be activated either by sending the genesis config through the new transaction type (see §8.3), or by calling the AuthConfigManager precompile with the genesis config (possibly via a relayer’s transaction).
8. Configuration Supply and Transaction Shape
8.1 Configuration storage
The config is stored in the account record, and is bound to the address only at genesis. The config is mutable thereafter.
8.2 Supplying the configuration
The chain cannot know the preimage of the address until it is revealed, so the genesis configuration is supplied exactly once and then stored. The only exception is a legacy account, which has no stored configuration and is treated as the implicit default (§5.1). Every new account supplies its genesis configuration once, in an optional init_config field, the hash of which is checked against the address, and it is then stored. Later transactions omit it.
8.3 New transaction type
A new transaction type that includes from address (not present currently in EVM transactions), auth_data (may include signatures and public keys / proofs) instead of the “ECDSA v,r,s values”, and optional init_config for the first transaction that can install config and perform a call atomically. Config need not be supplied again per transaction.
The envelope carries three authentication fields. auth_data is a single list of (authenticator_id, signature / proofs) entries over sig_hash, and authenticates the transaction against the signing policy. init_config, present only in the first transaction of a new account, is rlp([genesis_config, pop_entries]); sig_hash covers genesis_config and excludes pop_entries, which are signatures themselves. authorization_list carries generalized EIP-7702 tuples (authorized with signing policy of the account). All changes to an existing configuration go through the AuthConfigManager precompile (§9.1). The transaction consumes the account nonce like any other, so replay protection and ordering come for free.
9. Reconfiguration, Rotation and Recovery
9.1 Reconfiguration
Changes to the authenticator set, the signing policy, or the reconfiguration policy are authorized by satisfying the current reconfiguration policy, expressed in this same language. Installing a new policy additionally requires a proof of possession of every authenticator being installed. A configuration that cannot be satisfied therefore can never be installed (aka account is not bricked accidentally), and the change takes effect k blocks later, where k is Monad’s execution delay gap (3 today). The reconfiguration policy governs changes to itself in the same way.
Example. A spending key and a passkey, recoverable by two guardians.
- authenticators: { id 0: secp256k1, main key }, { id 1: P-256, passkey }, { id 2: secp256k1, guardian1 }, { id 3: secp256k1, guardian2 }.
- signing_policy = THRESHOLD(1, [ID(0), ID(1)]): the main key or the passkey may sign.
- reconfiguration_policy = THRESHOLD(1, [ID(0), THRESHOLD(2, [ID(2), ID(3)])]): the main key alone, or guardian1 and guardian2 together, may change the configuration.
To rotate the spending key, the holder sends a reconfiguration satisfying the reconfiguration policy with ID(0) and a proof of possession of the new key (e.g., signature). If the main key is lost, guardian1 and guardian2 together install a fresh key; hence the reconfiguration mechanism also allows for recovery inherently.
AuthConfigManager precompile
All changes to an existing configuration are made by calling the precompile, from any transaction type and by any sender. The precompile checks that the calldata satisfies the reconfiguration policy of the specified target account, carries proofs of possession, and then installs the config while incrementing the config_version to prevent replay. The same precompile also activates a new account: the calldata carries the genesis configuration instead of a change, and is authorized by the address hash check together with the proofs of possession rather than by a reconfiguration policy.
9.2 Rotation and Upgrade
Rotation changes which keys control an account while keeping the address.
- Rotate: replace an authenticator’s key, or add a new one and remove the old.
- Upgrade scheme: add an authenticator under a new scheme (e.g. a post-quantum scheme), then retire the old one. The account is post-quantum in place; the address is unchanged.
- Convert to multisig: add authenticators and change the policy from a single-signer threshold to a k-of-n threshold.
Because the address commits only to the genesis configuration, after any rotation the address no longer equals the hash of the live configuration. The address stays a stable identifier even after the live keys have changed.
9.3 Recovery
Recovery itself is just a clause of the reconfiguration policy (see the example above).
10. Account Lookup
Two directions:
10.1 Address to configuration
This is all the protocol needs and it is unaffected by rotation. A transaction names its sender; the account record is keyed by the address; the protocol reads the live configuration directly. The chain does not derive an address from a key.
10.2 Key to address
“Given a credential, which account does it control?” matters to wallets (the protocol never needs it), and rotation breaks it: the current key no longer hashes to the address. It matters on device restore, for guardians, and for credential-based discovery. We adopt an on-chain index:
| Mechanism | Description | Implications |
|---|---|---|
| On-chain index | Maintain key → address in state, updated on every rotation (the Aptos OriginatingAddress approach). The value is a set of addresses, since a shared guardian key legitimately controls several accounts. | Trustless and universal. Costs state; needs a rule for keys controlling several accounts; publishes which key controls which account (a privacy cost); stale entries must be deleted on rotation. |
11. Components Touched
Mempool, account state trie, new tx type, consensus (validation before voting).
Execution gains the AuthConfigManager precompile and the pending-configuration state it keeps.
12. Backward Compatibility
| Account | Transaction type | Result |
|---|---|---|
| No configuration | Existing (0, 1, 2) | Unaffected |
| No configuration | New | Valid immediately under the implicit configuration: one secp256k1 authenticator at id 0, both policies ID(0). |
| Configured | New | Full policy evaluation |
| Configured | Existing (0, 1, 2) | Valid if and only if the recovered ECDSA key alone satisfies the effective signing policy. |
12.1 Upgrade using existing wallet software
The first upgrade of a legacy account is an ordinary transaction to the AuthConfigManager precompile. No extra signature is needed to satisfy the reconfiguration policy, because the legacy account’s implicit reconfiguration policy is its ECDSA key, and the transaction’s own signature already satisfies it.
12.2 EIP-7702
An authorization tuple is honored if and only if its signature set satisfies the authority’s effective signing policy at the time the tuple is processed. A legacy account’s tuples are unaffected whereas a tuple signed by a retired key is refused. The new transaction type also carries generalized tuples, which carry their own signature set in place of the legacy v,r,s values. EIP-7702 delegations already in effect when the authorising keys are retired remain in effect until re-delegated / un-delegated using the new keys and signing policy.