💰 Currency – Policies¶
Namespace: RevGaming.RevFramework.Currency.Policies
The Policies layer defines data-only rule assets used by the Currency system to enforce balance constraints and special requirements such as escrow-required debits.
Policies do not execute logic themselves. They are consumed by internal decorators and helpers to influence behaviour at runtime.
Purpose¶
- Define per-currency constraints (minimum, maximum, clamp vs fail).
- Allow designers to tune economic rules without touching code.
- Act as the authoritative rule source for:
- Caps enforcement
- Escrow requirement enforcement
- Transfer preview calculations
- Keep rules lightweight, deterministic, and runtime-friendly.
Policies are passive configuration assets. All enforcement happens in decorators/helpers.
Core asset¶
CurrencyPolicy¶
A ScriptableObject that defines rule data for a currency stack.
It contains:
- A list of authoring rules (CurrencyRuleAuthoring)
- A fast lookup cache (rebuilt on enable/validate)
- A global Require Escrow toggle
- Built-in sanitisation for invalid data
Runtime code should treat policy assets as read-only configuration.
Supported public query surface:
- TryGetCapRule(CurrencyId id, out CurrencyCapRule rule)
- RequireEscrow
Authoring rules (CurrencyRuleAuthoring)¶
[Serializable]
public sealed class CurrencyRuleAuthoring
{
public string id; // canonical key (normalized for lookups)
public long min; // minimum balance (>= 0)
public long max; // maximum balance (0 = no upper bound)
public CapMode mode; // Clamp or Fail
}
Normalisation & sanitisation¶
On enable / validate:
- Currency ids are trimmed and lowercased for lookups
- Negative
min/maxvalues are corrected to0 - If
min > max(andmax > 0), values are swapped - When multiple rules share the same id, the last rule wins
At runtime, authoring rules are converted into the stable primitive:
CurrencyCapRule
This ensures gameplay code never depends on authoring shapes.
CapMode¶
Defines how out-of-range values are handled:
- Clamp — silently adjust into
[min, max] - Fail — reject the operation with a clear
CurOpCode
Escrow requirement¶
Inside CurrencyPolicy:
public bool RequireEscrow { get; }
When RequireEscrow == true:
- In supported factory compositions, the service is wrapped with
RequireEscrowCurrencyServicewhen this flag is enabled. - Debit and Transfer operations must execute against a service exposing
ICurrencyEscrow - If escrow is missing, operations fail with
CurOpCode.ServiceMissing
Important
Policies do not add escrow. They only enforce its presence.To enable escrow behaviour, you must explicitly compose: -
CurrencyFactories.WithEscrow(...), or -CurrencyFactories.WithEscrowAndPump(...)
How policies are applied¶
1. Caps enforcement¶
Applied by CappedCurrencyService using a shared utility:
CurrencyPolicyUtil.ApplyAbsolute(policy, currency, tentative, out adjusted);
- Applies to Set / Credit / Debit directly.
- Transfer enforcement uses transfer preview helpers that respect the same cap rules.
- Clamps or fails based on rule mode
- Prevents invalid balances from being written
2. Transfer previews¶
By default, transfer previews use the same policy rules for both source and destination currencies.
This behaviour is implemented via an ITransferPolicyProvider.
Default behaviour (same rules for both sides)¶
When no ITransferPolicyProvider is supplied, transfer previews use the active
CurrencyPolicy directly.
In this mode:
- The same cap rule (if present) is applied to both source and destination.
- Preview logic internally queries
CurrencyPolicy.TryGetCapRule(...). - No authoring or internal types are exposed to callers.
Custom providers (advanced)¶
For asymmetric transfer rules (e.g. different source/destination caps),
you may supply your own ITransferPolicyProvider implementation.
This is an advanced extension point intended for tooling and specialised gameplay logic.
3. Escrow enforcement¶
When RequireEscrow == true, the RequireEscrow guard:
- Blocks Debit / Transfer unless escrow is present
- Does not affect Credit or SetBalance
This guarantees escrow is always used when required by design (when the guard is composed).
Usage¶
Applying via factories¶
svc = CurrencyFactories.WithCaps(svc, policy);
Bootstrap-style composition¶
svc = CurrencyFactories.WithCapsAuditAuthority(inner, policy, this);
Escrow enforcement is applied automatically when policy.RequireEscrow is true.
Gotchas¶
max == 0means unbounded, not zero.- Missing rules mean no restriction for that currency.
- Rule matching uses ordinal lowercase string comparison.
- Policies are queried frequently — keep rule lists tidy.
CurrencyIdnormalises input; authoring ids should match.- Transfer previews apply cap rules; exact behaviour depends on the provider used.
Safe to delete¶
If your project does not need: - balance caps - escrow enforcement - transfer previews
You can delete this entire folder safely.
Currency can run without Policies; they are an optional configuration layer.
Related¶
- Decorators — caps and escrow enforcement wrappers
- Core — policy utilities and transfer preview helpers
- Abstractions —
CurrencyCapRule,CapMode