Measured on devnet · 1,000 holders, 2,193 transactions

Who held the bond at 09:00, and was each holder paid exactly once?

A coupon belongs to whoever held the bond at the record moment. One Solana program finds those holders, pays them from the terms and records it. Pick a party: what it expects, what was kept, what was refused. Or see how it works.

Paid on the 09:00 list4,000 / 4,0001,039 wrong if paid by balance
Audit from public data3,959 / 3,959checks · result
Attacks33 / 33refused, each a failed transaction
Coupons paid2,008,350.004 coupons · final supply 0

Holder

investor

ExpectsPaid exactly, on time, for what it held

Keptmeasured on devnet
  • Paid for the bonds held at the record moment
    4,000 of 4,000
    coupon payments exact, 1,000 holders; paying by holdings at payment time would have got 1,039 wrong
  • Sells right after the record moment, keeps the coupon
    BH#458: 6 s
    sold 3 of 12 bonds 6 s after the record moment; paid on 12: 600.00
  • The listing's example
    BH#1: 500.00
    10 bonds x 1,000 x 10% / 2
Refused10 of 10 attempts · failed transactions
  1. Refused Attacker moves BH#1's bonds to itself only the owner moves its bonds · transaction
    Would cost: BH#1 loses its bonds.Logged: owner does not match
  2. Refused Attacker moves BH#1's bonds naming the program, the permanent delegate only the program acts as permanent delegate · transaction
    Would cost: BH#1 loses its bonds.Logged: MissingRequiredSignature
  3. Refused Attacker burns BH#1's bonds standard burns are disabled for this bond · transaction
    Would cost: BH#1 loses coupons and principal.Logged: Invalid instruction
  4. Refused Attacker burns BH#1's bonds with the permissioned burn, naming itself burn authority only the program is burn authority · transaction
    Would cost: BH#1 loses coupons and principal.Logged: InvalidAccountData
  5. Refused Attacker burns BH#1's bonds with the permissioned burn, naming the program, the burn authority only the program signs as burn authority · transaction
    Would cost: BH#1 loses coupons and principal.Logged: MissingRequiredSignature
  6. Refused Attacker freezes BH#1's bond account the bond has no freeze authority · transaction
    Would cost: BH#1 cannot sell.Logged: This token mint cannot freeze accounts
  7. Refused Attacker pays BH#1's coupon into its own cash account cash goes only to the holder's own account · transaction
    Would cost: BH#1's coupon.Logged: ConstraintTokenOwner: A token owner constraint was violated.
  8. Refused Attacker presents BH#1's holder record as its own a holder record belongs to one wallet · transaction
    Would cost: BH#1's coupon.Logged: ConstraintSeeds: A seeds constraint was violated.
  9. Refused Attacker redeems BH#1's bonds into its own cash account principal goes only to the holder's own account · transaction
    Would cost: BH#1's principal.Logged: ConstraintTokenOwner: A token owner constraint was violated.
  10. Refused Attacker sells BH#1's bonds to the buyback only the holder sells its bonds · transaction
    Would cost: BH#1's bonds sold without its consent.Logged: ConstraintRaw: A raw constraint was violated.

SimulatedHolders are generated wallets, labelled BH#1, BH#2, ...

HowA transfer first saves both holders' positions at every record moment passed. The coupon is computed from that saved position and paid only to the holder's own cash account.

Issuer

borrower

ExpectsPays what the terms say, not a unit more

Keptmeasured on devnet
  • Coupons paid = the terms
    2,008,350.00
    4 coupons to 1,000 holders; every vault: inflow = payments + leftover
  • Bonds retired only against cash
    948 of 948
    holders' buyback, tender and redemption: bonds burned and cash paid in one transaction
  • Its only task: deposit the cash
    7 of 2,193
    transactions in the 1000-holder run were the issuer's
Refused12 of 12 attempts · failed transactions
  1. Refused Attacker mints 1,000 bonds to itself only the program mints bonds · transaction
    Would cost: the issuer owes coupons and principal on bonds it never sold.Logged: owner does not match
  2. Refused Attacker mints 1,000 bonds naming the program as mint authority only the program signs as the program · transaction
    Would cost: the issuer owes coupons and principal on bonds it never sold.Logged: MissingRequiredSignature
  3. Refused Registrar places 100 new bonds with BH#2 the issue closes at the first record moment · transaction
    Would cost: bonds issued after the issue closed.Logged: PlacementClosed: Placement is allowed only before the first record moment.
  4. Refused Attacker pays BH#1's coupon again one payment per holder per coupon · transaction
    Would cost: the issuer pays a coupon twice; the last holders find the vault empty.Logged: AlreadyPaid: Already paid.
  5. Refused BH#3 bought after the record moment and asks for the coupon the coupon follows the record moment · transaction
    Would cost: the seller's coupon paid again to the buyer.Logged: NothingToPay: Nothing to pay.
  6. Refused Attacker redeems BH#1 again redeemed bonds are burned · transaction
    Would cost: principal paid twice.Logged: NothingToPay: Nothing to pay.
  7. Refused Attacker pays BH#1's coupon 1 from coupon 0's vault each payment has its own vault · transaction
    Would cost: one coupon's cash spent on another.Logged: ConstraintHasOne: A has one constraint was violated.
  8. Refused Attacker pays BH#1 a coupon from the redemption vault principal cash pays only principal · transaction
    Would cost: principal cash paid out as coupons.Logged: WrongEventKind: Instruction does not apply to this event type.
  9. Refused BH#1 sells 1 bond to the buyback after its maximum is reached an offer buys up to its maximum · transaction
    Would cost: the issuer buys more bonds than it offered.Logged: ExceedsOffer: Quantity exceeds what the offer still accepts.
  10. Refused BH#1 transfers all its bonds to BH#2, including those tendered to the issuer tendered bonds are locked · transaction
    Would cost: the same bonds sold twice: to the issuer and to a buyer.Logged: BondsLocked: Bonds are locked in a tender.
  11. Refused Attacker moves the vault's cash to itself a vault pays only through the program · transaction
    Would cost: the coupon's cash is gone.Logged: owner does not match
  12. Refused Attacker moves the vault's cash to itself naming the program, the vault's owner only the program signs for a vault · transaction
    Would cost: the coupon's cash is gone.Logged: MissingRequiredSignature

HowEach coupon and the redemption has its own vault, funded with exactly its budget. A payment and the holder's paid flag are one transaction.

Depository

registrar and paying agent

ExpectsA correct register, and no new problems

Keptmeasured on devnet
  • The register at every record moment, trading open
    1,000 holders
    600 trades 2 s to 22 min after record moments; cost per holder paid: 833 to 2,500 lamports
  • Works when the operator stops
    12 of 12
    our operator stopped after coupon 0; a wallet with no role paid the rest and redeemed everyone
  • Anyone can service; nobody can redirect
    0 to the attacker
    the attacker's wallet paid coupons and principal in 4 transactions: all cash went to holders
Refused8 of 8 attempts · failed transactions
  1. Refused Attacker registers itself as a holder, signing as registrar only the registrar registers holders · transaction
    Would cost: an unvetted wallet in the register.Logged: ConstraintHasOne: A has one constraint was violated.
  2. Refused BH#1 sends a bond to an unregistered wallet (the attacker) bonds go only to registered holders · transaction
    Would cost: bonds held outside the register.Logged: AccountOwnedByWrongProgram: The given account is owned by a different program than expected.
  3. Refused BH#1 moves a bond to a second account of its own one bond account per holder · transaction
    Would cost: a position the register does not see.Logged: NotAssociatedTokenAccount: Bonds may be held only in the owner's associated token account.
  4. Refused BH#1 sends a bond to BH#2 with the old transfer instruction, which skips the transfer hook every transfer must pass the program · transaction
    Would cost: a bond moves without its record-moment position being saved.Logged: custom program error: 0x1f
  5. Refused Attacker calls the transfer hook directly to add 5 bonds to BH#2's record the program's hook runs only inside a real transfer · transaction
    Would cost: coupons paid on bonds nobody holds.Logged: NotTransferring: The hook was called outside a token transfer.
  6. Refused Attacker confirms the coupon funded funding must cover the whole budget · transaction
    Would cost: the last holders find the vault empty.Logged: InsufficientFunding: Vault balance does not cover the event's budget.
  7. Refused Attacker pays BH#1 from the unconfirmed vault no payment before full funding · transaction
    Would cost: the last holders find the vault empty.Logged: NotFunded: The event is not funded.
  8. Refused Attacker makes itself the program's upgrade authority only the upgrade authority changes the program · transaction
    Would cost: a replaced program could empty every vault.Logged: Incorrect authority provided

SimulatedHolder onboarding and KYC is a registrar key.

HowThe program is the register and the paying agent. Opening, funding confirmation, payment and redemption can be sent by any wallet; none can change who is paid or how much.

Bank

cash rail

ExpectsIts money and its controls respected

Keptmeasured on devnet
  • Cash enters a vault only from the bank
    4 of 4
    vault checks: deposits only from the bank's mints, payments only to holders
  • Its freeze holds; the bond keeps paying
    BH#5 waits
    the bank froze BH#5's cash account for coupon 1: the others were paid, BH#5 after the thaw
Refused2 of 2 attempts · failed transactions
  1. Refused Registrar creates a bond paid in a cash token that keeps a 1% fee on every transfer cash tokens that change amounts are refused · transaction
    Would cost: holders receive 1% less than the terms.Logged: UnsupportedCashToken: Cash token has an extension that changes or conditions delivery (transfer fee, transfer hook, non-transferable) or one this program cannot read.
  2. Refused Anyone pays BH#5, whose cash account the bank froze the bank's freeze holds; the others were paid first · transaction
    Would cost: nothing: the others are already paid, and the right stays open.Logged: Account is frozen

SimulatedThe cash is a test token (6 decimals) minted by a test bank key, standing for fiat or a stablecoin.

HowThe program moves cash only with the cash token's own transfer: it cannot pass a frozen account. At bond creation it refuses cash tokens that change amounts or let a third party move a vault's cash.

Exchange

trading venue

ExpectsTrading open every day until maturity

Keptmeasured on devnet
  • Open through every record moment
    600 trades
    2 s to 22 min after record moments, 1,000 holders
  • Nobody can freeze a holder
    No freeze authority
    the bond token was created without one
Refused1 of 1 attempts · failed transactions
  1. Refused BH#2 sells 1 bond to BH#1 trading closes at the final record moment · transaction
    Would cost: a bond changes hands while its principal is being paid.Logged: TransfersClosedAtMaturity: Transfers are closed from the final record moment (redemption).

SimulatedTrades are direct transfers between registered holders; there is no order book.

HowNo snapshot and no freeze: the hook saves positions as bonds move, so a record moment needs no pause. Trading stays open for transfers through the bond token's transfer hook (Solana Token-2022), so a venue has to support it.

Auditor

or regulator

ExpectsEvery payment checkable from public data

Keptmeasured on devnet
  • Every payment recomputed from the ledger
    3,959 of 3,959
    checks over 4,085 transactions; 1,000 of 1,000 holders' cash equals the terms to the unit
  • Tampering is caught
    2 checks fail
    after 0.000001 is sent into a vault from outside (local network)
  • The attacker run, audited the same way
    35 of 35
    checks pass; 33 of 33 attacks refused, each a failed transaction anyone can open

SimulatedThe tampering probe ran on a local network, not devnet.

HowThe audit is independent in its input: it reads the public ledger only, not program accounts or our operator's report. It checks every amount against the bond's terms and the run's scenario.

How it works

Every bond needs the same processes. The program runs them under real constraints; where it has to differ, the outcome must not.
Abstract

The issuer sells bonds on fixed terms. Each buyer holds what it bought, and the supply is fixed.

Real

create_bond writes the terms and creates the bond token with the program as its only mint and burn authority and no freeze authority. register_holder admits a wallet; place mints bonds to it, only before the first record moment.

Real-world constraint

Anyone can open a token account. A bond needs known holders and a supply nobody can inflate.

Why the outcome is the same

Only the program changes the supply, and it records every change, so the supply at any record moment is known without counting holders.

Sent by
registrar
Reads
terms
Writes
bond, holder records
Moves
bonds minted to holders
Same outcomemeasured on devnet
  • 10,251 bonds
    placed with 1,000 registered holders
Under stress 5 of 5 attacks refused
  1. Refused Attacker registers itself as a holder, signing as registrar only the registrar registers holders · transaction
    Would cost: an unvetted wallet in the register.Logged: ConstraintHasOne: A has one constraint was violated.
  2. Refused Attacker mints 1,000 bonds to itself only the program mints bonds · transaction
    Would cost: the issuer owes coupons and principal on bonds it never sold.Logged: owner does not match
  3. Refused Attacker mints 1,000 bonds naming the program as mint authority only the program signs as the program · transaction
    Would cost: the issuer owes coupons and principal on bonds it never sold.Logged: MissingRequiredSignature
  4. Refused Registrar places 100 new bonds with BH#2 the issue closes at the first record moment · transaction
    Would cost: bonds issued after the issue closed.Logged: PlacementClosed: Placement is allowed only before the first record moment.
  5. Refused Registrar creates a bond paid in a cash token that keeps a 1% fee on every transfer cash tokens that change amounts are refused · transaction
    Would cost: holders receive 1% less than the terms.Logged: UnsupportedCashToken: Cash token has an extension that changes or conditions delivery (transfer fee, transfer hook, non-transferable) or one this program cannot read.
Abstract

A trade changes ownership at once. The register always says who holds what.

Real

Every transfer runs the program's transfer hook in the same transaction. Both sides must be registered; the hook saves their holdings at the record moments passed, then updates both holder records.

Real-world constraint

Transfers run in the token program, not in ours, and one holder could open several accounts.

Why the outcome is the same

If the hook fails, the transfer fails. One account per holder, and a transfer is refused if a holder record ever disagrees with its balance.

Sent by
holder; the token program calls the hook
Reads
bond time
Writes
both holder records
Moves
bonds, seller to buyer
Same outcomemeasured on devnet
  • 600 trades
    between 1,000 holders, every one through the hook
Under stress 8 of 8 attacks refused
  1. Refused Attacker moves BH#1's bonds to itself only the owner moves its bonds · transaction
    Would cost: BH#1 loses its bonds.Logged: owner does not match
  2. Refused Attacker moves BH#1's bonds naming the program, the permanent delegate only the program acts as permanent delegate · transaction
    Would cost: BH#1 loses its bonds.Logged: MissingRequiredSignature
  3. Refused Attacker freezes BH#1's bond account the bond has no freeze authority · transaction
    Would cost: BH#1 cannot sell.Logged: This token mint cannot freeze accounts
  4. Refused BH#1 sends a bond to an unregistered wallet (the attacker) bonds go only to registered holders · transaction
    Would cost: bonds held outside the register.Logged: AccountOwnedByWrongProgram: The given account is owned by a different program than expected.
  5. Refused BH#1 moves a bond to a second account of its own one bond account per holder · transaction
    Would cost: a position the register does not see.Logged: NotAssociatedTokenAccount: Bonds may be held only in the owner's associated token account.
  6. Refused BH#1 sends a bond to BH#2 with the old transfer instruction, which skips the transfer hook every transfer must pass the program · transaction
    Would cost: a bond moves without its record-moment position being saved.Logged: custom program error: 0x1f
  7. Refused BH#1 transfers all its bonds to BH#2, including those tendered to the issuer tendered bonds are locked · transaction
    Would cost: the same bonds sold twice: to the issuer and to a buyer.Logged: BondsLocked: Bonds are locked in a tender.
  8. Refused BH#2 sells 1 bond to BH#1 trading closes at the final record moment · transaction
    Would cost: a bond changes hands while its principal is being paid.Logged: TransfersClosedAtMaturity: Transfers are closed from the final record moment (redemption).
Abstract

At the record moment the register is fixed: each holder is owed for what it holds at that instant.

Real

Nothing runs at the record moment. The first change to a holding after it saves the holding it replaces; a holding that never changed is still the one held then.

Real-world constraint

A program sees only the accounts in a transaction and runs only when one is sent: it cannot read every holder at 09:00, and nothing is sent at 09:00 by itself.

Why the outcome is the same

Between two changes a holding is constant, so what the next change saves is exactly the holding at the record moment, however late anyone looks. No freeze, no deadline.

Sent by
nobody: it happens inside the next transfer
Reads
bond time, record moments
Writes
holding history, supply history
Moves
nothing
Same outcomemeasured on devnet
  • 4,000 of 4,000
    payments on the saved holdings equal the independent audit's register at the record moments
Under stress 1 of 1 attacks refused
  • 600 trades
    2 s to 22 min after record moments; paying by holdings at payment time: 1,039 wrong
  1. Refused Attacker calls the transfer hook directly to add 5 bonds to BH#2's record the program's hook runs only inside a real transfer · transaction
    Would cost: coupons paid on bonds nobody holds.Logged: NotTransferring: The hook was called outside a token transfer.
Abstract

Owed = holding at the record moment x face x rate / periods, as the terms say.

Real

Computed inside each payment from the saved holding and the terms, in integers, rounded down once per holder. The event's budget is the same formula on the supply at the record moment.

Real-world constraint

Token amounts are whole smallest units; there are no fractions on chain.

Why the outcome is the same

Rounding once on each holder's total keeps the sum within the budget; any residue stays visible in the event's vault.

Sent by
part of each payment
Reads
terms, saved holding
Writes
nothing
Moves
nothing
Same outcomemeasured on devnet
  • BH#1: 500.00
    10 bonds x 1,000 x 10% / 2, the listing's example
  • passed
    every amount checked against QuantLib and FinancePy
Under stress 1 of 1 attacks refused
  1. Refused BH#3 bought after the record moment and asks for the coupon the coupon follows the record moment · transaction
    Would cost: the seller's coupon paid again to the buyer.Logged: NothingToPay: Nothing to pay.
Abstract

The issuer provides the full amount before payment.

Real

After the record moment anyone opens the event: it gets its own vault and a budget fixed by the supply at the record moment. The issuer deposits; anyone confirms, accepted only if the vault covers the budget.

Real-world constraint

Cash is a separate token that the issuer moves outside the program, at its own pace.

Why the outcome is the same

No holder is paid from an event that cannot pay all, and one event's cash cannot pay another. The issuer's only action is the deposit.

Sent by
anyone (open, confirm); issuer (deposit)
Reads
supply history, vault balance
Writes
event, its vault
Moves
cash, issuer to the event's vault
Same outcomemeasured on devnet
  • 7 of 2,193
    transactions were the issuer's deposits
Under stress 4 of 4 attacks refused
  1. Refused Attacker confirms the coupon funded funding must cover the whole budget · transaction
    Would cost: the last holders find the vault empty.Logged: InsufficientFunding: Vault balance does not cover the event's budget.
  2. Refused Attacker pays BH#1 from the unconfirmed vault no payment before full funding · transaction
    Would cost: the last holders find the vault empty.Logged: NotFunded: The event is not funded.
  3. Refused Attacker moves the vault's cash to itself a vault pays only through the program · transaction
    Would cost: the coupon's cash is gone.Logged: owner does not match
  4. Refused Attacker moves the vault's cash to itself naming the program, the vault's owner only the program signs for a vault · transaction
    Would cost: the coupon's cash is gone.Logged: MissingRequiredSignature
Abstract

On the payment date each entitled holder receives its amount, exactly once.

Real

Any wallet, any time after payment opens, in any order, pays one holder: cash from the event's vault to the holder's own cash account and the holder's paid flag, in one transaction.

Real-world constraint

Paying 1,000 holders takes many transactions; someone must send them; the operator may stop; a holder's cash account may be frozen.

Why the outcome is the same

Whoever sends it and whenever, state fixes the amount and the recipient; the paid flag makes it once; rights never expire; one holder's failure blocks nobody.

Sent by
anyone
Reads
bond, event, holder record
Writes
holder's paid flag
Moves
cash, event vault to the holder's own account
Same outcomemeasured on devnet
  • 2,008,350.00
    4 coupons to 1,000 holders, exact
Under stress 6 of 6 attacks refused
  • 12 of 12
    operator stopped after coupon 0; a wallet with no role paid the rest
  • 0 to itself
    the attacker's wallet paid coupons and principal in 4 transactions
  • BH#5 waited
    its cash account frozen by the bank: the others were paid first, BH#5 after the thaw
  1. Refused Attacker pays BH#1's coupon into its own cash account cash goes only to the holder's own account · transaction
    Would cost: BH#1's coupon.Logged: ConstraintTokenOwner: A token owner constraint was violated.
  2. Refused Attacker presents BH#1's holder record as its own a holder record belongs to one wallet · transaction
    Would cost: BH#1's coupon.Logged: ConstraintSeeds: A seeds constraint was violated.
  3. Refused Attacker pays BH#1's coupon again one payment per holder per coupon · transaction
    Would cost: the issuer pays a coupon twice; the last holders find the vault empty.Logged: AlreadyPaid: Already paid.
  4. Refused Attacker pays BH#1's coupon 1 from coupon 0's vault each payment has its own vault · transaction
    Would cost: one coupon's cash spent on another.Logged: ConstraintHasOne: A has one constraint was violated.
  5. Refused Attacker pays BH#1 a coupon from the redemption vault principal cash pays only principal · transaction
    Would cost: principal cash paid out as coupons.Logged: WrongEventKind: Instruction does not apply to this event type.
  6. Refused Anyone pays BH#5, whose cash account the bank froze the bank's freeze holds; the others were paid first · transaction
    Would cost: nothing: the others are already paid, and the right stays open.Logged: Account is frozen
Abstract

At maturity holders receive the principal and the bonds cease to exist.

Real

Transfers close at the final record moment. Any wallet redeems a holder: its bonds are burned and the principal paid in one transaction.

Real-world constraint

Redemption takes one transaction per few holders, and bonds tendered to an open offer are not free.

Why the outcome is the same

Burn and payment are one transaction: no bond is burned unpaid and no principal is paid for a live bond. Bonds committed to an offer are left to it.

Sent by
anyone
Reads
bond, event, holder record
Writes
holder record, supply
Moves
bonds burned; cash, vault to holder
Same outcomemeasured on devnet
  • 9,766 bonds
    redeemed from 934 holders; final supply 0
Under stress 5 of 5 attacks refused
  1. Refused Attacker redeems BH#1's bonds into its own cash account principal goes only to the holder's own account · transaction
    Would cost: BH#1's principal.Logged: ConstraintTokenOwner: A token owner constraint was violated.
  2. Refused Attacker redeems BH#1 again redeemed bonds are burned · transaction
    Would cost: principal paid twice.Logged: NothingToPay: Nothing to pay.
  3. Refused Attacker burns BH#1's bonds standard burns are disabled for this bond · transaction
    Would cost: BH#1 loses coupons and principal.Logged: Invalid instruction
  4. Refused Attacker burns BH#1's bonds with the permissioned burn, naming itself burn authority only the program is burn authority · transaction
    Would cost: BH#1 loses coupons and principal.Logged: InvalidAccountData
  5. Refused Attacker burns BH#1's bonds with the permissioned burn, naming the program, the burn authority only the program signs as burn authority · transaction
    Would cost: BH#1 loses coupons and principal.Logged: MissingRequiredSignature
Abstract

The issuer buys bonds back at a price: first come up to a maximum, or pro rata among tenders. Bought bonds cease to exist.

Real

The registrar opens an offer with its own funded vault. A sale burns and pays at once. A tender locks bonds in the holder's own account; after the window anyone settles each tender: accepted = tendered x maximum / total, rounded down.

Real-world constraint

The tender window spans a record moment, and bonds come in whole units.

Why the outcome is the same

Tendered bonds never move, so they keep the coupon whose record moment falls in the window. Rounding down never buys more than the maximum; it may buy fewer, and the unspent cash stays in the offer's vault.

Sent by
registrar (offer); holder (sell, tender); anyone (settle)
Reads
offer, holder record
Writes
offer, tender, holder record, supply
Moves
bonds burned; cash, offer vault to holder
Same outcomemeasured on devnet
  • 176 bonds
    bought back first come, maximum 410
  • 309 bonds
    accepted pro rata from tenders, maximum 410
Under stress 2 of 2 attacks refused
  • 132 of 132
    tendering holders paid coupon 2 on their tendered bonds
  1. Refused Attacker sells BH#1's bonds to the buyback only the holder sells its bonds · transaction
    Would cost: BH#1's bonds sold without its consent.Logged: ConstraintRaw: A raw constraint was violated.
  2. Refused BH#1 sells 1 bond to the buyback after its maximum is reached an offer buys up to its maximum · transaction
    Would cost: the issuer buys more bonds than it offered.Logged: ExceedsOffer: Quantity exceeds what the offer still accepts.
Abstract

Anyone can reconcile the register and every payment.

Real

Every change is a ledger transaction. An independent audit reads only the public ledger and recomputes every amount from the terms.

Real-world constraint

A ledger shows transactions, not obligations: the auditor must rebuild the obligations itself.

Why the outcome is the same

The audit needs no operator report and no program accounts, and it fails on one unit out of place.

Sent by
anyone, off chain
Reads
public ledger, terms
Writes
nothing
Moves
nothing
Same outcomemeasured on devnet
  • 3,959 of 3,959
    checks over 4,085 transactions
  • 35 of 35
    checks on the attacker run
Under stress measured
  • 2 checks fail
    after 0.000001 is sent into a vault from outside (local network)

The design that follows

  1. Record on write, not by snapshotHoldings at record moments are saved by the next change, so the market never stops and nothing has a deadline.
  2. Every balance change passes the programTransfer hook, the program as only mint and burn authority, no freeze authority; records checked against balances.
  3. Outcomes follow from state, so anyone may actOpening, confirming, paying, redeeming and settling need no authority: the sender cannot change who gets what.
  4. Each obligation isolatedOne vault per event and offer, one paid flag per holder per coupon; a payment writes only that holder's record and that vault.
  5. Value moves only with its counterpartCash and paid flag in one transaction; burn and payment in one transaction.
  6. Checked from outsideAn audit from the public ledger alone, failing on one unit out of place.
ProcessSent byBond: terms, supplyHolder recordEventOffer, tenderVault (cash)Bond token
create_bondregistrarcreatecreate
register_holderregistrarreadcreate
placeregistrarsupplywritemint
transfer + hookholderreadwrite bothmove
open_eventanyonereadcreatecreate
depositissuercash in
confirm_fundinganyonewriteread
pay_couponanyonereadpaid flagreadcash out
redeem_holderanyonesupplywritereadcash outburn
create_offerregistrarreadcreatecreate
sell_to_offerholdersupplywritewritecash outburn
change_tenderholderreadlockwrite
settle_tenderanyonesupplywritereadcash outburn

Outcome of the 1000-holder run

Bonds10,251 placed = 176 bought back + 309 tendered + 9,766 redeemed ; 0 left

Cash12,590,250.00 deposited = 12,257,590.00 to holders + 332,660.00 unspent offer cash, still in its vaults ; 0.00 left in coupon and redemption vaults

Assumed, not proven here

  • Time is the chain's clock; devnet runs compress a bond day to 1-20 seconds.
  • The registrar registers only vetted wallets (investor onboarding is simulated).
  • The program's upgrade authority is one wallet: an attacker cannot take it (refused), but its holder could replace the program.
  • Cash is a test token minted by a test bank; holders hold directly, with no custodians.