Documentation Index
Fetch the complete documentation index at: https://mintlify.com/magicblock-labs/magicblock-engine-examples/llms.txt
Use this file to discover all available pages before exploring further.
Session Keys
Session Keys enable gasless transactions by allowing users to create temporary keypairs with limited permissions. This advanced pattern is essential for providing smooth, wallet-free experiences in games and applications built on Ephemeral Rollups.What Are Session Keys?
Session Keys are temporary keypairs that users can create and grant limited permissions to interact with their accounts. They enable:- Gasless transactions: Users don’t need to approve every transaction
- Improved UX: No wallet popups during gameplay
- Time-limited access: Sessions expire automatically
- Scoped permissions: Sessions can only access specific accounts/programs
Session Keys are particularly powerful in Ephemeral Rollups because they enable continuous, low-latency interactions without wallet approval friction.
How It Works
use session_keys::{session_auth_or, Session, SessionError, SessionToken};
#[session_auth_or(
ctx.accounts.counter.authority.key() == ctx.accounts.payer.key(),
SessionError::InvalidToken
)]
pub fn increment(ctx: Context<Increment>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.count += 1;
if counter.count > 1000 {
counter.count = 0;
}
msg!("PDA {} count: {}", counter.key(), counter.count);
Ok()
}
#[derive(Accounts, Session)]
pub struct Increment<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(
mut,
seeds = [ COUNTER_SEED, counter.authority.key().as_ref() ],
bump
)]
pub counter: Account<'info, Counter>,
#[session(
signer = payer,
authority = counter.authority.key()
)]
pub session_token: Option<Account<'info, SessionToken>>,
}
import { SessionTokenManager } from "@magicblock-labs/gum-sdk";
// Initialize the session manager
const sessionKeypair = Keypair.generate(); // In practice, store this securely
const sessionTokenManager = new SessionTokenManager(
provider.wallet,
provider.connection
);
// Create the session token
const topUp = true;
const validUntilBN = new anchor.BN(Math.floor(Date.now() / 1000) + 3600); // valid for 1 hour
const topUpLamportsBN = new anchor.BN(0.0005 * LAMPORTS_PER_SOL);
const tx = await sessionTokenManager.program.methods.createSession(
topUp,
validUntilBN,
topUpLamportsBN
)
.accounts({
targetProgram: program.programId,
sessionSigner: sessionKeypair.publicKey,
authority: provider.wallet.publicKey,
})
.transaction();
const txHash = await sendAndConfirmTransaction(
provider.connection,
tx,
[sessionKeypair, provider.wallet.payer]
);
// Increment using the session key (no main wallet needed!)
let tx = await program.methods
.increment()
.accounts({
counter: counterPDA,
sessionToken: sessionTokenPDA,
payer: sessionKeypair.publicKey, // Session key signs instead of main wallet
})
.transaction();
// Only the session keypair signs - no wallet popup
const txHash = await sendAndConfirmTransaction(
providerEphemeralRollup.connection,
tx,
[sessionKeypair]
);
#[session_auth_or(
ctx.accounts.pda.authority.key() == ctx.accounts.payer.key(),
SessionError::InvalidToken
)]
pub fn delegate(ctx: Context<DelegateInput>) -> Result<()> {
ctx.accounts.delegate_pda(
&ctx.accounts.payer,
&[COUNTER_SEED, ctx.accounts.pda.authority.key().as_ref()],
DelegateConfig {
validator: ctx.remaining_accounts.first().map(|acc| acc.key()),
..Default::default()
},
)?;
Ok()
}
#[delegate]
#[derive(Accounts, Session)]
pub struct DelegateInput<'info> {
pub payer: Signer<'info>,
#[account(mut, del)]
pub pda: Account<'info, Counter>,
#[session(
signer = payer,
authority = pda.authority.key()
)]
pub session_token: Option<Account<'info, SessionToken>>,
}
Counter Account with Authority
Notice the counter includes anauthority field:
programs/anchor-counter-session/src/lib.rs
#[session_auth_or] macro to validate session tokens.
Session Token PDA
The session token PDA is derived from:tests/anchor-counter-session.ts
What Makes This Advanced?
Session Keys demonstrate advanced patterns:- Dual Authorization: Support both direct authority and delegated session access
- Time-Limited Security: Sessions expire automatically for safety
- Funded Sessions: Can pre-fund sessions to cover gas costs
- Macro-Based Validation: Use
#[session_auth_or]for clean, declarative auth logic - ER Integration: Works seamlessly with delegation and commitment flows
Complete Flow Example
Security Considerations
Use Cases
Session Keys are essential for:- Gaming: Players can make rapid moves without wallet approvals
- Social Apps: Continuous interactions without friction
- Trading Bots: Automated trading with limited permissions
- Mobile Apps: Better UX without constant wallet popups
Benefits in Ephemeral Rollups
Combining Session Keys with ERs provides:- Ultra-low latency + No wallet popups = Best possible UX
- Gasless ER transactions funded by the session
- Automatic state commitment using the session key
- Secure time-limited access to delegated accounts
Next Steps
- Explore Magic Actions to trigger base layer handlers on commit
- Learn about Cranks for automated execution
- Check the full source code