5.5 KiB
Safe Inventory Target Sizing (Closed-Form Approximation)
A closed-form approximation for I_T^* (inventory target) per chain so capital allocation can be justified mathematically instead of heuristically. Inputs: expected routed volume, bridge β/γ, refill latency, and target band.
1. Objective
Choose I_T^* per chain and per cW token so that:
- With high probability we do not deplete inventory before bridge/mint can refill.
- Peg stays inside the target band during normal and stressed flow.
- Intervention cost (bridge/mint/burn) remains bounded and preferably linear in volume.
2. Assumptions
- Epoch: time window over which we measure flow (e.g. 1 hour or “refill cycle”).
- V_epoch: expected net outflow of cW token from the pool during one epoch (routed volume, one-sided; we conservatively use net outflow).
- σ: volatility multiplier for V (e.g. 1.5–2 for “stress”); so worst-case net outflow in one epoch is on the order of V_epoch · σ.
- T_refill: refill latency (time or blocks) for bridge/mint to restore inventory on this chain.
- β: bridge fee (fraction) for refill path; γ: fixed cost per refill (gas + relayer).
- Band: we require that after a shock we do not breach the circuit-break band (e.g. 2% for USD). That effectively caps how far inventory can fall before we must intervene.
3. First-order formula
Safe inventory target (units of cW token):
I_T^* ≥ V_epoch · σ · (1 + T_refill / T_epoch) / (1 - β) + γ_buffer
Interpretation:
- V_epoch · σ: worst-case net outflow in one epoch.
- T_refill / T_epoch: number of “epochs” in one refill cycle; we need to cover outflow over that period without going to zero.
- (1 − β): bridge fee reduces effective refill; we size so that after fee we still refill enough.
- γ_buffer: small buffer for fixed costs (e.g. one or a few refill batches); can be set to a multiple of γ or a constant.
Simpler variant (single refill cycle):
If refill is exactly one epoch and we want to absorb one full shock without depleting:
I_T^* ≥ V_epoch · σ / (1 - β) + buffer
buffer = small constant or γ · (typical refill count per epoch).
4. Per-chain inputs (from your config)
From config/simulation-params.json and config/token-map.json:
- β =
bridgeBetafor the chain (refill path). - γ =
bridgeGammaUnits(nominal; use for γ_buffer if desired). - V_epoch: not in config; must be estimated or taken from simulation (e.g. expected routed volume per epoch for that chain/token).
- σ: e.g. 1.5–2 for stress.
- T_refill, T_epoch: in blocks or seconds; chain-dependent (e.g. 1 epoch = 1 hour, T_refill = 20 min → T_refill/T_epoch ≈ 1/3).
5. Depth consistency (D_0 vs I_T^*)
Your PMM uses D = D_0 · min(1, I_T / I_T^*). For the pool to stay “deep” enough under stress:
- D_0 should be sized so that at I_T = I_T^, the pool can absorb a typical trade size without slipping past the band. A simple rule: D_0 on the order of I_T^ / 2 to I_T^* (so that at target inventory, effective depth is meaningful). So:
D_0 ≈ (0.5 to 1.0) · I_T^*
Then the formula above gives I_T^*; set D_0 accordingly.
6. EUR tokens (cWEURC, cWEURT)
- Same formula applies, but use EUR-specific expected volume and, if refill is via a different path, EUR bridge β/γ.
- Use wider band (and possibly higher σ) to reflect FX and dual-source peg risk; that may imply a larger buffer or higher σ in the sizing formula.
7. Usage
- Estimate V_epoch per chain (and per token if needed) from historical or simulated routed volume.
- Set σ (e.g. 1.5–2) and T_refill / T_epoch from chain/contract data.
- Read β (and optionally γ) from
simulation-params.json. - Compute I_T^* from the formula; optionally add γ_buffer.
- Set D_0 ≈ (0.5–1) · I_T^* for that pool.
- Put inventoryTargetUnits (and depth) into
simulation-params.jsonordeployment-status.jsonand run simulations to validate churn and intervention cost.
This gives a justified starting point for capital allocation per chain; tune from there using the three metrics (capture, churn, intervention cost) and stress tests (hub-only, full-quote, bridge shock).
8. Example first pass (chains 1, 56, 137, 25)
Assumptions: σ = 1.5 (USD), σ = 2 (EUR); T_refill/T_epoch = 0.33; γ_buffer = 2·γ. Formula: I_T^* ≥ V_epoch·σ·(1 + 0.33)/(1 − β) + γ_buffer; D_0 = 0.75·I_T^*.
| Chain | Name | Hub | β | γ | V_epoch (example) | I_T^* (USD) | I_T^* (EUR) | D_0 (USD) |
|---|---|---|---|---|---|---|---|---|
| 1 | Ethereum | USDC | 0.001 | 50 | 100_000 | ~200k | ~266k | ~150k |
| 56 | BSC | USDT | 0.001 | 10 | 80_000 | ~160k | ~213k | ~120k |
| 137 | Polygon | USDC | 0.001 | 5 | 60_000 | ~120k | ~160k | ~90k |
| 25 | Cronos | USDT | 0.002 | 15 | 30_000 | ~60k | ~80k | ~45k |
Run node scripts/size-inventory.cjs or node scripts/size-inventory.cjs --v-epoch '{"1":100000,"56":80000,"137":60000,"25":30000}' to regenerate from your simulation-params.json. Use k = 0.15–0.2 on thin chains (e.g. Cronos) if sims show excessive capture; EUR tokens use eurDefaults (higher k, σ, feeBps).