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 session verifier instances backed by EIP-1271 compliant verifier implementations.
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 verifier instances act as the transaction level signatories via programmable EIP-1271 smart contracts. Session verifier implementations 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 verifier instances 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. |
WORLD_CHAIN_SESSION_VERIFIER_INSTANCE_DOMAIN | bytes32 | keccak256("WORLD_CHAIN_SESSION_VERIFIER_INSTANCE") | Domain for session verifier instance derivation. |
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;
mapping(bytes32 sessionVerifierInstance => InstalledSessionVerifier) sessionVerifierInstances;
bytes32 keyRingHash;
uint64 adminNonce;
uint64 transactionNonce;
}
struct WorldChainAccountVerifier {
address verifier;
bytes installation;
}
struct InstalledSessionVerifier {
address verifier;
bytes32 keyRingHash;
}
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 session verifier instances;- for each active
sessionVerifiers[i], the corresponding derivedsessionVerifierInstanceMUST resolve tosessionVerifiers[i].verifierand the currentkeyRingHash; keyRingHash == keccak256(abi.encode(sessionVerifiers));adminNonceis consumed only by admin operations;transactionNonceis consumed only by successful0x1Dtransaction execution attempts;
Verifier code is selected by verifier implementation address, not by a protocol-level enum. Session verifier membership and configuration are selected by session verifier instance. For each active sessionVerifiers[i], the corresponding instance identifier is:
sessionVerifierInstance = keccak256(abi.encode(
WORLD_CHAIN_SESSION_VERIFIER_INSTANCE_DOMAIN,
chainId,
account,
sessionVerifiers[i].verifier,
keccak256(sessionVerifiers[i].installation)
));
A session verifier implementation is the contract address whose code is executed by the account router. A session verifier instance is one installed use of that implementation for a specific account and installation payload. Multiple session verifier instances MAY reference the same verifier implementation address, provided their installation hashes differ and therefore their sessionVerifierInstance identifiers are distinct. installation is opaque to the protocol and is interpreted only by the selected verifier implementation.
sessionVerifierInstances is account-router resolver state. It maps each installed session verifier instance to the verifier implementation address to DELEGATECALL and the keyRingHash for which that resolver entry is active. A session verifier instance is active iff sessionVerifierInstances[sessionVerifierInstance].keyRingHash == keyRingHash != 0 and sessionVerifierInstances[sessionVerifierInstance].verifier != address(0). Entries from previous key rings MAY remain in storage, but MUST NOT authorize after keyRingHash changes.
After account creation, setKeyRing is the only execution path that mutates sessionVerifiers, sessionVerifierInstances, keyRingHash, or validation-affecting account storage for active session verifier instances. 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(
bytes32 sessionVerifierInstance,
bytes32 hash,
bytes calldata signature
) external view returns (bytes4 magicValue);
function evaluateSessionPolicyForVerifier(
bytes32 sessionVerifierInstance,
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 active session verifier instance; unknown or stale session verifier instances MUST fail without executing verifier implementation code. For session validation, the router MUST resolve sessionVerifierInstance through sessionVerifierInstances, require the stored keyRingHash to equal the account’s current keyRingHash, and then use the stored verifier address for the controlled DELEGATECALL.
When the account router handles a manager-issued admin signature check (isValidSignatureForAdmin), it MUST DELEGATECALL the selected admin verifier implementation with calldata IERC1271.isValidSignature.selector || abi.encode(hash, signature). When the router handles a session signature check (isValidSignatureForVerifier), it MUST DELEGATECALL the selected session verifier implementation with calldata IERC1271.isValidSignature.selector || abi.encode(hash, abi.encode(sessionVerifierInstance, signature)). When the router handles evaluateSessionPolicyForVerifier, it MUST DELEGATECALL the selected session verifier implementation with calldata IWorldChainSessionVerifier.evaluateSessionPolicy.selector || abi.encode(context).
For session signature checks, the signature bytes visible to the session verifier’s ERC-1271 entrypoint are router-encoded as abi.encode(sessionVerifierInstance, signature), where the second field is the 0x1D envelope’s verifier-defined signature field. The 0x1D transaction sender supplies only the envelope sessionVerifierInstance and verifier-defined signature; the account router performs this wrapping before dispatching to the verifier implementation.
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;
bytes32 sessionVerifierInstance;
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. For session verifier installs, the account router MUST derive each sessionVerifierInstance, store sessionVerifierInstances[sessionVerifierInstance] = InstalledSessionVerifier(verifier, keyRingHash), and call install with abi.encode(sessionVerifierInstance, installation). A session verifier implementation’s install function MUST fully write all validation-affecting state implied by installation into a deterministic account-storage namespace derived from sessionVerifierInstance. Because sessionVerifierInstance includes keccak256(installation), two installs of the same implementation with different installation bytes MUST use different validation-affecting storage slots. Removed verifier state MAY remain in account storage, but it MUST be unreachable unless the same session verifier instance 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, bytes32 sessionVerifierInstance) external view returns (bool);
function getAuthorizedSessionVerifier(address account, bytes32 sessionVerifierInstance) external view returns (WorldChainAccountVerifier memory);
}
isAuthorizedSessionVerifier MUST return true only when sessionVerifierInstance resolves to an InstalledSessionVerifier whose keyRingHash equals the account’s current keyRingHash. getAuthorizedSessionVerifier MUST return the matching current sessionVerifiers entry and MUST fail for an absent or stale sessionVerifierInstance.
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 zero session verifier implementation addresses;
- reject oversized
admin.installationor session verifierinstallation; - compute
adminHash = keccak256(abi.encode(admin)); - derive
account; - reject if
accountalready exists or already has deployed code; - reject duplicate session verifier instances for
account; - 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, activesessionVerifierInstances, 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 session verifier instances and 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; - write active
sessionVerifierInstancesentries for the supplied set undernewKeyRingHash; - 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 admin or session signature check, the protocol MUST perform a STATICCALL from WORLD_CHAIN_ACCOUNT_MANAGER to the account router at account with value 0, gas exactly EIP1271_VALIDATION_GAS_LIMIT, and calldata:
IWorldChainAccountRouter.isValidSignatureForAdmin.selector || abi.encode(hash, signature)for admin authorization checks; orIWorldChainAccountRouter.isValidSignatureForVerifier.selector || abi.encode(sessionVerifierInstance, hash, signature)for session signature checks.
The call succeeds only if it returns EIP1271_MAGIC_VALUE and triggers no tracer rule violation. The account router is responsible for DELEGATECALLing the configured verifier implementation per the dispatch rules in “Account Router and Verifier Interfaces”; the manager never calls a verifier implementation directly.
For each session policy check, the protocol MUST perform a STATICCALL from WORLD_CHAIN_ACCOUNT_MANAGER to the account router at account with value 0, gas exactly EXECUTION_TRACE_VALIDATION_GAS_LIMIT, and calldata IWorldChainAccountRouter.evaluateSessionPolicyForVerifier.selector || abi.encode(sessionVerifierInstance, context). The call succeeds only if it returns true and triggers no tracer rule violation. The account router is responsible for DELEGATECALLing the configured session verifier implementation.
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,
sessionVerifierInstance,
to,
value,
data,
accessList,
signature
])
The signing hash is:
signingHash = keccak256(
0x1D ||
rlp([
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
account,
sessionVerifierInstance,
to,
value,
data,
accessList
])
);
The transaction hash is:
txHash = keccak256(
0x1D ||
rlp([
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
account,
sessionVerifierInstance,
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;sessionVerifierInstanceMUST be exactly 32 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; - load the current
keyRingHash, resolvesessionVerifierInstancethroughsessionVerifierInstances, requireverifier != address(0)and require the storedkeyRingHashto equal the currentkeyRingHash, and load the resolved verifier implementation address; - require the account balance to cover the worst-case payload gas charge and
MIN_VALIDATION_FAILURE_FEE; - compute
signingHash; - call
IWorldChainAccountRouter(account).isValidSignatureForVerifier(sessionVerifierInstance, 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.sessionVerifierInstanceset to the selected session verifier instance andcontext.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
IWorldChainAccountRouter(account).evaluateSessionPolicyForVerifier(sessionVerifierInstance, 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, sessionVerifierInstance, 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;
sessionVerifierInstanceresolves to a nonzero verifier insessionVerifierInstanceswith the account’s currentkeyRingHash;- 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, sessionVerifierInstance, 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 session verifier instance. Validation-affecting updates MUST be represented by selecting a different verifier implementation address or different installation bytes, deriving a different sessionVerifierInstance, 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 through the account router; 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. Session verifier instances allow that ordered set to contain multiple entries backed by the same verifier implementation while still giving clients a stable membership key for cached validation. The account-router resolver stores the verifier implementation address beside the key-ring generation so validation can dispatch from sessionVerifierInstance without accepting a verifier address from transaction calldata. 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, repeated implementation with distinct installations, and reordered verifier sets;sessionVerifierInstancederivation, including domain separation, chain ID binding, account binding, implementation-address binding, installation-hash binding, duplicate-instance rejection, and repeated implementation addresses with distinct installation bytes;sessionVerifierInstancesresolution, including verifier address lookup, stale key-ring hash rejection, zero verifier rejection, and repeated implementation addresses with distinct instance IDs;createvalidation, including deployed-code checks, duplicate instance and zero verifier rejection, account-exists rejection, initial nonce values, initialkeyRingHash,AccountCreated, and default World ID admin signer account binding;setKeyRingvalidation, including staleexpectedCurrentKeyRingHash, duplicate instance, 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 session verifier instance resolver, including unauthorized session verifier instance 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, session verifier instance authorization, account balance, or nonce eligibility changes; - signer-state determinism, including rejection of validation-affecting session verifier updates behind an unchanged session verifier instance;
- 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 instance 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 orderedsessionVerifiersset. Clients MUST discard cached signature results when the active hash changes, even if the transaction’ssessionVerifierInstanceremains 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 instance; upgrades that affect validation require a different verifier implementation address or different installation bytes installed throughsetKeyRing.- The protocol MUST verify that
sessionVerifierInstanceis authorized foraccountbefore calling verifier code. Unauthorized instances MUST fail as a precheck rather than receiving a validation call. sessionVerifierInstancesentries from prior key rings MAY remain in storage. They MUST NOT authorize unless their storedkeyRingHashequals the account’s currentkeyRingHash.- 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.- Because verifier implementations execute via
DELEGATECALLagainst account storage, storage isolation is not enforced by the EVM. Session verifier implementations MUST use deterministic account-storage namespaces derived fromsessionVerifierInstanceand MUST NOT write outside their assigned namespace except through explicitly specified account-router storage APIs, if any. Protocol deployments SHOULD allow only audited or approved verifier implementations when verifier code is delegatecalled. - The
0x1Denvelope exposesaccountandsessionVerifierInstance. 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.