nx_balance.nx
buildroot/runtime/nx_balance.nx
about
nx_balance.nx -- per-account balance cache for the ledger.
module: nishi-core.finance.balance
depends: nishi-core.finance.ledger, nishi-core.io.iso8601
disk_kb: 5
capability: MARKETPLACE
license_tier: PUBLIC_NISHI_SUBSTRATE
genealogy_id: standard_accounting_running_balance_pattern +
cardinal_13_additive_only +
nishi_pillar_4_bits_up_sovereign_2026
Per-account running-balance cache. The ledger (`nx_ledger.nx`)
is the authoritative event log; this primitive maintains O(1)
balance lookups by listening to each posted transaction and
updating per-account totals.
Per Cardinal 13 (additive-only): cache entries are append-only
snapshots. Each transaction post creates a new BalanceSnapshot
row stamped with the post_ts. Querying current balance returns
the latest is_current=1 snapshot; querying historical balance
at time T returns the latest snapshot with post_ts <= T.
===== Why a cache when we have the ledger? =======================
Walking the ledger for every balance query is O(N) in
transaction count. At 100k+ transactions/year, that's prohibitive
for UI dashboards + dunning + fraud-detection workflows. The
cache is a denormalized projection that maintains the invariant:
sum(line.amount where account_id = A and is_debit = 1)
- sum(line.amount where account_id = A and is_debit = 0)
== BalanceSnapshot.balance_minor_q10 for A's latest snapshot
Substrate periodically asserts this invariant via
nx_balance_reconcile_against_ledger (queued).
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_ledger.nx
imported by: nobody (leaf or entry point)
structs
| 73 | struct BalanceSnapshot |
consts
| 49 | const NX_BALANCE_POSITIVE: i64 = 1 |
| 50 | const NX_BALANCE_ZERO: i64 = 2 |
| 51 | const NX_BALANCE_NEGATIVE: i64 = 3 // overdraft; substrate-level alert |
| 52 | const NX_BALANCE_UNCONFIRMED: i64 = 4 // pending transaction not posted |
| 53 | const NX_BALANCE_RECONCILING: i64 = 5 // mid-ledger-walk; do not trust |
| 54 | const NX_BALANCE_ACCOUNT_NOT_FOUND: i64 = 6 |
| 93 | const NX_BALANCE_SNAPSHOT_BYTES: i64 = 96 // 12 fields * 8 bytes |
| 220 | const NX_BALANCE_MAX_SNAPSHOT_WALK: i64 = 1048576 |
functions
| 56 | func nx_balance_verdict_name(v: i64) -> *u8 |
| 97 | func nx_balance_resolve_verdict(balance_minor_q10: i64) -> i64 called by 1: nx_balance_apply_line |
| 111 | func nx_balance_apply_line( |
| 179 | func nx_balance_current_minor_q10(latest_snapshot: *BalanceSnapshot) -> i64 |
| 185 | func nx_balance_current_verdict(latest_snapshot: *BalanceSnapshot) -> i64 |
| 195 | func nx_balance_can_debit( |
| 222 | func nx_balance_at_time( |
| 249 | func nx_balance_reconcile( calls 1: nx_ledger_account_balance_minor_q10 |