Currency / Exchange¶
Purpose¶
The Exchange layer provides optional, data-driven currency conversion.
It defines how one currency can be converted into another using authored rules and a runtime implementation that delegates all mutations to a provided currency service.
This layer does not own balances or wallet state.
Important Notes¶
- Exchange does not own or persist wallet state
- All mutations are delegated to a provided
ICurrencyService - Atomicity is not guaranteed
- Rollback is best-effort only
- Behaviour depends on the composed service stack
What Lives Here¶
CurrencyExchangeTable¶
ScriptableObject containing exchange rules between currency pairs.
Each rule defines:
rate— multiplier applied to the source amountfeePct— percentage fee (0–100)minSrc— minimum source amount (hard floor — requests below it are rejected, not clamped)maxSrc— maximum source amount (0= no cap; hard ceiling — requests above it are rejected, not clamped)roundDown— rounding behaviour
Behaviour:
- Currency identifiers are normalized before lookup
- Rules are stored as
(from, to)pairs - When duplicates exist, the last rule is used
- Table rebuilds on
OnEnableandOnValidate
Example:
if (table.TryGetRule(src, dst, out var rule))
{
// inspect rule
}
TableCurrencyExchange¶
Runtime implementation of ICurrencyExchange backed by a CurrencyExchangeTable.
- Uses the table for quoting
- Delegates mutations to a provided
ICurrencyService - Attempts rollback if credit fails after debit
Usage Guidance¶
TryQuote¶
Non-mutating preview of an exchange.
Returns:
truewhen a quote is producedfalsewhen inputs are invalid, a rule is missing, or the amount is outside[minSrc, maxSrc]
Flow:
- Validate inputs
- Lookup rule
- Reject if outside
[minSrc, maxSrc](hard bounds — no silent clamp) - Apply fee
- Apply rounding
- Reject if result is non-positive
TryQuote does not inspect wallet state or authority.
TryExchange¶
Applies an exchange using a provided ICurrencyService.
Flow:
- Validate inputs
- Ensure rule exists
- Reject if outside
[minSrc, maxSrc]before any mutation (hard bounds — no silent clamp) - Recompute quote
- Debit source currency
- Credit destination currency
- Attempt rollback if credit fails
Returns:
CurOpResult.Okon success- Failure codes from underlying operations
NotFoundwhen no rule existsBelowMinimumwhen the amount is belowminSrcAboveMaximumwhen the amount is abovemaxSrcInvalidArgswhen quoting fails
The exchange never silently converts a different amount than requested. To exchange "up to the cap," the caller quotes/exchanges maxSrc directly.
Usage Guidance¶
Mental model¶
- Exchange defines conversion rules, not balances
- Execution depends on the provided service
- Service composition (caps, authority, etc.) still applies
Internal Use Only¶
This folder provides optional conversion behaviour.
Do not:
- Store wallet state here
- Assume transactional guarantees
- Depend on specific service composition order
Safe to Remove¶
This folder may be removed if currency conversion is not required.
Core Currency functionality will continue to operate without it.
Related Documentation¶
- Abstractions —
ICurrencyExchange,ICurrencyService - Policies — caps and transfer rules
- Definitions — formatting and presentation
- Core — composition and service resolution