Sol.College

Full lesson

hakata-perps

Core Perpetual Futures Engine for Real-World Asset Trading on Solana

What it does

From the project README:

Hakata Perpetuals is Core Perpetual Futures Engine for Real-World Asset Trading on Solana
Original
hakata-finance/hakata-perps
SuperStack fork
Blockchains/hakata-perps @ 18aac3e
Stars
6
Last push (original)
2025-06-23
Languages
TypeScript, Rust, CSS, JavaScript
Detected stack
AnchorNext.jsweb3.js
Categories
DeFi
Licence
Apache-2.0
Homepage
https://app.hakata.fi

Architecture

178 source files. By extension: .tsx ×98, .rs ×44, .ts ×36.

Top-level directorySource files
packages178

On-chain programs: packages/core/programs/hakata-perpetuals

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/hakata-perps/. 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.

hakata-finance/hakata-perps · packages/core/programs/hakata-perpetuals/src/instructions/remove_collateral.rs L13–36 @ 18aac3e · Apache-2.0 · original
    anchor_spl::token::{Token, TokenAccount},}; #[derive(Accounts)]#[instruction(params: RemoveCollateralParams)]pub struct RemoveCollateral<'info> {    #[account(mut)]    pub owner: Signer<'info>,     #[account(        mut,        constraint = receiving_account.mint == custody.mint,        has_one = owner    )]    pub receiving_account: Box<Account<'info, TokenAccount>>,     /// CHECK: empty PDA, authority for token accounts    #[account(        seeds = [b"transfer_authority"],        bump = perpetuals.transfer_authority_bump    )]    pub transfer_authority: AccountInfo<'info>,     #[account(
  1. Line 20 pub owner: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 25 has_one = owner: Checks that a stored pubkey field matches the passed account.
  3. Line 31 seeds = [b"transfer_authority"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 32 bump = perpetuals.transfer_authority_bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
hakata-finance/hakata-perps · packages/core/programs/hakata-perpetuals/src/instructions/swap.rs L13–36 @ 18aac3e · Apache-2.0 · original
#[derive(Accounts)]#[instruction(params: SwapParams)]pub struct Swap<'info> {    #[account(mut)]    pub owner: Signer<'info>,     #[account(        mut,        constraint = funding_account.mint == receiving_custody.mint,        has_one = owner    )]    pub funding_account: Box<Account<'info, TokenAccount>>,     #[account(        mut,        constraint = receiving_account.mint == dispensing_custody.mint,        has_one = owner    )]    pub receiving_account: Box<Account<'info, TokenAccount>>,     /// CHECK: empty PDA, authority for token accounts    #[account(        seeds = [b"transfer_authority"],        bump = perpetuals.transfer_authority_bump
  1. Line 17 pub owner: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 22 has_one = owner: Checks that a stored pubkey field matches the passed account.
  3. Line 35 seeds = [b"transfer_authority"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 36 bump = perpetuals.transfer_authority_bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
hakata-finance/hakata-perps · packages/core/programs/hakata-perpetuals/src/instructions/open_position.rs L13–36 @ 18aac3e · Apache-2.0 · original
        },    },    anchor_lang::{prelude::*, solana_program::program_error::ProgramError},    anchor_spl::token::{Token, TokenAccount},}; #[derive(Accounts)]#[instruction(params: OpenPositionParams)]pub struct OpenPosition<'info> {    #[account(mut)]    pub owner: Signer<'info>,     #[account(        mut,        constraint = funding_account.mint == custody.mint,        has_one = owner    )]    pub funding_account: Box<Account<'info, TokenAccount>>,     /// CHECK: empty PDA, authority for token accounts    #[account(        seeds = [b"transfer_authority"],        bump = perpetuals.transfer_authority_bump    )]
  1. Line 23 pub owner: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 28 has_one = owner: Checks that a stored pubkey field matches the passed account.
  3. Line 34 seeds = [b"transfer_authority"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 35 bump = perpetuals.transfer_authority_bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
hakata-finance/hakata-perps · packages/core/programs/hakata-perpetuals/src/instructions/init.rs L1–24 @ 18aac3e · Apache-2.0 · original
//! Init instruction handler use {    crate::{        error::PerpetualsError,        state::{multisig::Multisig, perpetuals::Perpetuals},    },    anchor_lang::prelude::*,    anchor_spl::token::Token,}; #[derive(Accounts)]pub struct Init<'info> {    #[account(mut)]    pub upgrade_authority: Signer<'info>,     #[account(        init,        payer = upgrade_authority,        space = Multisig::LEN,        seeds = [b"multisig"],        bump    )]    pub multisig: AccountLoader<'info, Multisig>,
  1. Line 15 pub upgrade_authority: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 18 init,: Creates and pays for a new account. Space and payer must be specified.
  3. Line 21 seeds = [b"multisig"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 22 bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.

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
packages/corenpm run lintprettier */*.js "*/**/*{.js,.ts}" --check
packages/uinpm run devnext dev --turbopack
packages/uinpm run startnext start
packages/uinpm run buildnext build
packages/uinpm run lintnext lint
packages/uinpm run deploynpm run pages:build && wrangler pages deploy
packages/coreanchor buildcompile programs and generate IDLs
packages/coreanchor testlocal validator + test suite
packages/coreanchor run testyarn run ts-mocha -p ./tsconfig.json -t 1000000 programs/hakata-perpetuals/tests/anchor/*.ts

What you could build from it

Used in lessons