Sol.College

Full lesson

accel-AgentAuth

What it does

From the project README:

Solana authenticates keys; it does not say what a delegated key is allowed to do. An agent that holds a key can do whatever the key can do, including what a prompt injection tells it to.
Original
Turbin3/accel-AgentAuth
SuperStack fork
Blockchains/accel-AgentAuth @ 5a74154
Stars
2
Last push (original)
2026-10-13
Languages
TypeScript, Rust, Shell, JavaScript, Python, CSS
Detected stack
@solana/kitNext.jsPinocchio
Categories
AI & AgentsWallets & Identity
Licence
MIT

Architecture

374 source files. By extension: .ts ×215, .rs ×106, .tsx ×43, .js ×7, .py ×3.

Top-level directorySource files
packages150
apps115
programs65
tests18
src12
crates8
security6

On-chain programs: programs/evil-token, programs/gated-example, programs/mock-amm, programs/relay, programs/relay/tests/common/stubs/always_ok

Dapp: devnet deploy pending

⏳ Devnet deploy pending — goes live when the devnet wallet is topped up. This fork's own dapp is not deployed yet, so there is no program address or transaction to show. It is queued in dapp-queue.txt; once devnet SOL is available it is deployed, exercised with real devnet transactions, and this section switches to the live dapp at /dapps/accel-agentauth/. Until then, the lesson and step-by-step guide below are complete, and you can run it locally with the commands below.

Key code, explained

Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.

Turbin3/accel-AgentAuth · programs/mock-amm/src/swap.rs L109–132 @ 5a74154 · MIT · original
        token_program: s.in_token_program,        from: s.in_account,        mint: s.in_mint,        to: s.in_vault,        authority: s.payer,        amount: pull,        decimals: s.in_decimals,    }    .invoke_signed(&[])?;     // 2. pay the output from the output vault, authority = pool PDA.    let destination = if s.pool.mode == Mode::RedirectOutput {        s.in_account    } else {        s.out_account    };    let bump = [POOL_AUTHORITY_BUMP];    let seeds = [Seed::from(POOL_AUTHORITY_SEED), Seed::from(&bump)];    TransferChecked {        token_program: s.out_token_program,        from: s.out_vault,        mint: s.out_mint,        to: destination,        authority: s.authority,
  1. Line 117 .invoke_signed(&[])?;: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.
  2. Line 125 let bump = [POOL_AUTHORITY_BUMP];: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  3. Line 126 let seeds = [Seed::from(POOL_AUTHORITY_SEED), Seed::from(&bump)];: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 127 TransferChecked {: A checked token transfer, which also verifies the mint and decimals.
Turbin3/accel-AgentAuth · src/instructions/create_mandate.rs L37–60 @ 5a74154 · MIT · original
    };    if !owner.is_signer() {        return Err(MandateError::InvalidSigner.into());    }    if owner.key() == agent.key() {        return Err(MandateError::InvalidAgent.into());    }    let (expiry, rest) = take_u64(data)?;    expect_end(rest)?;    let expiry = expiry as i64;     let (expected, bump) = find_program_address(        &[MANDATE_SEED, owner.key().as_ref(), agent.key().as_ref()],        program_id,    );    if mandate_account.key() != &expected {        return Err(MandateError::InvalidAccountType.into());    }    if expiry <= Clock::get()?.unix_timestamp {        return Err(MandateError::InvalidExpiry.into());    }    if !mandate_account.is_writable() {        return Err(ProgramError::InvalidAccountData);    }
  1. Line 48 let (expected, bump) = find_program_address(: Derives a PDA from seeds and the program ID and returns the address with its canonical bump.
  2. Line 48 let (expected, bump) = find_program_address(: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  3. Line 55 if expiry <= Clock::get()?.unix_timestamp {: Reads on-chain time from the Clock sysvar.
Turbin3/accel-AgentAuth · programs/relay/src/cpi.rs L61–84 @ 5a74154 · MIT · original
        AccountMeta::readonly(owner.key()),        AccountMeta::readonly_signer(agent.key()),    ];    let ix = Instruction {        program_id: &AGENT_AUTH_ID,        accounts: &metas,        data: &data,    };    invoke_signed_with_bounds::<3>(&ix, &[mandate, owner, agent], &[])} /// Accounts of the `TransferChecked` CPI.pub struct TransferChecked<'a> {    /// SPL Token or Token-2022 program.    pub token_program: &'a AccountInfo,    /// Source token account (writable).    pub source: &'a AccountInfo,    /// Mint (read-only).    pub mint: &'a AccountInfo,    /// Destination token account (writable).    pub destination: &'a AccountInfo,    /// Authority PDA, the SPL delegate; signs with the stored bump.    pub authority: &'a AccountInfo,    /// Mandate key, the second seed of the authority PDA.
  1. Line 69 invoke_signed_with_bounds::<3>(&ix, &[mandate, owner, agent], &[]): Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.
  2. Line 72 /// Accounts of the `TransferChecked` CPI.: A checked token transfer, which also verifies the mint and decimals.
  3. Line 82 /// Authority PDA, the SPL delegate; signs with the stored bump.: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
Turbin3/accel-AgentAuth · programs/relay/src/ix/swap.rs L313–336 @ 5a74154 · MIT · original
        AccountMeta::writable(a.output_vault.key()),        AccountMeta::readonly(a.in_token_program.key()),        AccountMeta::readonly(a.out_token_program.key()),        AccountMeta::readonly(a.in_mint.key()),        AccountMeta::readonly(a.out_mint.key()),        AccountMeta::writable(a.observation_state.key()),    ];    let ix = Instruction {        program_id: a.amm_program.key(),        accounts: &metas,        data: &data,    };    let bump = [authority_bump];    let seeds = [        Seed::from(AUTHORITY_SEED),        Seed::from(a.mandate.key()),        Seed::from(&bump),    ];    let signer = Signer::from(&seeds);    invoke_signed_with_bounds::<13>(        &ix,        &[            a.authority,            a.amm_authority,
  1. Line 325 let bump = [authority_bump];: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  2. Line 326 let seeds = [: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 332 invoke_signed_with_bounds::<13>(: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.

How to run, build and test

These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.

DirectoryCommandRuns
.npm run buildpnpm -r --if-present run build
.npm run testpnpm -r --if-present run test
.npm run lintpnpm -r --if-present run lint
apps/dashboardnpm run devnext dev
apps/dashboardnpm run startnext start
apps/dashboardnpm run buildnext build
apps/dashboardnpm run testvitest run
apps/dashboardnpm run linteslint . && tsc --noEmit && prettier --check "src/**/*.{ts,tsx,css}" "tests/**/*.{ts,tsx}" "e2e/**/*.ts" "*.ts" "*.mjs"
apps/demonpm run testvitest run
apps/demonpm run linttsc --noEmit && prettier --check "src/**/*.ts" "test/**/*.ts" "live-agent/**/*.ts" vitest.config.ts
packages/clinpm run buildtsup
packages/clinpm run testvitest run
packages/clinpm run linttsc --noEmit && prettier --check "src/**/*.ts" "test/**/*.ts" tsup.config.ts vitest.config.ts
packages/mcpnpm run buildtsup
packages/mcpnpm run testvitest run
packages/mcpnpm run linttsc --noEmit && prettier --check "src/**/*.ts" "test/**/*.ts" "scripts/**/*.ts" tsup.config.ts vitest.config.ts

What you could build from it

Used in lessons