Ledger model
The financial ledger is double-entry, append-only and the source of truth for what customers own; the chain is
reconciled against it. Implementation: backend/internal/ledger, migration 00003_ledger.sql (ported verbatim from
the Ethereum sibling) with the cold Treasury kinds of 00006.
1. Rules
- Every posting is one ledger transaction with two or more entries; per instrument, debits = credits (checked in code and by a deferred database constraint).
- Entries are never updated or deleted (database triggers). Corrections are reversals of whole transactions, at
most once (
Reverse). - A transaction is unique per
(source_type, source_id, purpose): a retried business step cannot post twice. - Accounts marked non-negative can never go below zero (checked on the balance projection in the same database
transaction); an attempt fails with a typed error (
insufficient_funds,insufficient_tokens,insufficient_treasury_inventory). - Which module may touch which account is fixed in code (
allowedKinds): only supply operations and clawbacks moveTOKEN_SUPPLY; only cash operations touch the bank and adjustment accounts; customer trading never reaches COLD.
2. Chart of accounts
| Kind | Instrument | Normal | Non-negative | Meaning |
|---|---|---|---|---|
CUST_CASH_AVAILABLE | IRR | Credit | yes | Customer cash the customer can spend |
CUST_CASH_HELD | IRR | Credit | yes | Customer cash reserved by an open BUY or withdrawal |
CUSTOMER_FUNDS_BANK | IRR | Debit | yes | Customer money held at the bank |
TREASURY_TRADING | IRR | Credit | no | Platform's trading result in cash |
FEE_REVENUE | IRR | Credit | no | Fees earned |
CASH_ADJUSTMENTS | IRR | Debit | no | Approved manual adjustments |
CUST_TOKEN_AVAILABLE | asset | Debit | yes | Customer tokens free to sell or transfer |
CUST_TOKEN_HELD | asset | Debit | yes | Customer tokens held by an open SELL, TRANSFER or clawback |
TREASURY_AVAILABLE | asset | Debit | yes | Hot Treasury float free for sales |
TREASURY_RESERVED | asset | Debit | yes | Hot float reserved by accepted BUYs and sweeps |
TREASURY_COLD_AVAILABLE | asset | Debit | yes | Cold Treasury inventory |
TREASURY_COLD_RESERVED | asset | Debit | yes | Cold inventory reserved by approved burns and refills |
TOKEN_SUPPLY | asset | Credit | yes | Tokens in existence (issued − burned − clawed back) |
3. Postings
Every money-moving flow has a hold at acceptance and a settlement when the Stellar transaction is applied in a closed ledger (final).
Cash accounts are credit-normal, so a debit decreases them; token and Treasury accounts are debit-normal.
| Flow | Hold (at acceptance) | Settle (transaction applied) |
|---|---|---|
| BUY | Dr CUST_CASH_AVAILABLE / Cr CUST_CASH_HELD (total); Dr TREASURY_RESERVED / Cr TREASURY_AVAILABLE (qty) | Dr CUST_TOKEN_AVAILABLE / Cr TREASURY_RESERVED (qty); Dr CUST_CASH_HELD (total) / Cr TREASURY_TRADING (gross) + Cr FEE_REVENUE (fee) |
| SELL | Dr CUST_TOKEN_HELD / Cr CUST_TOKEN_AVAILABLE (qty) | Dr TREASURY_AVAILABLE / Cr CUST_TOKEN_HELD (qty); Dr TREASURY_TRADING (gross) / Cr CUST_CASH_AVAILABLE (net) + Cr FEE_REVENUE (fee) |
| TRANSFER | Dr CUST_TOKEN_HELD / Cr CUST_TOKEN_AVAILABLE (sender) | Dr CUST_TOKEN_AVAILABLE (recipient) / Cr CUST_TOKEN_HELD (sender) |
| ISSUE | — | Dr TREASURY_COLD_AVAILABLE / Cr TOKEN_SUPPLY |
| BURN | Dr TREASURY_COLD_RESERVED / Cr TREASURY_COLD_AVAILABLE | Dr TOKEN_SUPPLY / Cr TREASURY_COLD_RESERVED |
| REFILL (COLD → HOT) | as BURN | Dr TREASURY_AVAILABLE / Cr TREASURY_COLD_RESERVED |
| SWEEP (HOT → COLD) | Dr TREASURY_RESERVED / Cr TREASURY_AVAILABLE | Dr TREASURY_COLD_AVAILABLE / Cr TREASURY_RESERVED |
| CLAWBACK | Dr CUST_TOKEN_HELD / Cr CUST_TOKEN_AVAILABLE | Dr TOKEN_SUPPLY / Cr CUST_TOKEN_HELD |
| DEPOSIT (second approval) | — | Dr CUSTOMER_FUNDS_BANK / Cr CUST_CASH_AVAILABLE |
| WITHDRAWAL | Dr CUST_CASH_AVAILABLE / Cr CUST_CASH_HELD | Paid: Dr CUST_CASH_HELD / Cr CUSTOMER_FUNDS_BANK |
| ADJUSTMENT | — | Dr CASH_ADJUSTMENTS or FEE_REVENUE / Cr CUST_CASH_AVAILABLE (credit), or the reverse (debit) |
A failed transaction reverses its hold exactly once.
The worked examples with exact amounts are TestLedgerModelWorkedExample (ledger) and
TestLedgerModelWorkedExamplePrices (money); the demo's BUY/SELL totals and fee revenue are checked to the rial.
4. Relation to the chain
| Ledger | On-chain |
|---|---|
| TREASURY_COLD_AVAILABLE + TREASURY_COLD_RESERVED | COLD trustline balance |
| TREASURY_AVAILABLE + TREASURY_RESERVED | HOT trustline balance |
| CUST_TOKEN_AVAILABLE + CUST_TOKEN_HELD (per customer) | That customer's trustline balance |
| TOKEN_SUPPLY | Sum of every holding of the asset (all trustlines, claimable balances, pools, contract balances) |
Reconciliation checks each line (RECONCILIATION_RUNBOOK.md), skipping a balance while an intent that touches it is
between submission and a closed ledger.