Full lesson
kamui
Efficient Elliptic Curve VRF on Solana.
What it does
From the project README:
In `mangekyou`, one can compare all currently implemented cryptographic schemes by running:
- Original
- mangekyou-labs/kamui
- SuperStack fork
- Blockchains/kamui @
62454cc - Stars
- 1
- Last push (original)
- 2026-05-03
- Languages
- TypeScript, Rust, JavaScript, Solidity, Shell, Move
- Detected stack
- Anchorweb3.js
- Categories
- Other
- Licence
- Apache-2.0
- Homepage
- https://kamuipoc.vercel.app
Architecture
268 source files. By extension: .ts ×126, .rs ×89, .sol ×20, .go ×13, .js ×8, .move ×7.
| Top-level directory | Source files |
|---|---|
kamui-program | 118 |
iota | 45 |
crosschain-prover | 32 |
mangekyou | 30 |
vrf-server | 20 |
kamui-node | 10 |
mangekyou-cli | 5 |
example-vrf-game | 5 |
On-chain programs: kamui-program/programs/kamui-vrf, kamui-program/programs/kamui-vrf-consumer
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/kamui/. 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.
kamui-program/programs/kamui-vrf/src/light_compressed_vrf.rs L49–72 @ 62454cc · Apache-2.0 · original #[cfg(feature = "light-compression")]#[derive(Accounts)]#[instruction(seed: [u8; 32], callback_data: Vec<u8>, num_words: u32, minimum_confirmations: u8, callback_gas_limit: u64, pool_id: u8)]pub struct CreateCompressedVrfRequest<'info> { #[account(mut)] pub signer: Signer<'info>, #[account( init, payer = signer, space = 8 + std::mem::size_of::<CompressedVrfRequest>() + 200, // Extra space for dynamic data seeds = [b"COMPRESSED_VRF", seed.as_ref(), signer.key().as_ref()], bump )] pub vrf_request: Account<'info, CompressedVrfRequest>, pub system_program: Program<'info, System>,} #[cfg(feature = "light-compression")]#[derive(Accounts)]#[instruction(request_id: [u8; 32])]pub struct FulfillCompressedVrfRequest<'info> { #[account(mut)] pub authority: Signer<'info>,- Line 55
pub signer: Signer<'info>,: Requires this account to have signed the transaction. - Line 57
init,: Creates and pays for a new account. Space and payer must be specified. - Line 60
seeds = [b"COMPRESSED_VRF", seed.as_ref(), signer.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 61
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
kamui-program/programs/kamui-vrf/src/state.rs L325–347 @ 62454cc · Apache-2.0 · original expired_count }} // Context structs for instructions #[derive(Accounts)]pub struct CreateEnhancedSubscription<'info> { #[account(mut)] pub owner: Signer<'info>, #[account( init, payer = owner, space = 8 + EnhancedSubscription::INIT_SPACE, seeds = [b"subscription", seed.key().as_ref()], bump )] pub subscription: Account<'info, EnhancedSubscription>, /// CHECK: This account is used only as a seed for the subscription PDA and is not read or written to pub seed: AccountInfo<'info>,- Line 335
pub owner: Signer<'info>,: Requires this account to have signed the transaction. - Line 338
init,: Creates and pays for a new account. Space and payer must be specified. - Line 341
seeds = [b"subscription", seed.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 342
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
kamui-program/programs/kamui-vrf-consumer/src/lib.rs L205–228 @ 62454cc · Apache-2.0 · original }} #[derive(Accounts)]pub struct Initialize<'info> { #[account(mut)] pub owner: Signer<'info>, #[account( init, payer = owner, space = 8 + 32 + 1 + 8 + 8 + 8 + 32, // Added space for last_verification_hash seeds = [b"game", owner.key().as_ref()], bump )] pub game_state: Account<'info, GameState>, pub system_program: Program<'info, System>,} #[derive(Accounts)]pub struct VerifyVrfProof<'info> { pub signer: Signer<'info>,}- Line 211
pub owner: Signer<'info>,: Requires this account to have signed the transaction. - Line 214
init,: Creates and pays for a new account. Space and payer must be specified. - Line 217
seeds = [b"game", owner.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 218
bump: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
vrf-server/src/processor.rs L349–372 @ 62454cc · Apache-2.0 · original proof: proof.clone(), proof_block: 0, // Will be set by the runtime }; // Check if VRF result account already exists let vrf_result_data_len = vrf_result_account.data_len(); if vrf_result_data_len == 0 { // Create new VRF result account let space = borsh::to_vec(&vrf_result)?.len() + 8; // Add 8 bytes for discriminator let rent = Rent::get()?; let lamports = rent.minimum_balance(space); // Verify VRF result PDA let (expected_vrf_result, bump) = Pubkey::find_program_address( &[b"vrf_result", requester.as_ref()], program_id ); if expected_vrf_result != *vrf_result_account.key { return Err(ProgramError::InvalidSeeds); } invoke_signed( &system_instruction::create_account( oracle.key,- Line 362
let (expected_vrf_result, bump) = Pubkey::find_program_address(: Derives a PDA from seeds and the program ID and returns the address with its canonical bump. - Line 362
let (expected_vrf_result, bump) = Pubkey::find_program_address(: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 370
invoke_signed(: 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.
| Directory | Command | Runs |
|---|---|---|
. | npm run dev | LOG_LEVEL=debug node standalone-vrf-server.js |
. | npm run start | node standalone-vrf-server.js |
. | npm run test | node standalone-vrf-server.js --test-cli |
. | npm run build | cargo build --bin ecvrf-cli --release |
crosschain-prover/contracts | npm run dev | pnpm mud dev-contracts --tsgenOutput ../client/src/mud |
crosschain-prover/contracts | npm run build | forge clean && bash build_with_abi.sh |
crosschain-prover/contracts | npm run test | tsc --noEmit && mud test |
crosschain-prover/contracts | npm run lint | pnpm run prettier && pnpm run solhint |
crosschain-prover/contracts | npm run deploy | pnpm run initialize && mud deploy --profile=hackathon-testnet |
vrf-server/nodejs | npm run start | node vrf-server-nodejs.js |
kamui-program | npm run build | anchor build |
kamui-program | npm run test | anchor test |
kamui-program/app | npm run dev | vite |
kamui-program/app | npm run build | tsc && vite build |
kamui-program/app | npm run lint | eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 |
iota/node | npm run dev | ts-node src/index.ts |
What you could build from it
- Fork the SuperStack copy and extend it within the Other gaps listed on the idea page.
- Learn the pattern first: Program Derived Addresses (PDAs)
- Learn the pattern first: Cross-Program Invocations (CPIs)