Skip to content

💰 Currency — System Guarantees Matrix

This page defines the behavioural contract of the Currency system.

No marketing. No implication. Just guarantees — and explicit non-guarantees.


🔎 Quick Navigation

  • Core Service
  • Caps
  • Authority
  • Escrow
  • RequireEscrow Guard
  • Idempotency
  • Batch Events
  • Transactions
  • Hold Transactions
  • Persistence
  • Exchange
  • Awaiters
  • UI
  • Definitions
  • Public API
  • Non-Guarantees
  • Final Summary

1️⃣ Core Service

ICurrencyService

Aspect Guarantee
Storage Per-owner (GameObject) balances keyed by CurrencyId
Units Integer minor units (long)
Threading Synchronous, expected to run on Unity main thread
Mutation result Always returns CurOpResult
Events Emits OnWalletChanged after successful mutation
Atomicity ✔ Single-operation atomic
Cross-operation atomicity ❌ Not guaranteed

2️⃣ Caps

CappedCurrencyService

Aspect Guarantee
Min/Max enforcement ✔ Per-currency
Clamp mode Adjusts to bounds
Fail mode Returns BelowMinimum / AboveMaximum
Applies to Set, Credit, Debit, Transfer
Transfer semantics Uses transfer preview helper
Cross-operation atomicity ❌ Not guaranteed

3️⃣ Authority

AuthorityCurrencyService

Aspect Guarantee
Gated ops Credit, Debit, SetBalance, Transfer
Read ops gated ❌ No (GetBalance, EnsureWallet allowed)
Missing binder Returns Unauthorized
Denied mutation Returns Unauthorized
Multiplayer logic ❌ Not provided
Replication ❌ Not provided

4️⃣ Escrow

EscrowCurrencyService

Aspect Guarantee
TryHold Immediate debit + token
Commit Logical finalize (no additional debit)
Release Refund credit
TTL Optional expiry
ExpireStale Refund or drop destroyed owners
Hard atomic multi-op ❌ Not by itself (single-hold atomic only)

5️⃣ RequireEscrow Guard

Aspect Guarantee
When RequireEscrow == true Debit/Transfer must use escrow
Escrow missing Deterministic ServiceMissing
Affects Credit ❌ No
Adds escrow capability ❌ No (guard only)

6️⃣ Idempotency

IdempotencyCurrencyService

Aspect Guarantee
Scope Audit-aware overloads only
Key format "|rid:<token>" suffix in sourceId
Duplicate request Short-circuits to Ok("Idempotent replay")
Non-audit calls Pass through unchanged
Storage Per-owner fixed-capacity ring buffer
Cross-process dedupe ❌ No

Note:
Replayed requests may not emit wallet events or audit entries, depending on decorator order.


7️⃣ Batch Events

Aspect Guarantee
Requires WithBatchEvents
Batch capture Via CurrencyBatch
Emit control Caller decides Emit()
Per-op OnWalletChanged ✔ Always fires normally
Auto-emit on dispose ❌ No
Multi-op collapse ✔ Single aggregated batch event (when emitted)

Batching affects event emission only, not mutation behaviour.


8️⃣ Transactions

CurrencyTxn

Aspect Guarantee
Multi-op staging ✔ Yes
Pre-sanity check Funds + overflow only
Rollback on failure ✔ Best-effort reverse ops
Uses composed stack ✔ Yes
Strong atomicity ❌ Not guaranteed
Escrow-aware ❌ No

9️⃣ Hold Transactions

CurrencyHoldTxn

Aspect Guarantee
Requires escrow ✔ Yes
Phase A Acquire holds first
Phase B Preflight credit effects
Phase C Apply credits
Phase D Commit tokens
Failure before credit phase ✔ Net-zero guarantee
Failure after credit phase ✔ Best-effort unwind
True atomicity ❌ Not guaranteed

🔟 Persistence

Aspect Guarantee
Snapshot type Absolute balances
Restore behaviour Sequential SetBalance
Rollback ✔ Best-effort per owner
Batch emit ✔ Single event when supported
Authority applies ✔ Yes
RequireEscrow applies ✔ Yes
Escrow holds used ❌ No
Cross-owner atomicity ❌ No

11️⃣ Exchange

Aspect Guarantee
Quote Non-mutating calculation with rule constraints (rate, min/max, fee, rounding)
Execution Debit then Credit
Credit failure rollback ✔ Best-effort
Uses composed stack ✔ Yes
True atomic swap ❌ No
Cap/Authority enforced ✔ Yes

12️⃣ Awaiters

CurrencyAwaiters

Aspect Guarantee
Waiting model Event-driven with lightweight fallback polling for invalidation
Async versions May resume off main thread
Coroutine versions Always resume on main thread
Service absence Await returns failure / default

13️⃣ UI

Aspect Guarantee
Event-driven updates ✔ Yes
Service resolution Via CurrencyResolve
Auto-rebind ✔ Yes (service swap detection)
Modifies state ❌ Never
Multiplayer-safe default ❌ Owner fallback is SP convenience

14️⃣ Definitions

Aspect Guarantee
Formatting only ✔ Yes
Affects storage ❌ No
CurrencyId contract Stable primitive
Required for runtime ❌ No

15️⃣ Public API

Aspect Guarantee
Stable supported helpers ✔ Yes
Exposes internal types ❌ No
Batch wrapper safe ✔ Yes
TransferPolicyProviders Preview logic only

🚨 Non-Guarantees

Currency does not guarantee:

  • ❌ True multi-operation atomicity across decorators
  • ❌ Network replication
  • ❌ Server authority enforcement
  • ❌ Deterministic rollback across policy + authority + idempotency
  • ❌ Automatic escrow integration (must compose explicitly)
  • ❌ Cross-scene ownership safety without StableId
  • ❌ Strong transactional guarantees under arbitrary decorator reorder

🎯 Final Summary

Layer Strong Guarantee Best Effort Not Guaranteed
Single operation
Multi-op rollback (CurrencyTxn)
Escrow holds
Persistence atomicity (per owner)
Cross-stack atomicity
Multiplayer replication

System Philosophy

Currency is:

  • Deterministic
  • Explicit
  • Decorator-composed
  • Service-driven
  • Honest about guarantees

It is not:

  • A database
  • A networking framework
  • A financial-grade ACID system