Sol.College

Full lesson

solana-lockbox

A decentralized password manager built on Solana blockchain with client-side encryption.

What it does

From the project README:

[](https://opensource.org/licenses/MIT) [](https://solana.com) [](https://nextjs.org) [](https://www.rust-lang.org)
Original
hackingbutlegal/solana-lockbox
SuperStack fork
Blockchains/solana-lockbox @ 63fe0e4
Stars
1
Last push (original)
2026-02-20
Languages
TypeScript, Rust, CSS, JavaScript, Shell, HTML
Detected stack
AnchorNext.jsweb3.js
Categories
Infrastructure & Tooling
Licence
MIT
Homepage
https://lockbox.web3stud.io

Architecture

208 source files. By extension: .ts ×102, .tsx ×76, .rs ×21, .js ×9.

Top-level directorySource files
nextjs-app157
programs21
sdk12
scripts12
tests4
(root)1
migrations1

On-chain programs: programs/lockbox

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/solana-lockbox/. 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.

hackingbutlegal/solana-lockbox · programs/lockbox/src/instructions/category_management.rs L13–36 @ 63fe0e4 · MIT · original
    pub master_lockbox: Account<'info, MasterLockbox>,     #[account(        init,        payer = owner,        space = 8 + CategoryRegistry::INIT_SPACE,        seeds = [CategoryRegistry::SEEDS_PREFIX, master_lockbox.key().as_ref()],        bump    )]    pub category_registry: Account<'info, CategoryRegistry>,     #[account(mut)]    pub owner: Signer<'info>,     pub system_program: Program<'info, System>,} pub fn initialize_category_registry_handler(ctx: Context<InitializeCategoryRegistry>) -> Result<()> {    let category_registry = &mut ctx.accounts.category_registry;    let master_lockbox = &ctx.accounts.master_lockbox;    let current_timestamp = Clock::get()?.unix_timestamp;     // Verify subscription tier supports categories (Basic and above)    require!(
  1. Line 16 init,: Creates and pays for a new account. Space and payer must be specified.
  2. Line 19 seeds = [CategoryRegistry::SEEDS_PREFIX, master_lockbox.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 20 bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 25 pub owner: Signer<'info>,: Requires this account to have signed the transaction.
  5. Line 33 let current_timestamp = Clock::get()?.unix_timestamp;: Reads on-chain time from the Clock sysvar.
  6. Line 36 require!(: An explicit validation that aborts the instruction with an error.
hackingbutlegal/solana-lockbox · programs/lockbox/src/instructions/subscription.rs L145–168 @ 63fe0e4 · MIT · original
} /// Downgrade to free tier (can only happen after subscription expires)#[derive(Accounts)]pub struct DowngradeSubscription<'info> {    #[account(        mut,        seeds = [MasterLockbox::SEEDS_PREFIX, owner.key().as_ref()],        bump = master_lockbox.bump,        constraint = master_lockbox.owner == owner.key() @ crate::errors::LockboxError::Unauthorized    )]    pub master_lockbox: Account<'info, MasterLockbox>,     #[account(mut)]    pub owner: Signer<'info>,} pub fn downgrade_subscription_handler(ctx: Context<DowngradeSubscription>) -> Result<()> {    let master_lockbox = &mut ctx.accounts.master_lockbox;    let current_timestamp = Clock::get()?.unix_timestamp;     // Can only downgrade if subscription expired    require!(        !master_lockbox.is_subscription_active(current_timestamp),
  1. Line 152 seeds = [MasterLockbox::SEEDS_PREFIX, owner.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  2. Line 153 bump = master_lockbox.bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  3. Line 159 pub owner: Signer<'info>,: Requires this account to have signed the transaction.
  4. Line 164 let current_timestamp = Clock::get()?.unix_timestamp;: Reads on-chain time from the Clock sysvar.
  5. Line 167 require!(: An explicit validation that aborts the instruction with an error.
hackingbutlegal/solana-lockbox · programs/lockbox/src/instructions/emergency_access_management.rs L325–348 @ 63fe0e4 · MIT · original
    // Verify countdown is active    require!(        emergency_access.status == EmergencyStatus::CountdownStarted,        LockboxError::NoActiveCountdown    );     emergency_access.cancel_countdown(clock.unix_timestamp);     msg!("Emergency countdown cancelled");     Ok(())} // ============================================================================// Account Validation Contexts// ============================================================================ #[derive(Accounts)]pub struct InitializeEmergencyAccess<'info> {    #[account(        init,        payer = owner,        space = 8 + EmergencyAccess::INIT_SPACE,        seeds = [b"emergency_access", owner.key().as_ref()],
  1. Line 326 require!(: An explicit validation that aborts the instruction with an error.
  2. Line 331 emergency_access.cancel_countdown(clock.unix_timestamp);: Reads on-chain time from the Clock sysvar.
  3. Line 333 msg!("Emergency countdown cancelled");: Logs to the program log, which is visible in explorers and the test output.
  4. Line 345 init,: Creates and pays for a new account. Space and payer must be specified.
  5. Line 348 seeds = [b"emergency_access", owner.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
hackingbutlegal/solana-lockbox · programs/lockbox/src/lib.rs L673–696 @ 63fe0e4 · MIT · original
pub struct StoreEncrypted<'info> {    /// The user's lockbox PDA (Program Derived Address)    /// Created on first use, updated on subsequent calls    #[account(        init_if_needed,        payer = user,        space = 8 + Lockbox::MAX_SIZE,        seeds = [b"lockbox", user.key().as_ref()],        bump    )]    pub lockbox: Account<'info, Lockbox>,     /// The user's wallet (must sign the transaction)    /// Pays for account creation and transaction fees    #[account(mut)]    pub user: Signer<'info>,     /// Fee receiver account (e.g., program treasury or operator wallet)    /// Receives 0.001 SOL per storage operation    /// CHECK: Fee receiver account (could be program treasury)    #[account(mut)]    pub fee_receiver: AccountInfo<'info>,     /// System program for account creation and SOL transfers
  1. Line 677 init_if_needed,: Creates the account only if it doesn't exist. Watch for re-initialisation bugs.
  2. Line 680 seeds = [b"lockbox", user.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 681 bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 688 pub user: Signer<'info>,: Requires this account to have signed the transaction.

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 devcd nextjs-app && npm run dev
.npm run buildcd nextjs-app && npm install && npm run build
.npm run testanchor test
.npm run lintprettier */*.js "*/**/*{.js,.ts}" --check
.npm run deployanchor deploy
sdknpm run buildtsc
nextjs-appnpm run devnext dev
nextjs-appnpm run startnext start
nextjs-appnpm run buildnext build
nextjs-appnpm run testjest
nextjs-appnpm run linteslint
nextjs-app/sdknpm run buildtsc
.anchor buildcompile programs and generate IDLs
.anchor testlocal validator + test suite
.anchor run testyarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts
.cargo buildRust workspace build

What you could build from it