Sol.College

Full lesson

molpha-solana-program

What it does

From the project README:

The on-chain half of the Molpha oracle network on Solana: a versioned node registry, USDC subscriptions, feeds updated by threshold Schnorr attestations, and epoch-based node rewards. The attestation format is shared with Molpha's EVM contracts, so one coalition signature verifies on both.
Original
molpha/molpha-solana-program
SuperStack fork
Blockchains/molpha-solana-program @ 31d65f0
Stars
0
Last push (original)
2026-10-27
Languages
Rust, TypeScript, JavaScript, Python
Detected stack
@solana/kitAnchorweb3.js
Categories
Other
Licence
Apache-2.0

Architecture

147 source files. By extension: .rs ×103, .ts ×35, .js ×7, .py ×2.

Top-level directorySource files
programs67
molpha-tests36
cli35
scripts9

On-chain programs: programs/molpha

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/molpha-solana-program/. 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.

molpha/molpha-solana-program · programs/molpha/src/instructions/gateway/deactivate_gateway.rs L13–36 @ 31d65f0 · Apache-2.0 · original
    gateway.status = GatewayStatus::Deactivated;     emit!(GatewayDeactivated {        gateway: gateway.key(),        authority: gateway.authority,        deactivated_at: clock.unix_timestamp,    });    Ok(())} #[derive(Accounts)]pub struct DeactivateGateway<'info> {    #[account(mut)]    pub authority: Signer<'info>,     #[account(        has_one = authority,        seeds = [ProtocolConfig::SEED_PREFIX],        bump = protocol_config.bump,    )]    pub protocol_config: Account<'info, ProtocolConfig>,     #[account(        mut,
  1. Line 15 emit!(GatewayDeactivated {: Emits an Anchor event that clients can parse from the logs.
  2. Line 18 deactivated_at: clock.unix_timestamp,: Reads on-chain time from the Clock sysvar.
  3. Line 26 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
  4. Line 29 has_one = authority,: Checks that a stored pubkey field matches the passed account.
  5. Line 30 seeds = [ProtocolConfig::SEED_PREFIX],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  6. Line 31 bump = protocol_config.bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
molpha/molpha-solana-program · programs/molpha/src/instructions/revenue/withdraw_protocol_reserve.rs L25–48 @ 31d65f0 · Apache-2.0 · original
            &[&ctx.accounts.protocol_config.seeds()],        ),        amount,    )?;     emit!(ProtocolReserveWithdrawn {        destination: ctx.accounts.destination.key(),        amount,        withdrawn_at: Clock::get()?.unix_timestamp,    });     Ok(())} #[derive(Accounts)]pub struct WithdrawProtocolReserve<'info> {    pub authority: Signer<'info>,     #[account(        mut,        has_one = authority,        seeds = [ProtocolConfig::SEED_PREFIX],        bump = protocol_config.bump,    )]
  1. Line 30 emit!(ProtocolReserveWithdrawn {: Emits an Anchor event that clients can parse from the logs.
  2. Line 33 withdrawn_at: Clock::get()?.unix_timestamp,: Reads on-chain time from the Clock sysvar.
  3. Line 41 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
  4. Line 45 has_one = authority,: Checks that a stored pubkey field matches the passed account.
  5. Line 46 seeds = [ProtocolConfig::SEED_PREFIX],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  6. Line 47 bump = protocol_config.bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
molpha/molpha-solana-program · programs/molpha/src/instructions/admin/update_registry_policy.rs L25–48 @ 31d65f0 · Apache-2.0 · original
        redundancy_buffer,    )?;     emit!(RegistryUpdated {        new_version: ctx.accounts.registry_state.current_version,        node_count,        redundancy_buffer,        reason: RegistryVersionReason::PolicyChanged,        updated_at: Clock::get()?.unix_timestamp,    });    Ok(())} #[derive(Accounts)]pub struct UpdateRegistryPolicy<'info> {    #[account(mut)]    pub authority: Signer<'info>,     #[account(        has_one = authority,        seeds = [ProtocolConfig::SEED_PREFIX],        bump    )]    pub protocol_config: Account<'info, ProtocolConfig>,
  1. Line 28 emit!(RegistryUpdated {: Emits an Anchor event that clients can parse from the logs.
  2. Line 33 updated_at: Clock::get()?.unix_timestamp,: Reads on-chain time from the Clock sysvar.
  3. Line 41 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
  4. Line 44 has_one = authority,: Checks that a stored pubkey field matches the passed account.
  5. Line 45 seeds = [ProtocolConfig::SEED_PREFIX],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  6. Line 46 bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
molpha/molpha-solana-program · programs/molpha/src/instructions/slash_offence.rs L73–96 @ 31d65f0 · Apache-2.0 · original
        node_count,        redundancy_buffer,        reason: RegistryVersionReason::NodePunished,        updated_at: clock.unix_timestamp,    });     // Cancelled reward credits stop being a claim on the treasury.    config.reward_liability = config        .reward_liability        .checked_sub(batch.slashed_rewards)        .ok_or(error!(MolphaError::RewardLiabilityUnderflow))?;     // Sweep the forfeited principal out of the deposit vault in one transfer, whatever the size    // of the guilty set. The amount comes directly from the guilty nodes' locked balances.    if batch.slashed_locked > 0 {        require!(            ctx.accounts.node_deposit_vault.amount >= batch.slashed_locked,            MolphaError::DepositVaultUnderfunded        );         token::transfer(            CpiContext::new_with_signer(                ctx.accounts.token_program.key(),                Transfer {
  1. Line 76 updated_at: clock.unix_timestamp,: Reads on-chain time from the Clock sysvar.
  2. Line 82 .checked_sub(batch.slashed_rewards): Uses checked arithmetic so overflow fails instead of wrapping.
  3. Line 88 require!(: An explicit validation that aborts the instruction with an error.
  4. Line 93 token::transfer(: An SPL token transfer through the Token Program.
  5. Line 94 CpiContext::new_with_signer(: 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 buildyarn build:cli && yarn build:molpha
.npm run testanchor build -p molpha && cargo test --workspace
.npm run lintprettier --check "cli/**/*.{js,ts}" "scripts/**/*.{js,mjs,ts}"
.anchor buildcompile programs and generate IDLs
.anchor testlocal validator + test suite
.anchor run testcargo test --workspace
.cargo buildRust workspace build
.cargo testRust tests

What you could build from it

Used in lessons