Skip to content

💰 Currency – Bootstrap

Namespace: RevGaming.RevFramework.Currency.Bootstrap

This folder contains the composition entry points for the Currency system. It defines how a runtime ICurrencyService stack is assembled, wrapped, and published for resolution.

The bootstrap layer is intentionally small and predictable: Typically one composed service per scene; if multiple bootstraps publish, the last published override becomes active..

Purpose

  • Build a fully composed currency service at scene load.
  • Enforce a clear, deterministic decorator order.
  • Provide a simple, Inspector-driven setup for designers.
  • Ensure all systems (Inventory, Pickups, UI, Abilities, Economy, etc.) resolve the same service instance.

Bootstrap code does not implement currency logic — it only wires the stack together.


Key files

CurrencyServiceBootstrap.cs

A MonoBehaviour that composes and publishes the active currency service for the scene.

High-level behaviour:

  • Locates the inner baseline service (SceneCurrencyService).
  • Wraps it with decorators in a fixed, intentional order.
  • Automatically inserts a RequireEscrow guard when the active policy requests it.
  • Publishes the final service using the supported bootstrap API.
  • Runs before most user scripts (DefaultExecutionOrder(-499)).

CurrencyBootstrap.cs

The supported public entry point for publishing a composed service.

using var scope = CurrencyBootstrap.Publish(service);
  • Publishes a service for resolution via CurrencyResolve.
  • Returns an IDisposable scope.
  • Disposing the scope clears the resolver override.

This avoids leaking global state and keeps bootstrapping explicit and scoped.


CurrencyServiceOverride.cs (internal)

Internal storage for the currently published resolver override.

  • Not part of the supported runtime API.
  • Used only by bootstrappers, tests, and teachables.
  • Automatically reset on domain reload.

External code should never interact with this type directly.


Default composition pipeline

The built-in bootstrap constructs the service stack in this order:

Stage Layer Purpose
1 Caps Enforce per-currency min/max rules
2 Audit Record wallet deltas with reason/sourceId
3 Authority Gate mutations via ICurrencyAuthority
4 RequireEscrow (guard) Enforce escrow presence when policy requires it
5 Idempotency Drop duplicate replayed requests
6 BatchEvents Emit a single event for multi-op commits

This ordering matches the runtime implementation exactly.

Important
The RequireEscrow layer is a guard only. It does not add escrow capability.


Typical usage

Add CurrencyServiceBootstrap to a GameObject in your scene:

[DefaultExecutionOrder(-499)]
public sealed class CurrencyServiceBootstrap : MonoBehaviour
{
    void Awake()
    {
        // Composition handled internally
    }
}

At runtime:

  1. The bootstrap locates SceneCurrencyService.
  2. The service stack is composed.
  3. The composed service is published.
  4. All consumers resolve via:
var svc = CurrencyResolve.ServiceFrom(this);

Customisation

If you need a different decorator order or a custom stack, create your own bootstrap:

public sealed class MyCurrencyBootstrap : MonoBehaviour
{
    void Awake()
    {
        var inner = FindFirstObjectByType<SceneCurrencyService>();
        ICurrencyService svc = inner;

        // Example: Audit → Caps → Authority only
        svc = CurrencyFactories.WithAudit(svc);
        svc = CurrencyFactories.WithCaps(svc, policy);
        svc = CurrencyFactories.WithAuthority(svc, this);

        using var scope = CurrencyBootstrap.Publish(svc);
    }
}

Custom bootstraps should resolve the raw SceneCurrencyService directly to avoid stacking on top of an already-published override.

You fully control the order and which layers are included.


Escrow notes

  • RequireEscrow is added only when CurrencyPolicy.RequireEscrow == true.
  • The guard does not add escrow functionality.
  • To enable real holds, you must explicitly compose:
  • CurrencyFactories.WithEscrow(...), or
  • CurrencyFactories.WithEscrowAndPump(...).

If no escrow layer exists and the policy requires it: - Debits and transfers fail with CurOpCode.ServiceMissing.


Gotchas

  • Only one bootstrap should typically exist per scene.
  • The last bootstrap to publish wins (the override is process-global; supports additive scenes if ordered intentionally).
  • Removing a bootstrap without disposing the publish scope (for example, in a custom bootstrap) will leave the last published service active.

  • Core — factories, resolver, service override helpers
  • Internal — decorator implementations
  • Authority — authority binders and enforcement
  • Policies — cap rules and escrow requirements