The Ledger Model
FWallet uses a double-entry, append-only ledger to record every money movement. This is the same accounting model used by banks and financial institutions — adapted for a wallet API.
Why Double-Entry?
Single-entry systems (just tracking balances) are fragile. If a balance is wrong, you have no way to trace why. Double-entry bookkeeping solves this by recording every transaction as a set of balanced debit and credit lines.
The core invariant:
For every journal entry, the sum of debits equals the sum of credits, per currency.
If this invariant ever breaks, something is wrong at the system level. FWallet enforces it in application code and can verify it with a reconciliation sweep.
Journal Entries and Lines
A journal entry is a single financial event (a deposit, transfer, payout, fee charge, or reversal). It contains one or more journal lines, each touching one ledger account.
Example: Internal Transfer with Fee
When user Alice transfers 100,000 UGX to user Bob, with a 2,000 UGX fee:
Journal Entry: je_01JQHXYZ...Date: 2026-03-18T10:00:05ZDescription: "Transfer from Alice to Bob"
DR wallet:alice 100,000 UGX (sender debited) DR wallet:alice 2,000 UGX (fee debited from sender) CR wallet:bob 100,000 UGX (receiver credited) CR revenue:fees 2,000 UGX (platform fee revenue)
Total debits: 102,000 UGX Total credits: 102,000 UGX (balanced)Every line has:
- Direction — debit (DR) or credit (CR)
- Account — which ledger account is affected (mapped to a wallet or system account)
- Amount — always a positive integer in the smallest currency unit
- Currency — the currency code (e.g.,
UGX)
Example: MoMo Deposit
When a mobile money deposit of 500,000 UGX arrives:
Journal Entry: je_02ABCDEF...Date: 2026-03-18T10:00:03ZDescription: "MoMo deposit MOMO-ABC12345"
DR momo-float:ug-mtn 500,000 UGX (external funds received) CR wallet:user-001 500,000 UGX (user balance increased)
Total debits: 500,000 UGX Total credits: 500,000 UGX (balanced)The momo-float account tracks money that has entered the system from a mobile money provider. This lets you reconcile your MoMo provider balance against your ledger.
Append-Only: No Mutations, No Deletions
Ledger entries are immutable. Once a journal entry is posted, it is never modified or deleted. This is a fundamental property of the system.
Reversing Entries
To correct an error, you post a new journal entry that reverses the original:
Journal Entry: je_03GHIJKL... (reversal of je_01JQHXYZ...)Date: 2026-03-18T11:00:00ZDescription: "Reversal: Transfer from Alice to Bob"
CR wallet:alice 100,000 UGX (sender re-credited) CR wallet:alice 2,000 UGX (fee refunded) DR wallet:bob 100,000 UGX (receiver debited) DR revenue:fees 2,000 UGX (fee revenue reversed)The original entry remains in the ledger unchanged. The reversal is a new entry that references the original. Both are visible in the audit trail.
Materialized Balances
While the ledger is the source of truth, reading a wallet balance by summing all journal lines would be slow. FWallet maintains materialized balances that are updated atomically alongside journal entry posting.
This means:
- Wallet balances are always consistent with the ledger
- Balance reads are fast (single row lookup, not an aggregation query)
- The materialized balance can be verified against the ledger at any time via reconciliation
Overdraft Protection
FWallet checks the sender’s available balance before posting a transfer. If the transfer amount plus fees exceeds the available balance, the transfer is rejected. This check and the balance update happen atomically within the same database transaction.
Reconciliation
Because every money movement is recorded as balanced journal entries, you can verify the integrity of the entire ledger:
- Per-entry balance check — For every journal entry, sum of debits equals sum of credits
- Account balance check — For every account, the materialized balance equals the net of all journal lines touching that account
- Float reconciliation — System float accounts (MoMo, bank) can be compared against external provider statements
These checks can be run as background jobs or on-demand audits.