💰 Currency – Exchange¶
Namespace: RevGaming.RevFramework.Currency.Exchange
The Exchange layer provides a simple, table-driven mechanism to convert one currency into another.
It is entirely optional — use it only if your economy needs designer-defined conversion rates (e.g. gold → gems, credits → tokens, scrap → fuel).
The exchange system is stateless, lightweight, and works with any composed ICurrencyService stack.
Purpose¶
- Provide designer-friendly, data-driven exchange rates.
- Keep conversion logic out of the currency service itself.
- Support both quoting (preview) and execution (debit + credit).
- Remain independent of composition layers (Caps, Audit, Authority, Escrow, Idempotency).
Exchange never owns balances — it delegates all mutation to the provided ICurrencyService.
Core assets¶
CurrencyExchangeTable¶
A ScriptableObject defining conversion rules between currencies.
Each rule contains:
fromId— source currency idtoId— destination currency idrate— multiplier applied to the source amountfeePct— optional fee percentage (0= no fee)minSrc— minimum allowable source amountmaxSrc— maximum allowable source amount (0= no upper limit)roundDown— when true, uses floor; otherwise uses rounding
Example entry:
fromId: gold
toId: gems
rate: 0.05
feePct: 10
minSrc: 10
maxSrc: 1000
roundDown: true
Runtime behaviour¶
- Currency ids are normalised (trimmed, lowercased) for lookup.
- Lookup cache is rebuilt on enable/validate.
- Missing rules cause
TryQuoteto return false.
TableCurrencyExchange¶
Concrete implementation of ICurrencyExchange backed by a CurrencyExchangeTable.
API¶
bool TryQuote(CurrencyId src, CurrencyId dst, long srcAmount, out long dstAmount);
CurOpResult TryExchange(
ICurrencyService svc,
GameObject owner,
CurrencyId src,
CurrencyId dst,
long srcAmount,
string reason = "Exchange",
string sourceId = null);
Behaviour¶
- Validates
minSrcandmaxSrc. - Applies conversion as:
dst = srcAmount * rate * (1 - feePct / 100) - Rounds using floor or
Math.Rounddepending onroundDown. - Returns failed results when:
- No matching rule exists (
CurOpCode.NotFound) - Source amount is invalid or below
minSrc - Computed destination amount is
<= 0 TryQuoteperforms math only and does not check wallet balance or policy/authority. Execution may still fail at debit/credit time.
⚠ Important details¶
TryQuoteclampssrcAmountwhenmaxSrc > 0.TryExchangecallsTryQuote, but still debits the originalsrcAmount.- Callers must always quote first to validate amounts.
TryExchangeperforms a debit followed by a credit.- If the credit fails after a successful debit, a best-effort rollback credit is attempted.
- Full transactional atomicity is not guaranteed.
- On credit failure, rollback uses the audit reason label
"RollbackExchange"when auditing is enabled.
Factories¶
Create an exchange engine via CurrencyFactories:
var ex = CurrencyFactories.BuildExchange(exchangeTable);
This returns an ICurrencyExchange without exposing concrete implementation types.
Usage example¶
var ex = CurrencyFactories.BuildExchange(table);
if (ex.TryQuote(new CurrencyId("gold"), new CurrencyId("gems"), 200, out var gems))
Debug.Log($"200 gold = {gems} gems");
var result = ex.TryExchange(
svc, player,
new CurrencyId("gold"),
new CurrencyId("gems"),
200,
"ShopExchange");
if (!result.Success)
Debug.LogWarning(result);
Gotchas¶
feePctis applied after the rate conversion.maxSrc == 0means no upper limit.- Quotes are always recomputed — no caching.
TryQuoteignores caps and policy — it is pure math.TryExchangeperforms real debit/credit via the composed service, so:- Caps
- Audit
- Authority
- RequireEscrow
- Idempotency
may all apply at execution time.
Best practices¶
- Keep exchange tables in dedicated assets.
- Use
feePctinstead of baking fees into rates. - Always call
TryQuotebeforeTryExchange. - If
maxSrcis used, clamp your input amount before callingTryExchange(the exchange does not automatically debit the clamped amount). - Group exchange tables by purpose (global, shop, crafting, tiered economy).
- Store tables in a consistent location (e.g.
Assets/RevFramework/Economy/Exchange/).
Safe to delete¶
If your game does not need currency conversion, you can delete this entire folder safely.
No other part of the Currency system depends on Exchange.
Related¶
- Core — transactions, purchases, awaiters
- Definitions — currency metadata for UI
- Policies — caps and escrow enforcement
- UI — currency bars and debug tooling