Textbook rule beside the devnet ledger
Nine stages of a bond, each one twice
Left: the bond as corporate finance teaches it, with the rule's source. Right: the same stage run by a Solana program, measured on devnet. Underneath: the outcome both give.
Click chart marks, underlined figures or references to inspect their evidence. Hover for one second, or focus a link, for an auditor card. Refused transactions are scripted negative tests, with their scope explained in each card.
Issue: a fixed supply, on a known register
The issuer sells bonds on fixed terms. The central securities depository keeps the register of who holds them.
SCoRE glossary: the CSD ensures the integrity of securities issues · SCoRE · CSD glossary
The terms live in the bond's account on chain. Only the program can mint, and only before the first record moment. The token has no freeze authority.
- Refused Attacker mints 1,000 bonds to itself · transaction
- Refused Registrar places 100 new bonds with BH#2 · transaction
- Refused Attacker registers itself as a holder, signing as registrar · transaction
Same outcomeSame register: 10,251 bonds placed with 1,000 holders; nobody else can add one.
Trade: the securities leg settles in one transaction
A trade settles a settlement cycle later. If it is still pending on the record date, the coupon goes to the seller and is then reclaimed: a market claim.
AFME Market Claims, standard 1b; T2S market claims standards · AFME · market claims · T2S · market claims
Bond-token transfer and the register update are one transaction. No securities transfer is left pending at the record moment. This run does not demonstrate a cash trade leg.
- Refused BH#1 sends a bond to an unregistered wallet (the attacker) · transaction
- Refused BH#1 sends a bond to BH#2 with the old transfer instruction, which skips the transfer hook · transaction
- Refused Attacker moves BH#1's bonds to itself · transaction
Same outcomeSame register after every trade; no market claims to process.
Record: who held the bond at 09:00
| Holder | Bonds at 09:00 |
|---|---|
| BH#458 | 12 |
| BH#43 | 5 |
At 09:00 on payment day the CSD takes the list of holders. Trades keep being registered, but the coupon follows the list.
Kazakhstan CSD rules for government bonds, art. 8 p. 6 · Kazakhstan CSD · article 8
| Time | Entry | BH#458 | BH#43 |
|---|---|---|---|
| 09:00:00 | record moment 0 | 12 | 5 |
| +6 s | hook: positions saved for 09:00 | 12 | 5 |
| +6 s | transfer 3 → BH#43 | 9 | 8 |
No list is taken. BH#458's sale 6 s after 09:00 first saved the 12 bonds it held at 09:00; the coupon used that.
- Refused Attacker calls the transfer hook directly to add 5 bonds to BH#2's record · transaction
Same outcomeSame entitlement: BH#458 paid on 12, BH#43 on 5, as the 09:00 list says.
SimulatedDevnet time is compressed: 1 bond day = 20 chain seconds, so “6 s after 09:00” is 6 chain seconds.
Calculate: the coupon statement
Coupon = holding x face x rate / payments a year, on the holder's total holding, rounded down to the currency's smallest unit.
SCoRE, standard 4; government bond terms: S = N x C · SCoRE · Standard 4
The program computes it in integers of the cash token's smallest unit, rounded down once per holder. How we checked: we read every transaction of this bond from the public devnet RPC (the data Solana Explorer shows), add up the token balance changes each one recorded, and get each holder's bonds at the record moment and the cash it received. Bonds x 50.00 = cash received. QuantLib and FinancePy, run on the bond's terms, give the same amounts.
- Refused BH#3 bought after the record moment and asks for the coupon · transaction
Same outcomeSame amount: BH#1, 10 bonds, 500.00.
Fund: no payment until all the money is there
The issuer pays the whole amount to the CSD by 16:00; the CSD pays holders by 17:00.
Kazakhstan CSD rules, art. 8 p. 6-7 · Kazakhstan CSD · article 8
Each coupon has its own vault and a budget fixed by the supply at 09:00. Anyone may confirm the funding; it is refused until the vault covers the budget.
- Refused Attacker confirms the coupon funded · transaction
- Refused Attacker pays BH#1 from the unconfirmed vault · transaction
- Refused Attacker moves the vault's cash to itself · transaction
Same outcomeSame rule: coupon 0's budget, 10,251 bonds x 50.00 = 512,550.00, fully in the vault before anyone is paid.
Pay: every holder paid, exactly once
On payment day the CSD pays every holder on the list its coupon, once, by 17:00.
SCoRE event INTR; Kazakhstan CSD rules, art. 8 p. 6 · SCoRE · INTR · Kazakhstan CSD · article 8
Any wallet may pay any holder, in any order. Payment and the holder's paid flag for that coupon are one transaction, so it cannot happen twice.
- Refused Attacker pays BH#1's coupon again · transaction
- Refused Attacker pays BH#1's coupon into its own cash account · transaction
- Refused Attacker pays BH#1's coupon 1 from coupon 0's vault · transaction
Same outcomeSame result: 1,000 holders paid coupon 0 once; when our operator stopped, a wallet with no role paid the rest (12 of 12 exact).
Redeem: burned the moment the principal is paid
At maturity trading stops, the CSD pays the face value and writes the bonds off. Cash and securities should move at the same time.
Kazakhstan CSD rules, art. 10 p. 6; SCoRE, standard 8 · SCoRE · Standard 8 · Kazakhstan CSD · article 10
Trading closes at the final record moment. One transaction burns the holder's bonds and pays the principal: neither can happen without the other.
- Refused Attacker redeems BH#1 again · transaction
- Refused Attacker redeems BH#1's bonds into its own cash account · transaction
- Refused BH#2 sells 1 bond to BH#1 · transaction
Same outcomeSame end: every bond retired against cash; supply 10,251 to 0.
Offer: tendered bonds stay put and keep their coupon
In an issuer bid or tender offer, holders' instructions travel up the custody chain to the issuer before a deadline. Bonds bought back are redeemed.
ISO 20022 events BIDS and TEND; Kazakhstan government bond rules, p. 16 · SCoRE · BIDS / TEND · Kazakhstan government bond rules
Holders tender directly. Tendered bonds stay in their own accounts, locked, so a coupon whose record moment falls in the window is still theirs.
- Refused BH#1 transfers all its bonds to BH#2, including those tendered to the issuer · transaction
- Refused BH#1 sells 1 bond to the buyback after its maximum is reached · transaction
- Refused Attacker sells BH#1's bonds to the buyback · transaction
Same outcomeSame end for bonds bought back: retired against cash. 309 of 441 tendered accepted (maximum 410), 176 bought first come (maximum 410).
Audit: anyone can check every unit
Holdings sit in tiers: the CSD knows its participants, and each level informs the next until the information reaches the end investor.
AFME, standard 9; T2S note on the cascade · AFME · custody cascade · T2S · explanatory note
An independent scenario audit reads the public ledger and recomputes payments from the terms. A separate localnet test added 0.000001 to a vault; two reconciliation checks detected the unexpected deposit. That test is not a devnet exploit.
Same outcomeSame books, open to anyone: 1,000 of 1,000 holders' cash equals the terms to the unit.