Sol.College

Full lesson

valhalla

Solana LP/Token Locker

What it does

From the project README:

You can use any normal anchor commands. Either move to the `anchor` directory and run the `anchor` command or prefix the command with `npm run`, eg: `npm run anchor`.
Original
restacked-dev/valhalla
SuperStack fork
Blockchains/valhalla @ 7aa9ddf
Stars
8
Last push (original)
2024-04-15
Languages
TypeScript, Rust, JavaScript, CSS
Detected stack
AnchorMetaplexNext.jsweb3.js
Categories
Other
Licence
MIT

Architecture

146 source files. By extension: .tsx ×62, .ts ×58, .rs ×21, .js ×5.

Top-level directorySource files
web74
anchor32
lib31
scheduler6
(root)3

On-chain programs: anchor/programs/valhalla

🟡 Builds, verified 2026-11-02 Tested on 2026-11-02: full log results.json

Build it yourself: step-by-step guide

Every command below was run on 2026-11-02 by tests/run_fork.py against the pinned commit, and the results come from that run. The full output is in the committed log.

1. Prerequisites (versions we tested with)

cargo install --git https://github.com/coral-xyz/anchor avm --locked
avm install 0.29.0 && avm use 0.29.0
sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"

2. Clone at the tested commit

git clone https://github.com/Blockchains/valhalla.git && cd valhalla
git checkout 7aa9ddfb149f6f8b97efc5d523ca03a8b76aebf6

3. Configure

No environment file is needed.

4. Install dependencies

DirectoryCommandResultTime
.npx -y yarn@1 install --ignore-engines --network-timeout 600000✅ pass22s
anchornpx -y yarn@1 install --ignore-engines --network-timeout 600000✅ pass2s
anchornpx -y yarn@1 install --ignore-engines --network-timeout 600000✅ pass1s

5. Build

DirectoryCommandResultTime
anchoranchor@0.29.0 build✅ pass92s
.npx -y yarn@1 run web:build✅ pass156s

6. Test

DirectoryCommandResultTime
anchoranchor@0.29.0 test --skip-build --provider.cluster localnet --provider.wallet ~/.config/solana/id.json❌ exit 123s
Output tail: anchor@0.29.0 test --skip-build --provider.cluster localnet --provider.wallet ~/
      at fulfilled (../node_modules/@solana/spl-token/lib/cjs/state/account.js:5:58)
Test Suites: 1 failed, 1 total
Tests:       26 failed, 26 total
Snapshots:   0 total
Time:        15.251 s
Ran all test suites.

7. Deploy to a local validator

solana-test-validator --reset   # separate terminal
solana config set -ul && solana airdrop 100
anchor deploy --provider.cluster localnet
DirectoryCommandResultTime
anchorsolana airdrop 100 -u localhost >/dev/null; anchor@0.29.0 deploy --provider.cluster localnet --provider.wallet ~/.config/solana/id.json✅ pass8s

Proof: program IDs and transactions

Transaction signatures recorded on the local validator (1 shown). Local ledgers are thrown away after the run, so these work as receipts in the log and can't be looked up on an explorer:

Deploy to devnet (free)

solana config set --url devnet
solana airdrop 2   # or https://faucet.solana.com
anchor deploy --provider.cluster devnet   # native: solana program deploy target/deploy/<name>.so
# point the frontend RPC env var at https://api.devnet.solana.com

Notes: Anchor.toml clones the Metaplex token-metadata program from a public mainnet RPC into the local validator. That's read-only, and no mainnet transaction is sent.

How it works

Every instruction handler in the program, with the accounts struct it validates. Each link points to the line at the pinned commit.

InstructionAccountsWhere
update_adminUpdateAdminanchor/programs/valhalla/src/lib.rs:76
update_dao_treasuryUpdateDaoTreasuryanchor/programs/valhalla/src/lib.rs:89
update_dev_feeUpdateDevFeeanchor/programs/valhalla/src/lib.rs:103
mint_governance_tokensMintGovernanceTokensanchor/programs/valhalla/src/lib.rs:151
disburseDisburseVaultanchor/programs/valhalla/src/lib.rs:205
closeCloseVaultanchor/programs/valhalla/src/lib.rs:218
cancelCancelVaultanchor/programs/valhalla/src/lib.rs:231

Extend it

  1. Add an integration test that runs the main flow end to end and asserts every account state change.
  2. Add a negative test: call each instruction with the wrong signer or account and check that it fails.
  3. Add an event (emit!) to each state-changing instruction and subscribe to it from the client.
  4. Replace any unchecked AccountInfo with typed accounts plus has_one/seeds constraints.
  5. Wire the build into CI by copying this site's verify-forks workflow.

Key code, explained

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

restacked-dev/valhalla · anchor/programs/valhalla/src/instructions/disburse.rs L1–24 @ 7aa9ddf · MIT · original
use anchor_lang::prelude::*;use anchor_spl::{    associated_token::AssociatedToken,    token_interface::{        mint_to, transfer_checked, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked,    },}; use crate::{constants, errors::ValhallaError, state::Vault, Config}; #[derive(Accounts)]pub struct DisburseVault<'info> {    #[account(mut)]    pub signer: Signer<'info>,     pub creator: SystemAccount<'info>,     pub recipient: SystemAccount<'info>,     #[account(mut)]    pub dev_treasury: SystemAccount<'info>,     #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]    pub config: Box<Account<'info, Config>>,
  1. Line 5 mint_to, transfer_checked, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked,: A checked token transfer, which also verifies the mint and decimals.
  2. Line 5 mint_to, transfer_checked, Mint, MintTo, TokenAccount, TokenInterface, TransferChecked,: Mints new tokens. The mint authority must sign.
  3. Line 14 pub signer: Signer<'info>,: Requires this account to have signed the transaction.
  4. Line 23 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  5. Line 23 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  6. Line 23 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Checks that a stored pubkey field matches the passed account.
restacked-dev/valhalla · anchor/programs/valhalla/src/instructions/create.rs L13–36 @ 7aa9ddf · MIT · original
    Authority,}; #[derive(Accounts)]#[instruction(identifier: u64)]pub struct CreateVault<'info> {    #[account(mut)]    pub creator: Signer<'info>,     #[account(mut)]    pub recipient: SystemAccount<'info>,     #[account(mut)]    pub dev_treasury: SystemAccount<'info>,     #[account(mut)]    pub dao_treasury: SystemAccount<'info>,     #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]    pub config: Box<Account<'info, Config>>,     #[account(        init,        payer = creator,
  1. Line 20 pub creator: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 31 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 31 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 31 #[account(seeds = [constants::CONFIG_SEED], bump, has_one = dev_treasury)]: Checks that a stored pubkey field matches the passed account.
  5. Line 35 init,: Creates and pays for a new account. Space and payer must be specified.
restacked-dev/valhalla · anchor/programs/valhalla/src/instructions/admin/update_governance_token_amount.rs L1–24 @ 7aa9ddf · MIT · original
use anchor_lang::prelude::*; use crate::{constants, errors::ValhallaError, state::Config}; #[derive(Accounts)]pub struct UpdateGovernanceTokenAmount<'info> {    #[account(mut)]    pub admin: Signer<'info>,     #[account(        mut,        seeds = [constants::CONFIG_SEED],        bump,        has_one = admin,    )]    pub config: Box<Account<'info, Config>>,} impl<'info> UpdateGovernanceTokenAmount<'info> {    pub fn update(&mut self, governance_token_amount: u64) -> Result<()> {        // Ensure that the caller is authorized to update the configuration.        require!(            self.config.admin == self.admin.key(),            ValhallaError::Unauthorized
  1. Line 8 pub admin: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 12 seeds = [constants::CONFIG_SEED],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 13 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 14 has_one = admin,: Checks that a stored pubkey field matches the passed account.
  5. Line 22 require!(: An explicit validation that aborts the instruction with an error.
restacked-dev/valhalla · anchor/programs/valhalla/src/instructions/admin/update_token_fee_basis_points.rs L1–24 @ 7aa9ddf · MIT · original
use anchor_lang::prelude::*; use crate::{constants, errors::ValhallaError, state::Config}; #[derive(Accounts)]pub struct UpdateTokenFeeBasisPoints<'info> {    #[account(mut)]    pub admin: Signer<'info>,     #[account(        mut,        seeds = [constants::CONFIG_SEED],        bump,        has_one = admin,    )]    pub config: Box<Account<'info, Config>>,} impl<'info> UpdateTokenFeeBasisPoints<'info> {    pub fn update(&mut self, token_fee_basis_points: u64) -> Result<()> {        // Ensure that the caller is authorized to update the configuration.        require!(            self.config.admin == self.admin.key(),            ValhallaError::Unauthorized
  1. Line 8 pub admin: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 12 seeds = [constants::CONFIG_SEED],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 13 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 14 has_one = admin,: Checks that a stored pubkey field matches the passed account.
  5. Line 22 require!(: An explicit validation that aborts the instruction with an error.

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
anchoranchor buildcompile programs and generate IDLs
anchoranchor testlocal validator + test suite
anchoranchor run create-datayarn run ts-node ./scripts/create-data.ts
anchoranchor run inityarn ts-node ./scripts/init.ts
anchoranchor run test../node_modules/.bin/nx run anchor:jest

What you could build from it

Used in lessons