Abstract
We define a native World Chain account type managed by the WORLD_CHAIN_ACCOUNT_MANAGER predeploy. Each account has one immutable EIP-1271 compliant admin signer and one active key ring of EIP-1271 compliant session verifiers.
We extend EIP-2718 with a new typed transaction envelope with type flag 0x1D. 0x1D transactions are executed with the World Chain account framed as the sender. Session verifiers act as the transaction level signatories via programmable EIP-1271 smart contracts. Session verifiers dually function as signature verifiers, and session key policy evaluators.
We design with the principle of not defining signer-specific authentication as native execution paths. Signature schemes, proof systems, recovery policies, and spending policies are fully programmable. The protocol only prescribes deterministic transaction context, validation frames, and reusable precompiles for cryptographic operations.
Motivation
World Chain accounts need programmable authentication without adding a protocol branch for every signature scheme, or session key policy. Smart contracts allow us to converge protocol level authentication over a single authorization boundary with determinism enforced via protocol level restrictions on programmable validation frames.
Programmable policies over Session Keys unlock many capabilities that simply are not possible on traditional accounts. Some examples include account subscription models, delegated agent payments with scoped permissions enforced by the VM, social recovery, policy based authentication (e.g. Multi Factor authentication for high value transactions). World Chain accounts are unique in that the policies constraining a Session Key are fully programmable. This allows for expressive policies, and authentication strategies to be composed and updated dynamically without prescriptive standards enforced by the protocol.
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.
Protocol Constants
| Name | Type | Value | Meaning |
|---|---|---|---|
WORLD_TX_TYPE | uint8 | 0x1D | EIP-2718 transaction type for World Chain account transactions. |
MAX_SESSION_VERIFIERS | uint256 | 20 | Maximum active session verifiers per account. |
WORLD_CHAIN_ACCOUNT_DOMAIN | bytes32 | keccak256("WIP1001_ACCOUNT") | Domain for account address derivation. |
WORLD_CHAIN_ACCOUNT_CREATE_DOMAIN | bytes32 | keccak256("WORLD_CHAIN_ACCOUNT_CREATE") | Domain for account creation authorization. |
WORLD_CHAIN_ACCOUNT_SET_DOMAIN | bytes32 | keccak256("WORLD_CHAIN_ACCOUNT_SET") | Domain for key ring replacement authorization. |
Activation Parameters
WIP-1001 MUST NOT activate until every parameter below is assigned in fork configuration.
| Name | Type | Value | Requirement |
|---|---|---|---|
WORLD_CHAIN_ACCOUNT_MANAGER | address | TBD | Predeploy address for the account manager. |
WORLD_CHAIN_ACCOUNT_ROUTER_CODE_HASH | bytes32 | TBD | Runtime code hash required at every World Chain account address. |
EIP1271_VALIDATION_GAS_LIMIT | uint64 | TBD | Fixed gas forwarded to isValidSignature. |
EXECUTION_TRACE_VALIDATION_GAS_LIMIT | uint64 | TBD | Fixed gas forwarded to evaluateSessionPolicy. |
BLOCK_VALIDATION_GAS_BUDGET | uint64 | TBD | Per-block gas budget reserved for 0x1D validation calls. |
MIN_VALIDATION_FAILURE_FEE | uint256 | TBD | Minimum wei charged to the account on validation failure. |
MAX_EXECUTION_TRACE_BYTES | uint32 | TBD | Maximum ABI-encoded trace size passed to a session verifier. |
MAX_PAYLOAD_DATA_BYTES | uint32 | TBD | Maximum length of data in a 0x1D envelope. |
MAX_ACCESS_LIST_ENTRIES | uint32 | TBD | Maximum number of EIP-2930 access-list entries in a 0x1D envelope. |
MAX_SESSION_SIGNATURE_BYTES | uint32 | TBD | Maximum length of the session signature field. |
MAX_ADMIN_AUTHORIZATION_BYTES | uint32 | TBD | Maximum length of adminAuthorization calldata. |
MAX_VERIFIER_INSTALL_DATA_BYTES | uint32 | TBD | Maximum length of installation in an admin or session verifier configuration. |
EDDSA_PRECOMPILE | address | TBD | EdDSA verification precompile address and ABI. |
BLS12_381_PRECOMPILE | address | TBD | BLS12-381 verification precompile address and ABI. |
Reusable Crypto Precompiles
Signer contracts MAY call protocol-supported cryptographic precompiles from restricted validation frames.
| Primitive | Source |
|---|---|
ecrecover | Ethereum precompile |
sha256 | Ethereum precompile |
ripemd160 | Ethereum precompile |
identity | Ethereum precompile |
modexp | Ethereum precompile |
bn254 add | Ethereum precompile |
bn254 scalar multiplication | Ethereum precompile |
bn254 pairing | Ethereum precompile |
secp256r1 verify | RIP-7212 |
EdDSA verify | Activation parameter |
BLS12-381 verify | Activation parameter |
Account State
World Chain accounts are created and managed by WORLD_CHAIN_ACCOUNT_MANAGER.
struct Account {
WorldChainAccountVerifier admin;
bytes32 accountSalt;
WorldChainAccountVerifier[] sessionVerifiers;
bytes32 keyRingHash;
uint64 adminNonce;
uint64 transactionNonce;
}
struct WorldChainAccountVerifier {
address verifier;
bytes installation;
}
For every existing account:
- the account address MUST have deployed runtime bytecode whose hash equals
WORLD_CHAIN_ACCOUNT_ROUTER_CODE_HASH; admin.verifierMUST be a deployed EIP-1271 verifier implementation and the fulladminvalue is immutable for the life of the account address;sessionVerifiers.lengthMUST be in[1, MAX_SESSION_VERIFIERS];- each
installation.lengthinadminandsessionVerifiersMUST be at mostMAX_VERIFIER_INSTALL_DATA_BYTES; - each
sessionVerifiers[i].verifierMUST be a deployed EIP-1271 verifier implementation adhering toIWorldChainSessionVerifier; sessionVerifiersMUST NOT contain duplicate verifier addresses;keyRingHash == keccak256(abi.encode(sessionVerifiers));adminNonceis consumed only by admin operations;transactionNonceis consumed only by successful0x1Dtransaction execution attempts;
Verifier behavior is selected by verifier implementation address, not by a protocol-level enum. installation is opaque to the protocol and is interpreted only by the selected verifier implementation.
After account creation, setKeyRing is the only execution path that mutates sessionVerifiers, keyRingHash, or validation-affecting account storage for active session verifiers. WIP-1001 defines no operation that mutates admin or the account router runtime code.
Address Derivation
The manager derives an account address as:
adminHash = keccak256(abi.encode(admin));
account = address(uint160(uint256(keccak256(abi.encode(
WORLD_CHAIN_ACCOUNT_DOMAIN,
chainId,
WORLD_CHAIN_ACCOUNT_MANAGER,
WORLD_CHAIN_ACCOUNT_ROUTER_CODE_HASH,
adminHash,
accountSalt
)))));
The address binds the account to the manager instance, chain, account router runtime code, full admin verifier configuration, and salt. WIP-1001 defines no operation that rotates or reinstalls admin for an existing account.
Admin Operation Hashes
For every admin authorization, the manager computes a domain-separated adminOperationHash and calls:
IWorldChainAccountRouter(account).isValidSignatureForAdmin(
adminOperationHash,
adminAuthorization
)
The hash for each operation is:
createHash = keccak256(abi.encode(
WORLD_CHAIN_ACCOUNT_CREATE_DOMAIN,
chainId,
WORLD_CHAIN_ACCOUNT_MANAGER,
WORLD_CHAIN_ACCOUNT_ROUTER_CODE_HASH,
account,
adminHash,
accountSalt,
keyRingHash
));
setKeyRingHash = keccak256(abi.encode(
WORLD_CHAIN_ACCOUNT_SET_DOMAIN,
chainId,
WORLD_CHAIN_ACCOUNT_MANAGER,
account,
adminNonce,
expectedCurrentKeyRingHash,
newKeyRingHash
));
Account Router and Verifier Interfaces
The account router is the only validation target called by the manager. It dispatches to configured verifier implementations using a single controlled DELEGATECALL, so verifier implementations execute against the account’s storage.
interface IWorldChainAccountRouter {
function installAdmin(WorldChainAccountVerifier calldata admin) external;
function installKeyRing(
WorldChainAccountVerifier[] calldata sessionVerifiers
) external;
function isValidSignatureForAdmin(
bytes32 hash,
bytes calldata signature
) external view returns (bytes4 magicValue);
function isValidSignatureForVerifier(
address verifier,
bytes32 hash,
bytes calldata signature
) external view returns (bytes4 magicValue);
function evaluateSessionPolicyForVerifier(
address verifier,
IWorldChainSessionVerifier.ExecutionTraceContext calldata context
) external view returns (bool allowed);
}
installAdmin and installKeyRing MUST reject any caller other than WORLD_CHAIN_ACCOUNT_MANAGER. The account router MUST expose no non-manager path that can mutate validation-affecting verifier storage. isValidSignatureForAdmin, isValidSignatureForVerifier, and evaluateSessionPolicyForVerifier MUST be callable by WORLD_CHAIN_ACCOUNT_MANAGER in restricted validation frames.
The account router MUST dispatch admin validation only to its installed admin.verifier. It MUST dispatch session validation only to an installed session verifier address; unknown session verifier addresses MUST fail without executing verifier implementation code.
isValidSignatureForAdmin and isValidSignatureForVerifier MUST dispatch using calldata IERC1271.isValidSignature.selector || abi.encode(hash, signature). evaluateSessionPolicyForVerifier MUST dispatch using calldata IWorldChainSessionVerifier.evaluateSessionPolicy.selector || abi.encode(context).
Verifier implementations install scoped storage through the following installation hook:
interface IWorldChainAccountHooks {
function install(bytes calldata installation) external;
}
Admin verifier implementations MUST implement EIP-1271 and IWorldChainAccountHooks. Session verifier implementations MUST implement IWorldChainSessionVerifier:
interface IWorldChainSessionVerifier is IERC1271, IWorldChainAccountHooks {
struct AccessListEntry {
address account;
bytes32[] storageKeys;
}
struct WorldChainTransactionContext {
bytes32 signingHash;
uint256 chainId;
address account;
address sessionVerifier;
uint64 nonce;
uint256 maxPriorityFeePerGas;
uint256 maxFeePerGas;
uint64 gasLimit;
bool isCreate;
address target;
uint256 value;
bytes data;
bytes4 selector;
AccessListEntry[] accessList;
bytes32 keyRingHash;
}
enum TraceCallKind {
Call,
StaticCall,
DelegateCall,
Create,
Create2
}
struct CallTrace {
uint32 depth;
TraceCallKind kind;
address caller;
address target;
uint256 value;
bytes4 selector;
bytes32 inputHash;
bytes32 outputHash;
bool success;
uint64 gasUsed;
}
struct LogTrace {
address emitter;
bytes32[] topics;
bytes32 dataHash;
uint32 callIndex;
}
struct WorldChainExecutionTrace {
bool success;
uint64 gasUsed;
bytes32 outputHash;
CallTrace[] calls;
LogTrace[] logs;
}
struct ExecutionTraceContext {
WorldChainTransactionContext transaction;
WorldChainExecutionTrace trace;
}
function evaluateSessionPolicy(
ExecutionTraceContext calldata context
) external view returns (bool allowed);
}
installation is verifier-defined. The protocol does not parse it, but it commits to it through adminHash, createHash, and keyRingHash. A verifier implementation’s install function MUST fully write all validation-affecting state implied by installation into the verifier’s own deterministic account-storage namespace. Removed verifier state MAY remain in account storage, but it MUST be unreachable unless that verifier address is installed again through a later setKeyRing.
Verifier implementations MUST NOT rely on validation-affecting state stored at the verifier implementation address. Validation-affecting state MUST live in account storage installed through the account router.
Manager Interface
WORLD_CHAIN_ACCOUNT_MANAGER exposes:
interface IWorldChainAccountManager {
function create(
WorldChainAccountVerifier calldata admin,
bytes32 accountSalt,
WorldChainAccountVerifier[] calldata initialSessionVerifiers,
bytes calldata adminAuthorization
) external returns (address account);
function setKeyRing(
address account,
bytes32 expectedCurrentKeyRingHash,
WorldChainAccountVerifier[] calldata sessionVerifiers,
bytes calldata adminAuthorization
) external;
function getAdmin(address account) external view returns (WorldChainAccountVerifier memory);
function getAdminNonce(address account) external view returns (uint64);
function getTransactionNonce(address account) external view returns (uint64);
function getKeyRingHash(address account) external view returns (bytes32);
function getSessionVerifiers(address account) external view returns (WorldChainAccountVerifier[] memory);
function isAuthorizedSessionVerifier(address account, address sessionVerifier) external view returns (bool);
function getAuthorizedSessionVerifier(address account, address sessionVerifier) external view returns (WorldChainAccountVerifier memory);
}
Manager Events
Each successful state transition MUST emit exactly one matching event in the same transaction that mutates state.
interface IWorldChainAccountManagerEvents {
event AccountCreated(
address indexed account,
address indexed adminVerifier,
bytes32 indexed adminHash,
bytes32 accountSalt,
bytes32 keyRingHash
);
event KeyRingSet(
address indexed account,
bytes32 indexed previousKeyRingHash,
bytes32 indexed newKeyRingHash,
uint64 adminNonce
);
}
Manager State Transitions
Every admin-mutating method MUST reject adminAuthorization.length > MAX_ADMIN_AUTHORIZATION_BYTES. It MUST also reject any installation.length > MAX_VERIFIER_INSTALL_DATA_BYTES. If any requirement in a manager state transition fails, the method MUST revert without changing account state, account code, account storage, incrementing adminNonce, or emitting its state-transition event.
Admin authorization is valid only when IWorldChainAccountRouter(account).isValidSignatureForAdmin(adminOperationHash, adminAuthorization) succeeds in a restricted validation frame and returns EIP1271_MAGIC_VALUE.
Installing canonical account-router runtime bytecode is a native manager state transition. It MUST NOT execute account-supplied init code and MUST NOT be represented as an EVM CREATE or CREATE2 from the manager.
create
create MUST:
- reject
admin.verifier == address(0); - require
admin.verifierand each initial session verifier to have deployed code; - require
initialSessionVerifiers.lengthto be in[1, MAX_SESSION_VERIFIERS]; - reject duplicate or zero session verifier addresses;
- reject oversized
admin.installationor session verifierinstallation; - compute
adminHash = keccak256(abi.encode(admin)); - derive
account; - reject if
accountalready exists or already has deployed code; - compute
keyRingHash = keccak256(abi.encode(initialSessionVerifiers)); - atomically install canonical account-router runtime bytecode at
account; - call
IWorldChainAccountRouter(account).installAdmin(admin); - call
IWorldChainAccountRouter(account).installKeyRing(initialSessionVerifiers); - verify
createHashthrough the account router’s admin validation path; - initialize manager state with
admin,adminNonce = 0,transactionNonce = 0,sessionVerifiers = initialSessionVerifiers, andkeyRingHash; - emit
AccountCreated(account, admin.verifier, adminHash, accountSalt, keyRingHash).
setKeyRing
setKeyRing is the only post-creation method that mutates the key ring and MUST:
- require
accountto exist; - require
expectedCurrentKeyRingHash == keyRingHash; - require
sessionVerifiers.lengthto be in[1, MAX_SESSION_VERIFIERS]; - reject duplicate, zero, undeployed, or oversized session verifier entries;
- compute
newKeyRingHash = keccak256(abi.encode(sessionVerifiers)); - reject
newKeyRingHash == keyRingHash; - compute
setKeyRingHashusing the currentadminNonce; - verify
setKeyRingHashthrough the account router’s admin validation path; - call
IWorldChainAccountRouter(account).installKeyRing(sessionVerifiers); - set
previousKeyRingHash = keyRingHash; - replace the account’s full
sessionVerifiersset with the suppliedsessionVerifiers; - set
keyRingHash = newKeyRingHash; - increment
adminNonceby one; - emit
KeyRingSet(account, previousKeyRingHash, newKeyRingHash, adminNonce).
Restricted Validation Frames
The protocol uses restricted validation frames for admin authorization, session signature verification, and session policy evaluation.
For each EIP-1271 signature check, the protocol MUST perform a STATICCALL from WORLD_CHAIN_ACCOUNT_MANAGER to the signer with value 0, gas exactly EIP1271_VALIDATION_GAS_LIMIT, and calldata IERC1271.isValidSignature.selector || abi.encode(hash, signature). The call succeeds only if it returns EIP1271_MAGIC_VALUE and triggers no tracer rule violation.
For each session policy check, the protocol MUST perform a STATICCALL from WORLD_CHAIN_ACCOUNT_MANAGER to the session verifier with value 0, gas exactly EXECUTION_TRACE_VALIDATION_GAS_LIMIT, and calldata IWorldChainSessionVerifier.evaluateSessionPolicy.selector || abi.encode(context). The call succeeds only if it returns true and triggers no tracer rule violation.
Failure, revert, out-of-gas, malformed return data, or a tracer violation MUST be treated as signature or policy failure.
Restricted frames are enforced by a runtime opcode-and-target tracer derived from ERC-7562. Runtime enforcement is authoritative.
| Rule ID | ERC-7562 ref | Description |
|---|---|---|
WIP1001-VR-1 | OP-011 | Forbid block-context opcodes: BLOCKHASH, COINBASE, TIMESTAMP, NUMBER, PREVRANDAO/DIFFICULTY, GASLIMIT, BASEFEE, BLOBHASH, BLOBBASEFEE. |
WIP1001-VR-2 | OP-011 | Forbid transaction-context opcodes: GASPRICE, ORIGIN. |
WIP1001-VR-3 | OP-020 | Forbid external account inspection: BALANCE, SELFBALANCE, EXTCODESIZE, EXTCODECOPY, EXTCODEHASH. |
WIP1001-VR-4 | OP-031, OP-032 | Forbid persistent and transient state mutation: SSTORE, TLOAD, TSTORE. |
WIP1001-VR-5 | OP-031 | Forbid log emission: LOG0, LOG1, LOG2, LOG3, LOG4. |
WIP1001-VR-6 | OP-031, OP-040 | Forbid CALL, CALLCODE, CREATE, CREATE2. |
WIP1001-VR-7 | OP-051 | Forbid SELFDESTRUCT. |
WIP1001-VR-8 | OP-061 | Permit STATICCALL only to allowlisted precompiles. |
WIP1001-VR-9 | OP-052 | Permit DELEGATECALL only when the delegatee bytecode satisfies the same rule set. The tracer MUST validate delegatees recursively. |
WIP1001-VR-10 | - | Precompile execution does not create an additional EVM code frame. |
WIP1001-VR-11 | - | Permit reads of the signer contract’s own bytecode via CODECOPY and CODESIZE. |
WIP1001-VR-12 | - | Permit reads of the signer contract’s own storage via SLOAD. |
WIP1001-VR-13 | - | Permit KECCAK256, CHAINID, GAS, stack, memory, calldata, and deterministic arithmetic opcodes. |
Adding a new precompile to the validation allowlist is a hard-fork change.
Transaction Type 0x1D
The wire encoding is:
0x1D || rlp([
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
account,
sessionVerifier,
to,
value,
data,
accessList,
signature
])
The signing hash is:
signingHash = keccak256(
0x1D ||
rlp([
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
account,
sessionVerifier,
to,
value,
data,
accessList
])
);
The transaction hash is:
txHash = keccak256(
0x1D ||
rlp([
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
account,
sessionVerifier,
to,
value,
data,
accessList,
signature
])
);
Envelope validity requirements:
chainIdMUST equal the executing chain ID;nonceMUST equalAccount.transactionNonce;toMUST be the empty byte string for contract creation or exactly 20 bytes for a call;data.length <= MAX_PAYLOAD_DATA_BYTES;accessList.length <= MAX_ACCESS_LIST_ENTRIES;signature.length <= MAX_SESSION_SIGNATURE_BYTES;accessListMUST use EIP-2930 encoding;maxFeePerGasandmaxPriorityFeePerGasfollow EIP-1559 validity rules.
Validation, Execution, and Failure Semantics
Block builders MUST reserve EIP1271_VALIDATION_GAS_LIMIT + EXECUTION_TRACE_VALIDATION_GAS_LIMIT from BLOCK_VALIDATION_GAS_BUDGET before including a 0x1D transaction. If the remaining budget is insufficient, the transaction MUST be deferred; deferral is not a validation failure and MUST NOT mutate account state.
To include a 0x1D transaction, the protocol MUST:
- decode and validate the envelope;
- load
accountfromWORLD_CHAIN_ACCOUNT_MANAGER; - require
sessionVerifierto be authorized foraccountin the currentsessionVerifiersset and load the currentkeyRingHash; - require the account balance to cover the worst-case payload gas charge and
MIN_VALIDATION_FAILURE_FEE; - compute
signingHash; - call
sessionVerifier.isValidSignature(signingHash, signature)in a restricted validation frame; - if signature validation fails, apply validation-failure semantics and stop;
- increment
Account.transactionNonceby one; - execute the payload tentatively with
accountas the EVM sender and gas charged against envelopegasLimit; - construct
ExecutionTraceContextwithcontext.transaction.keyRingHashset to the current accountkeyRingHash; - if
abi.encode(context.trace).length > MAX_EXECUTION_TRACE_BYTES, discard tentative payload state, apply trace-overflow semantics, and stop; - call
sessionVerifier.evaluateSessionPolicy(context)in a restricted validation frame; - if policy validation fails, discard tentative payload state, apply validation-failure semantics, and stop;
- otherwise commit the normal payload result, including a reverted payload result if the target call reverted.
Failure classes are:
| Failure | Nonce | Payload state/logs | Fee behavior |
|---|---|---|---|
| Envelope, account, balance, nonce, or authorization precheck failure | unchanged | none | transaction is invalid and not included |
| Validation budget exhausted | unchanged | none | transaction is deferred |
| Signature validation failure | incremented | none | charge max(MIN_VALIDATION_FAILURE_FEE, baseFee * EIP1271_VALIDATION_GAS_LIMIT) |
Trace exceeds MAX_EXECUTION_TRACE_BYTES | incremented | discarded | charge as a failed payload execution under the envelope gas rules |
| Session policy failure | incremented | discarded | charge max(MIN_VALIDATION_FAILURE_FEE, baseFee * EIP1271_VALIDATION_GAS_LIMIT); payload gas is not additionally charged |
| Payload execution revert with accepted policy | incremented | EVM revert result committed | charge normal payload gas |
All validation gas consumed by signature and policy checks MUST count against BLOCK_VALIDATION_GAS_BUDGET. Validation gas is not deducted from the envelope gasLimit.
Txpool Revalidation
World Chain clients MUST revalidate every pooled 0x1D transaction at least once per block until inclusion or eviction.
On pool admission or successful revalidation, the client MUST record account, sessionVerifier, nonce, signingHash, signature, and the account’s active keyRingHash. A transaction is eligible to remain pooled only while all conditions hold:
accountexists;- the account balance can cover
MIN_VALIDATION_FAILURE_FEEat the currentbaseFee; - the transaction nonce can still become executable under the account nonce ordering rules;
sessionVerifieris a member of the account’s currentsessionVerifiersset;- the current account
keyRingHashequals the hash recorded when the transaction was admitted or last revalidated.
A client MAY reuse a prior successful signature-validation result only when the cached (account, sessionVerifier, signingHash, signature, keyRingHash) tuple exactly matches the current transaction and account state. Block builders MAY rely on such a cache to avoid repeating signature verification before block construction; if the active keyRingHash changes, the cached result MUST be discarded and the signature MUST be revalidated.
The keyRingHash comparison is the deterministic anchor for block validation: a verifier MAY accept a cached signature result only if the active account hash equals the recorded hash; otherwise it MUST execute signature validation again.
Signer-State Determinism
Session verifier behavior that can change isValidSignature or evaluateSessionPolicy for already accepted transactions MUST be immutable for a verifier address. Validation-affecting updates MUST be represented by deploying or selecting a different session verifier address and installing a replacement set through setKeyRing, which updates keyRingHash.
This applies to root rotations, verification-key rotations, signer recovery changes, signer revocation lists, account binding changes, and policy configuration changes. State that cannot affect validation MAY update in place.
Rationale
EIP-1271 gives World Chain one protocol boundary for all authorization schemes. The protocol verifies a signer contract; the signer contract decides how to authenticate.
Execution policy belongs inside the session verifier. The protocol supplies the canonical transaction context and execution trace, then asks the verifier for a boolean decision.
Validation gas limits are protocol constants, not signer-selected values. This gives clients a fixed resource bound for mempool validation and block execution.
Validation gas is supplied from a per-block budget rather than the envelope gasLimit. This keeps the payload gas model close to EIP-1559 transactions while bounding validation work per block.
Whole-set setKeyRing updates make the account’s session authorization state a single ordered set with one deterministic keyRingHash. Clients can bind cached signature validation to that hash and must revalidate when it changes.
Restricted validation frames prevent validation from depending on mutable external state, block metadata, or arbitrary external contract calls.
Backwards Compatibility
WIP-1001 introduces a new EIP-2718 transaction type and a new account manager predeploy. Existing Ethereum transaction types, EOAs, contracts, and Safe accounts are unchanged.
Existing Safe accounts MAY deploy or delegate to EIP-1271 signer contracts that satisfy this specification, but WIP-1001 does not require Safe migration.
Test Cases
Conforming implementations MUST pass vectors for:
- address derivation across multiple
(chainId, manager, adminSigner, accountSalt)tuples; keyRingHash = keccak256(abi.encode(sessionVerifiers))for empty-forbidden, single-verifier, multi-verifier, and reordered verifier sets;createvalidation, including deployed-code checks, duplicate and zero verifier rejection, account-exists rejection, initial nonce values, initialkeyRingHash,AccountCreated, and default World ID admin signer account binding;setKeyRingvalidation, including staleexpectedCurrentKeyRingHash, duplicate, zero, undeployed, empty, oversized, and no-op verifier set rejection;setKeyRingsuccess behavior, including full-set replacement,newKeyRingHash,adminNonceincrement,KeyRingSet, authorization lookup updates, and failure atomicity;createHashandsetKeyRingHashdomain separation, parameter binding, nonce binding, and negative replay cases across accounts, chains, managers, hashes, and operation types;0x1DRLP encoding, signing hash, and transaction hash;- malformed envelope rejection for
chainId,nonce,to,data,accessList,signature, and EIP-1559 fee bounds; - transaction validation against the current verifier set, including unauthorized verifier rejection and
ExecutionTraceContext.transaction.keyRingHash; - failure semantics for envelope/precheck failures, validation-budget exhaustion, signature failures, trace overflow, policy failures, and payload reverts;
- txpool revalidation, signature-validation cache reuse, and cache invalidation when
keyRingHash, verifier membership, account balance, or nonce eligibility changes; - signer-state determinism, including rejection of validation-affecting session verifier updates behind an unchanged verifier address;
- restricted-frame positive cases and each forbidden opcode or call target;
- default signer factory
CREATE2address computation, idempotency, metadata, and incompatible-code rejection; - World ID
worldIdField,worldIdAction, admin proof, and session proof inputs; - default secp256k1 low-s,
v, owner, and signature-length checks; - block validation budget exhaustion and validation-failure fee charging.
Complete golden vectors and a conformance test suite MUST be published before this WIP advances to Review.
Security Considerations
- Admin authorization hashes bind domain, chain ID, manager, account, operation parameters, and nonce where applicable. A signature valid for
createMUST NOT be valid forsetKeyRing, another account, another chain, another manager, or another key ring hash. - The admin signer is immutable in the account address. A broken or lost admin signer requires migrating assets to a new account; applications that need recovery SHOULD use admin signers with recovery or multisig logic from account creation.
setKeyRingis the only post-creation key ring mutation path. A compromised session verifier remains authorized until an admin-authorized replacement set is included.expectedCurrentKeyRingHashprotectssetKeyRingauthorizations from applying to an unexpected current set. Admin tooling SHOULD surface the complete replacement set before signing because omitted verifiers are removed atomically.keyRingHashbinds cached validation to the orderedSessionVerifier[]set. Clients MUST discard cached signature results when the active hash changes, even if the transaction’ssessionVerifierremains present in the new set.keyRingHashdoes not commit to mutable verifier behavior by itself. Session verifier state that can affectisValidSignatureorevaluateSessionPolicyMUST be immutable for that verifier address; upgrades that affect validation require a new verifier address installed throughsetKeyRing.- The protocol MUST verify that
sessionVerifieris authorized foraccountbefore calling EIP-1271. Unauthorized verifiers MUST fail as a precheck rather than receiving a validation call. - Fixed validation gas limits bound signer execution. Signers that exceed the limit fail validation, and all signature and policy validation gas counts against
BLOCK_VALIDATION_GAS_BUDGET. BLOCK_VALIDATION_GAS_BUDGETis scarce.MIN_VALIDATION_FAILURE_FEEMUST be tuned so validation spam is costly, and clients MUST reject pooled transactions that cannot pay it.- Builders may prefer transactions with cheaper validation profiles. Fixed validation gas bounds the worst-case bias but does not eliminate ordering incentives.
MAX_EXECUTION_TRACE_BYTES,MAX_PAYLOAD_DATA_BYTES,MAX_ACCESS_LIST_ENTRIES,MAX_SESSION_SIGNATURE_BYTES, andMAX_ADMIN_AUTHORIZATION_BYTESbound calldata, decoding, memory, and ABI-encoding costs.- Restricted frames forbid mutable external-state reads and block-context opcodes. Signers that need time, block, or external-state constraints MUST bind them into the signature or proof input.
DELEGATECALLinside restricted validation frames is safe only when the delegatee bytecode satisfies the same tracer rules recursively.- The
0x1Denvelope exposesaccountandsessionVerifier. Applications that need stronger privacy should use verifier contracts that aggregate or rotate credentials. - World ID signers MUST bind
block.chainidintoworldIdActionor session nonce inputs to prevent cross-chain proof replay. - The default World ID admin signer self-reports
account(). The manager checks this only for default-factory-deployed World ID admin signers; custom signers are responsible for their own consistency. createdoes not bindmsg.sender, so a copied call can be front-run. The resulting account state is identical because the authorization commits to all state-determining parameters.