Sol.College

Full lesson

Daddy-s-Club

Revenue bonds for Solana protocols — repaid automatically from a share of fees, in the same transaction the fee arises.

What it does

From the project README:

A protocol that already earns fees issues a short-term bond with a fixed face value and coupon. The bond repays itself: an agreed share of the protocol's fees is split off at the moment a fee arises and distributed to bondholders until face plus coupon has been paid out. After that the interception stops on its own. Bonds trade until maturity.
Original
daddyclub/Daddy-s-Club
SuperStack fork
Blockchains/Daddy-s-Club @ f3e8495
Stars
0
Last push (original)
2026-10-28
Languages
Rust, TypeScript, HTML, CSS, JavaScript
Detected stack
Anchorweb3.js
Categories
Other
Licence
MIT
Homepage
https://daddyclub.github.io/Daddy-s-Club/

Architecture

103 source files. By extension: .ts ×50, .rs ×27, .tsx ×24, .js ×2.

Top-level directorySource files
apps52
programs27
packages14
scripts10

On-chain programs: programs/daddys-club, programs/daddys-market, programs/demo-issuer

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/daddy-s-club/. 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.

daddyclub/Daddy-s-Club · programs/daddys-club/src/instructions/invest.rs L49–72 @ f3e8495 · MIT · original
        token_2022::{            burn_checked, mint_to, transfer_checked, BurnChecked, MintTo, Token2022,            TransferChecked,        },        token_interface::{Mint, TokenAccount},    },}; #[derive(Accounts)]pub struct OpenPosition<'info> {    /// Seeds випуску містять `seq` і джерело, яких у цьому наборі немає, — але    /// перевіряти їх тут і не треба: `Account<Issue>` уже вимагає власника-нашу    /// програму й дискримінатор `Issue`, а такий акаунт з'являється лише з    /// `create_issue`. Підробити його поза програмою нічим.    pub issue: Account<'info, Issue>,     /// Ті самі seeds, що їх резолвить список гука: випуск і власник. Збіг    /// доводить `the_hook_resolves_to_the_ledger_this_instruction_creates`.    #[account(        init,        payer = payer,        space = 8 + HolderCheckpoint::INIT_SPACE,        seeds = [HOLDER_SEED, issue.key().as_ref(), owner.key().as_ref()],        bump,
  1. Line 49 token_2022::{: Targets the Token-2022 program instead of the original Token Program.
  2. Line 50 burn_checked, mint_to, transfer_checked, BurnChecked, MintTo, Token2022,: A checked token transfer, which also verifies the mint and decimals.
  3. Line 50 burn_checked, mint_to, transfer_checked, BurnChecked, MintTo, Token2022,: Mints new tokens. The mint authority must sign.
  4. Line 68 init,: Creates and pays for a new account. Space and payer must be specified.
  5. Line 71 seeds = [HOLDER_SEED, issue.key().as_ref(), owner.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  6. Line 72 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
daddyclub/Daddy-s-Club · programs/daddys-market/src/instructions/market.rs L157–180 @ f3e8495 · MIT · original
    /// чужого випуску в цей набір не сходиться.    #[account(has_one = bond_mint)]    pub issue: Box<Account<'info, Issue>>,     #[account(mut)]    pub seller: Signer<'info>,     /// Рахунок, з якого їде бонд. `token::authority` тут не зайвий попри    /// підпис: продати можна лише своє, а не те, на що виписано делегата.    #[account(mut, token::mint = bond_mint, token::authority = seller)]    pub seller_bond: Box<InterfaceAccount<'info, TokenAccount>>,     pub bond_mint: Box<InterfaceAccount<'info, Mint>>,     /// `nonce` у seeds — щоб у продавця могло стояти кілька оферт на один    /// випуск. Повторний `nonce` упирається в `init`.    #[account(        init,        payer = seller,        space = 8 + Offer::INIT_SPACE,        seeds = [OFFER_SEED, issue.key().as_ref(), seller.key().as_ref(), &nonce.to_le_bytes()],        bump,    )]    pub offer: Box<Account<'info, Offer>>,
  1. Line 158 #[account(has_one = bond_mint)]: Checks that a stored pubkey field matches the passed account.
  2. Line 162 pub seller: Signer<'info>,: Requires this account to have signed the transaction.
  3. Line 174 init,: Creates and pays for a new account. Space and payer must be specified.
  4. Line 177 seeds = [OFFER_SEED, issue.key().as_ref(), seller.key().as_ref(), &nonce.to_le_bytes()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  5. Line 178 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
daddyclub/Daddy-s-Club · programs/daddys-club/src/instructions/source.rs L25–47 @ f3e8495 · MIT · original
    },    anchor_lang::prelude::*,    anchor_spl::{        token_2022::{transfer_checked, Token2022, TransferChecked},        token_interface::{Mint, TokenAccount},    },}; #[derive(Accounts)]#[instruction(seq: u64)]pub struct RegisterSource<'info> {    #[account(seeds = [CONFIG_SEED], bump = config.bump)]    pub config: Account<'info, ProtocolConfig>,     /// Seeds містять підписанта, тому джерело в чужій namespace не створити.    #[account(        init,        payer = issuer,        space = 8 + RevenueSource::INIT_SPACE,        seeds = [SOURCE_SEED, issuer.key().as_ref(), &seq.to_le_bytes()],        bump,    )]    pub source: Account<'info, RevenueSource>,
  1. Line 28 token_2022::{transfer_checked, Token2022, TransferChecked},: A checked token transfer, which also verifies the mint and decimals.
  2. Line 28 token_2022::{transfer_checked, Token2022, TransferChecked},: Targets the Token-2022 program instead of the original Token Program.
  3. Line 36 #[account(seeds = [CONFIG_SEED], bump = config.bump)]: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 36 #[account(seeds = [CONFIG_SEED], bump = config.bump)]: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  5. Line 41 init,: Creates and pays for a new account. Space and payer must be specified.
daddyclub/Daddy-s-Club · programs/daddys-club/src/instructions/issue.rs L301–324 @ f3e8495 · MIT · original
    /// меты), наповнює тіло інструкції, читає Token-2022.    #[account(        init,        payer = issuer,        space = ExtraAccountMetaList::size_of(EXTRA_ACCOUNT_METAS)?,        seeds = [EXTRA_METAS_SEED, bond_mint.key().as_ref()],        bump,    )]    pub extra_account_meta_list: UncheckedAccount<'info>,     pub token_program: Program<'info, Token2022>,     pub system_program: Program<'info, System>,     /// The issue the source backs right now — only when it is being replaced.    /// A free source passes the core's program id instead.    ///    /// Pinned to the source from both ends, as in `intercept`: `has_one` leads    /// from the issue to the source, `active_issue` back. A finished issue    /// cannot stand in for the live one — the ring will not close. It comes    /// last so that the accounts before it do not shift.    #[account(        mut,        has_one = source,
  1. Line 303 init,: Creates and pays for a new account. Space and payer must be specified.
  2. Line 306 seeds = [EXTRA_METAS_SEED, bond_mint.key().as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 307 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 311 pub token_program: Program<'info, Token2022>,: Targets the Token-2022 program instead of the original Token Program.
  5. Line 318 /// Pinned to the source from both ends, as in `intercept`: `has_one` leads: Checks that a stored pubkey field matches the passed account.

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 devpnpm -r --parallel run dev
.npm run buildpnpm -r run build
.npm run testpnpm -r run test
.npm run lintbiome lint .
apps/webnpm run devvite
apps/webnpm run buildtsc --noEmit && vite build
apps/webnpm run testvitest run --passWithNoTests
scriptsnpm run testvitest run --passWithNoTests
packages/sdknpm run testvitest run --passWithNoTests
.anchor buildcompile programs and generate IDLs
.anchor testlocal validator + test suite
.anchor run testpnpm vitest run
.cargo buildRust workspace build
.cargo testRust tests

What you could build from it

Used in lessons