Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 100 additions & 6 deletions programs/gpl_session/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ cargo add session-keys --features no-entrypoint

# Usage

The latest version uses `SessionTokenV2`. `SessionToken` (V1) is still supported for backwards compatibility — see [V1 usage](#v1-backwards-compatible) below.

1. Import the dependencies

```rust
use session_keys::{SessionError, SessionToken, session_auth_or, Session};
use session_keys::{SessionError, SessionTokenV2, session_auth_or, Session};
```

2. Derive the `Session` trait on your instruction struct
2. Derive the `Session` trait on your instruction struct. The macro auto-detects V1 vs V2 from the `session_token` field type.

```rust
#[derive(Accounts, Session)]
Expand All @@ -34,15 +36,15 @@ pub struct Instruction<'info> {
authority = user.authority.key()
)]
// Session Tokens are passed as optional accounts
pub session_token: Option<Account<'info, SessionToken>>,
pub session_token: Option<Account<'info, SessionTokenV2>>,

#[account(mut)]
pub signer: Signer<'info>,
.....
}
```

3. Add the `session_auth_or` macro to your instruction handler with fallback logic on who the instruction should validate the signer when sessions are not present and an appropirate ErrorCode. If you've used `require*!` macros in anchor_lang you already know how this works.
3. Add the `session_auth_or` macro to your instruction handler with fallback logic on who the instruction should validate the signer when sessions are not present and an appropriate ErrorCode. If you've used `require*!` macros in anchor_lang you already know how this works.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider clarifying the wording.

The phrase "who the instruction should validate the signer when sessions are not present" is slightly unclear. Consider rephrasing to something like "how to validate the signer when no session token is present" for improved clarity.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 47-47: Ordered list item prefix
Expected: 1; Actual: 3; Style: 1/1/1

(MD029, ol-prefix)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@programs/gpl_session/README.md` at line 47, Reword the sentence to clarify
intended behavior: replace "who the instruction should validate the signer when
sessions are not present" with a clearer phrase such as "how to validate the
signer when no session token is present" and ensure the surrounding text still
explains adding the session_auth_or macro to your instruction handler with
fallback logic and an appropriate ErrorCode (reference: session_auth_or and any
require*! usage in the instruction handler).


```rust
#[session_auth_or(
Expand All @@ -55,6 +57,98 @@ pub fn ix_handler(ctx: Context<Instruction>,) -> Result<()> {

```

# Example
# V1 (backwards-compatible)

To keep using `SessionToken` (V1), import `SessionToken` instead of `SessionTokenV2` and use it as the field type:

```rust
use session_keys::{SessionError, SessionToken, session_auth_or, Session};

#[derive(Accounts, Session)]
pub struct Instruction<'info> {
...
pub session_token: Option<Account<'info, SessionToken>>,
...
}
```

`#[derive(Session)]` picks the right trait implementation based on the field type. You can also use `#[derive(SessionV2)]` to require V2 explicitly.

# Full V2 example

A complete Anchor instruction using `SessionTokenV2`:

```rust
use anchor_lang::prelude::*;
use session_keys::{session_auth_or, Session, SessionError, SessionTokenV2};

declare_id!("YourProgramID11111111111111111111111111111111");

#[program]
pub mod my_program {
use super::*;

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let counter = &mut ctx.accounts.counter;
counter.authority = *ctx.accounts.user.key;
counter.count = 0;
Ok(())
}

/// Either the session signer (via SessionTokenV2) or the original authority
/// can call this. If no session token is provided, the fallback check runs.
#[session_auth_or(
ctx.accounts.counter.authority.key() == ctx.accounts.signer.key(),
SessionError::InvalidToken
)]
pub fn increment(ctx: Context<Increment>) -> Result<()> {
ctx.accounts.counter.count += 1;
Ok(())
}
}

#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
pub user: Signer<'info>,
#[account(
init,
payer = user,
space = 8 + 32 + 8,
seeds = [b"counter", user.key().as_ref()],
bump,
)]
pub counter: Account<'info, Counter>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts, Session)]
pub struct Increment<'info> {
#[account(mut)]
pub signer: Signer<'info>,

#[account(
mut,
seeds = [b"counter", counter.authority.key().as_ref()],
bump,
)]
pub counter: Account<'info, Counter>,

#[session(
signer = signer,
authority = counter.authority.key()
)]
pub session_token: Option<Account<'info, SessionTokenV2>>,
}

#[account]
pub struct Counter {
pub authority: Pubkey,
pub count: u64,
}
```

# More examples

See [KamikazeJoe](https://github.com/magicblock-labs/Kamikaze-Joe) for an example of a game using session-keys.
- [magicblock-engine-examples / session-keys](https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/session-keys) — Anchor counter program using `SessionTokenV2` with the ephemeral rollups SDK, plus tests and a React frontend.
- [KamikazeJoe](https://github.com/magicblock-labs/Kamikaze-Joe) — a game using session-keys.
Loading