Session Keys
A full game is several transactions (create/join, commit, reveal). Session keys reduce that to one wallet approval per session: the wallet delegates signing to an ephemeral key, and the ephemeral key plays the game. Both chains implement the same capability with chain-native shapes. The wallet always remains the on-chain player and payout recipient — the session key is an authorized signer, never the owner of funds.
Solana: session PDA + *_session instructions
Section titled “Solana: session PDA + *_session instructions”The player signs create_player_session once, authorizing an ephemeral session keypair. The session is recorded in a PDA and is valid for 24 hours (SESSION_DURATION_SECONDS = 86_400).
- The session key can sign any of the
*_session-suffixed instruction variants:deposit_stake_session,create_game_session,join_game_session,commit_guess_session,reveal_guess_session. There is no per-instruction permission bitmask — which instructions are session-callable is defined by which*_sessionhandlers exist in the program. - Expiry is enforced on-chain: after 24 hours every
*_sessioninstruction rejects the session key as a signer at the Anchor account-validation layer. No manual revocation is needed for natural rotation. - Early termination:
close_player_session(the wallet closes it) orclose_session_by_delegate(the session key voluntarily closes its own session — useful for an agent process cleaning up on shutdown).
Game creation is session-signed, unconditionally. P1’s frontend create path is create_game_session: the transaction carries the matchmaker’s cosignature (the matchup-commitment forgery guard — see Commit-Reveal Lifecycle) and is partial-signed and submitted by the player’s session key, with no second wallet popup. P2 never enters the create path — they receive game_ready and join_game session-signed.
In the browser, the session keypair lives in sessionStorage with a 24-hour TTL matching the on-chain expiry.
EVM: openSession delegate + escrowed stake
Section titled “EVM: openSession delegate + escrowed stake”The EVM contracts record one session per wallet: sessions(wallet) → (sessionKey, expiry). The whole setup is one popup:
- The wallet signs a single
openSessionAndDeposit(sessionKey, expiry, topUp)call. In that one transaction the contract registers the ephemeral session key as the wallet’s on-chain delegate, forwards gas (topUp) to the session key’s address, and escrows the stake to the wallet’swithdrawableledger. The stake never sits on the session key — a lost or expired session key can cost at most leftover gas dust, never the stake. - The gas-only session key then submits
createGame/joinGame(which debit the wallet’s escrow,msg.value == 0),commitGuess,revealGuess, andwithdrawForas the wallet, with zero further popups. The contract’s_actsForcheck (sessionKeymatches andblock.timestamp < expiry) authorizes each call. - Payouts go to the wallet, not the session key: on the V4 contracts winnings are pushed to the wallet at resolve (see EVM & Cross-Chain); any remaining escrow is swept with
withdrawFor.
Sessions last 24 hours, matching the sessionStorage TTL of the key itself. revokeSession ends one early.
Comparison
Section titled “Comparison”| Solana | EVM (Base / Ethereum) | |
|---|---|---|
| Authorization | create_player_session → session PDA | openSessionAndDeposit → sessions(wallet) record |
| Popups per game | one (session create; deposit rides the same session) | one (registers delegate + gas + stake escrow in a single tx) |
| What the session key signs | *_session instruction variants | all game calls, as the wallet, via _actsFor |
| Stake custody | player’s stake escrow PDA | contract escrow (withdrawable[wallet]) — never the session key |
| Expiry | 24h, enforced at Anchor account validation | 24h, enforced by expiry in _actsFor |
| Revocation | close_player_session / close_session_by_delegate | revokeSession |
| Key storage (browser) | sessionStorage, 24h TTL | sessionStorage, 24h TTL |