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 directory | Source files |
|---|---|
apps | 52 |
programs | 27 |
packages | 14 |
scripts | 10 |
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.
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,- Line 49
token_2022::{: Targets the Token-2022 program instead of the original Token Program. - Line 50
burn_checked, mint_to, transfer_checked, BurnChecked, MintTo, Token2022,: A checked token transfer, which also verifies the mint and decimals. - Line 50
burn_checked, mint_to, transfer_checked, BurnChecked, MintTo, Token2022,: Mints new tokens. The mint authority must sign. - Line 68
init,: Creates and pays for a new account. Space and payer must be specified. - 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. - Line 72
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
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>>,- Line 158
#[account(has_one = bond_mint)]: Checks that a stored pubkey field matches the passed account. - Line 162
pub seller: Signer<'info>,: Requires this account to have signed the transaction. - Line 174
init,: Creates and pays for a new account. Space and payer must be specified. - 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. - Line 178
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
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>,- Line 28
token_2022::{transfer_checked, Token2022, TransferChecked},: A checked token transfer, which also verifies the mint and decimals. - Line 28
token_2022::{transfer_checked, Token2022, TransferChecked},: Targets the Token-2022 program instead of the original Token Program. - 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. - 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. - Line 41
init,: Creates and pays for a new account. Space and payer must be specified.
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,- Line 303
init,: Creates and pays for a new account. Space and payer must be specified. - 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. - Line 307
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 311
pub token_program: Program<'info, Token2022>,: Targets the Token-2022 program instead of the original Token Program. - 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.
| Directory | Command | Runs |
|---|---|---|
. | npm run dev | pnpm -r --parallel run dev |
. | npm run build | pnpm -r run build |
. | npm run test | pnpm -r run test |
. | npm run lint | biome lint . |
apps/web | npm run dev | vite |
apps/web | npm run build | tsc --noEmit && vite build |
apps/web | npm run test | vitest run --passWithNoTests |
scripts | npm run test | vitest run --passWithNoTests |
packages/sdk | npm run test | vitest run --passWithNoTests |
. | anchor build | compile programs and generate IDLs |
. | anchor test | local validator + test suite |
. | anchor run test | pnpm vitest run |
. | cargo build | Rust workspace build |
. | cargo test | Rust tests |
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)