Skip to main content
For a product-level overview, see Smart Sessions. This page covers the technical model: the Sessions Module, the V3 config tree, session key lifecycle, EIP-7702 delegation, and on-chain enforcement.

Sessions Module

The Sessions Module is a sapient signer smart contract that occupies a leaf in the wallet’s V3 config tree. It is present in every wallet config from creation, initially with an empty session tree. This means every wallet is ready to accept session grants at any point, without a separate setup transaction. In the EOA+7702 configuration, the Sessions Module sits directly under the root, as a sibling of the EOA leaf, with threshold 1. Either the EOA leaf or the Sessions Module can authorize an operation independently. Config updates that add or remove sessions are signed by WaaS using the EOA’s private key. There is no supported out-of-band path for amending the Sessions Module; all session-related config updates go through WaaS. The empty-Sessions-Module image hash for a fresh wallet is well-known and recorded as deployHash in the Wallets table. It is the trust anchor from which the entire subsequent config update chain is validated.

Remote access credential

A remote access credential (RAC) is a keypair generated by the remote backend outside of WaaS. The backend retains the private key and uses it to sign API requests via the standard Oms-Wallet-Signature header. The base credential is created via RegisterCredential; it carries no wallet access until a wallet owner explicitly grants one via AuthorizeRemoteAccess. Higher-level authentication at the ingress proxy may be layered on for abuse prevention, but it is not security-critical: access to any wallet still requires explicit AuthorizeRemoteAccess by the wallet owner. The credential’s ExpiresAt caps the maximum session duration; no session can be authorized with an expiry that exceeds it. A single RAC can hold multiple session bindings, one per wallet. The (credentialId, walletId) pair is unique: re-authorizing the same pair updates the binding in place (an idempotent edit that replaces the permissions and expiry while reusing the existing session key) rather than creating a second binding. The credentialId is the SHA-256 hash of the credential type byte concatenated with the RAC public key, expressed in hex. The same identifier is used across RegisterCredential, AuthorizeRemoteAccess, InspectCredential, ListAccess, RevokeAccess, and RevokeCredential.

Session key

The session key is an ECDSA keypair (secp256k1) generated by WaaS inside the enclave during AuthorizeRemoteAccess. It is distinct from the RAC: the RAC authenticates API requests; the session key produces on-chain signatures through the Sessions Module. The private key is stored encrypted in the session binding and never leaves the enclave. On a subsequent AuthorizeRemoteAccess for an existing (credentialId, walletId) pair, the existing session key is reused and only the permissions and expiry are updated.

Wallet config tree

root (threshold 1)
├── EOA leaf
└── Sessions Module
    └── session tree (one leaf per active session)
Each session leaf in the tree encodes the session key address, the permissions (allowed contracts, methods, value limits), and the expiry. When WaaS adds or removes a session, it updates the Sessions Module subtree, increments the checkpoint, signs the config transition with the EOA’s private key, and writes the new checkpoint to the config store. Lazy expiry pruning happens at update time: any session whose expiresAt has already passed is dropped from the tree when the next config update is written. The on-chain Sessions Module already rejects signatures from expired sessions; the pruning step keeps the off-chain tree tidy.

Permissions

Session permissions define what the remote party is allowed to do with the wallet. They are specified by the client app at AuthorizeRemoteAccess time as a structured permission object describing:
  • The allowed target contracts.
  • The allowed methods on each contract.
  • Per-method value limits (including cumulative limits for recurring transfers).
  • The session expiry.
WaaS translates the client-facing permission object into the format required by the Sessions Module smart contract. All enforcement happens on-chain: WaaS does not validate individual transaction calls against the permissions before signing. The on-chain Sessions Module is the sole enforcement point. The wallet-owner-approved permission object is stored encrypted in the session binding alongside the session key. It is also returned to the wallet owner via ListAccess, so a connected-apps screen can display the exact permissions the owner approved on each wallet.

Credential inspection

Before calling AuthorizeRemoteAccess, a client app can call InspectCredential to retrieve the RAC’s public metadata (app name, app URL, logo URL, and any custom key-value pairs registered via RegisterCredential). This endpoint requires no authentication and is intended for rendering a consent screen prior to authorization. InspectCredential returns only the app metadata. It does not expose permissions, bound sessions, the binding count, or the credential’s expiry. That per-wallet data is only available to the authenticated wallet owner via ListAccess.

Lazy EIP-7702 delegation

Sessions operate on EOA wallets that are EIP-7702-delegated to a contract supporting V3 multi-signer signature validation. The delegation is per-chain and established lazily on the first WaaS-mediated transaction on that chain. When the first WaaS-mediated transaction is submitted on a chain the wallet has not yet been delegated on, the relayer includes a SetCodeAuthorization so the EOA delegates to the V3-compatible contract. Subsequent transactions on that chain use the existing delegation and do not include a new SetCodeAuthorization. Each chain handles delegation independently.

Config states

Every wallet has up to four distinct configurations at any point in time:
StateDescription
InitialThe config produced at wallet creation. Its image hash is persisted as deployHash and serves as the trust anchor for the signed update chain. The session tree starts empty.
CurrentThe latest config for which WaaS holds a signed update. It is the head of the wallet’s off-chain signed update chain, anchored at deployHash.
TargetDerived lazily from the active WaaS version and per-wallet properties (including the current session set). When target != current, WaaS signs a new update to advance current toward target.
On-chainPer-chain. Each chain maintains its own record of which config has been applied and advances lazily toward current with the next transaction on that chain.
Chained V3 signature propagation ensures consistency across chains: pending config updates ride along as chained signatures with the next signed operation on each chain. The on-chain V3 verifier replays the pending updates in order before evaluating the operation signature. All pending updates must be applied in order; none are skipped. The signed update payload is built with chainId=0, so the same signed update is valid on every chain. The config store (Keymachine as the permanent write target, DynamoDB as a read-optimized cache) holds the full signed update chain, and each chain catches up lazily as transactions occur. Keymachine being a permanent write target (not just a cache) exists to prevent a critical failure mode: if a signed config update were applied on-chain but lost from storage, the update chain would be unrecoverable and no new V3-path operation could be produced. In that case, the wallet owner’s EOA private key remains available as an escape hatch to sign raw transactions and recover funds directly.

On-chain enforcement

Every session-driven call goes through the Sessions Module. The module validates each of the following before executing the call:
  • The session key signature is valid.
  • The session has not expired (expiresAt has not passed).
  • The target contract is in the allowlist for this session.
  • The called method matches the approved method for this session.
  • The call value is within the approved limit.
Any failed check rejects the transaction at the contract level. WaaS does not duplicate this enforcement in the API layer; all permission validation happens on-chain. The wallet owner’s approved permissions, passed to AuthorizeRemoteAccess, are translated into the Sessions Module’s on-chain format by WaaS before being written to the session leaf.

Credential and session lifecycle

The following table summarizes the full lifecycle from credential creation through session retirement:
EventWho actsWhat happens
RegisterCredentialRemote backendBase credential created with ExpiresAt = now + lifetime. No wallet access. Idempotent on the same RAC public key.
AuthorizeRemoteAccessWallet ownerSession key generated (or reused on re-auth), session leaf added to Sessions Module, config checkpoint incremented.
Session activeRemote backendPrepareEthereumTransaction / PrepareEthereumContractCall then Execute using the RAC for authentication.
RevokeAccess(credentialId, walletId)Wallet ownerSession leaf removed from Sessions Module, binding deleted, checkpoint incremented.
RevokeCredential(credentialId)Remote backendEvery binding retired across all wallets; credential permanently retired and cannot be re-authorized.
Credential or binding ExpiresAt reachedDynamoDB TTLBinding is deleted; binding count decremented on the base credential. The on-chain Sessions Module independently rejects any call signed with an expired session key.
Note that credential expiry and session expiry are related but distinct. The RAC’s ExpiresAt is set at RegisterCredential time and caps all session expiries on that credential. Each session binding’s ExpiresAt is set at AuthorizeRemoteAccess time, capped at the RAC’s ExpiresAt.

Reference flow

The following steps trace what happens on a single Execute call from the remote backend.
1

Request reaches WaaS

The remote backend’s RAC-signed request reaches WaaS. WaaS credential middleware validates the Oms-Wallet-Signature header and resolves the session binding via (credentialId, walletId).
2

Session key decryption

WaaS decrypts the session binding’s encrypted payload inside the enclave to retrieve the session key. The signed update chain is reloaded and re-validated at this point. If the session has been revoked between the prepare and execute calls, the execute call is rejected here before any signing occurs.
3

On-chain signing

WaaS signs the prepared EIP-712 transaction payload with the session key. Any pending config updates are prepended as chained signatures so the on-chain V3 verifier can advance the wallet’s on-chain config before processing the operation signature.
4

Relayer submission

The relayer submits the transaction. If this is the first WaaS-mediated transaction on this chain for this wallet, a SetCodeAuthorization is included to establish the EIP-7702 delegation.
5

On-chain validation

The V3 contract delegates signature validation to the Sessions Module, which validates the call against the session leaf’s permissions and either executes or rejects. A rejection at this stage leaves no state change on-chain.
6

Status polling

The remote backend polls TransactionStatus(txnId) until the transaction reaches Executed or Failed.

Further reading

Smart Sessions overview

The consumer-facing overview: setup flow, revocation, and what sessions can and cannot do.

Wallet configuration

The on-chain configuration tree that holds the Sessions Module.

WaaS infrastructure

The enclave, key management, and relayer infrastructure that backs all wallet operations.