Docs/Use casesv0.0.1-preview
Rewards and closed-loop economies
Points, credits, and in-game currencies as first-class ledger money — earned, redeemed, conserved, and impossible to overspend.
Loyalty points, store credits, and in-game currencies fail in the same way real money does — double-credits from retried webhooks, negative balances from race conditions, totals that drift from what was issued — but usually with none of real money’s protections, because “it’s just points” until the liability shows up on a balance sheet. Running them on the ledger gives them the same guarantees as dollars, on the same engine.
Points are a currency
Register the unit once; the engine then enforces that points never mix with money — a transfer whose currency doesn’t match its ledger is refused:
pts = c.create_currency(CurrencyInstance(numeric=0, alpha="PTS",
scale=0, name="Loyalty points"))
P = c.create_ledger(LedgerInstance(ledger_id=0, currency=pts.numeric)).ledger_id
scale=0 makes points indivisible. A game with gems and gold registers two currencies and two ledgers; balances can never cross-contaminate.
Earn, redeem, and the liability account
The program pool is the issuing account in the points ledger P; every member account floors at zero:
POOL, MEMBER, APP = 40001, 42001, 1 # integer ids — unique across the engine in this release
c.create_account(AccountCreate(id=POOL, ledger_id=P, currency=pts.numeric,
limit_low=-10**15, limit_high=10**15))
c.create_account(AccountCreate(id=MEMBER, ledger_id=P, currency=pts.numeric,
limit_low=0, limit_high=10**9))
# Earn: pool → member (250 points for a purchase)
c.transfer(TransferCreate(source_id=APP, ledger_id=P, currency=pts.numeric,
debit_account_id=POOL, credit_account_id=MEMBER,
amount=250, txn_id="earn-order-7841"))
# Redeem: member → pool
c.transfer(TransferCreate(..., debit_account_id=MEMBER, credit_account_id=POOL,
amount=100, txn_id="redeem-19"))
Engine behaviours that carry the whole use case:
- Over-redemption is impossible: redeeming 500 against a 150-point balance is declined with
ERR_LIMITby the engine, not by a check you remembered to write. - The pool’s negative balance is your outstanding liability — total points in circulation, exact at any instant, because every earn and redeem is double-entry.
- Retried earns credit once:
txn_id="earn-order-7841"keyed to the triggering event makes webhook redelivery returnOK_DUPLICATEinstead of double-crediting.
Expiring points and promotions
Issue promotional points as a TTL hold instead of a posted transfer: commit when the member spends them, and let the engine expire the unspent remainder to its distinct EXPIRED state — breakage becomes a query, not a batch job.
Gaming and usage economies
The same shape runs in-game economies (mint into a sink account, floor player balances at zero, every drop and purchase idempotent by event id) and usage metering (each API call or kWh a transfer; the meter account’s balance is the invoice). The engine’s throughput characteristics are what make per-event granularity viable — batch frames carry up to 1,024 transfers.