Compare commits
2 Commits
main
...
codex/main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0c3692f64 | ||
|
|
eadcd9a927 |
@@ -1,22 +0,0 @@
|
||||
# Simulation/docs repo — no dedicated hosting VM; CI validates policy + scenarios.
|
||||
name: Validate capital efficiency
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: Run capital-efficiency validation gate
|
||||
run: node scripts/validate-capital-efficiency.cjs
|
||||
@@ -8,21 +8,17 @@ Implementation-grade blueprint for the **home-minted M1 suite on ChainID 138**,
|
||||
- **Pool topology**: Single-sided PMM edge pools per chain (`cW* / HUB` or multi-quote).
|
||||
- **Bot-driven mesh**: Deviation watcher, inventory re-centering, cross-pool routing, bridge-aware throttling; peg bands; oracles; mesh reflexivity controls.
|
||||
|
||||
**GRU policy integration (reference primacy):** this mesh is an **execution and capacity layer** for agreed peg bands and oracles — not an independent FX discovery layer for GRU/XDR. Parent canonical: [docs/04-configuration/GRU_REFERENCE_PRIMACY_AND_MESH_EXECUTION_MODEL.md](../docs/04-configuration/GRU_REFERENCE_PRIMACY_AND_MESH_EXECUTION_MODEL.md).
|
||||
|
||||
## Quick start
|
||||
|
||||
1. See [docs/01-token-map.md](docs/01-token-map.md) for canonical and cW* token mapping.
|
||||
2. See [docs/02-pool-topology.md](docs/02-pool-topology.md) for edge pool design.
|
||||
3. See [docs/06-deployment-recipe.md](docs/06-deployment-recipe.md) for step-by-step deployment.
|
||||
4. Config: [config/token-map.json](config/token-map.json), [config/pool-matrix.json](config/pool-matrix.json), [config/peg-bands.json](config/peg-bands.json), [config/chains.json](config/chains.json).
|
||||
5. **Simulation:** Two-layer model (design vs deployed graph), routing supergraph, PMM edge function, and scenario-based sim: [docs/08-simulation-model.md](docs/08-simulation-model.md). First-pass params: [config/simulation-params.json](config/simulation-params.json), [docs/09-simulation-params-sheet.md](docs/09-simulation-params-sheet.md). Deployment-realistic sim: fill [config/deployment-status.json](config/deployment-status.json). **Hub stable pools** (`cW*/USDC`, `cW*/USDT`) live in `pmmPools[]`. **Volatile cWUSD\* vs TRUU** (mainnet) use `anchorAddresses.TRUU` plus `pmmPoolsVolatile[]` (runbook `docs/03-deployment/MAINNET_PMM_TRUU_CWUSD_PEG_AND_BOT_RUNBOOK.md` §11; parent scripts `deploy-mainnet-pmm-cw-truu-pool.sh`, `add-mainnet-truu-pmm-topup.sh`).
|
||||
5. **Simulation:** Two-layer model (design vs deployed graph), routing supergraph, PMM edge function, and scenario-based sim: [docs/08-simulation-model.md](docs/08-simulation-model.md). First-pass params: [config/simulation-params.json](config/simulation-params.json), [docs/09-simulation-params-sheet.md](docs/09-simulation-params-sheet.md). Deployment-realistic sim: fill [config/deployment-status.json](config/deployment-status.json).
|
||||
6. **Behavioral stability:** What to simulate first, three metrics in economic terms, where it can break: [docs/10-behavioral-stability-analysis.md](docs/10-behavioral-stability-analysis.md). **Safe inventory sizing** (closed-form I_T^* per chain): [docs/11-safe-inventory-sizing.md](docs/11-safe-inventory-sizing.md).
|
||||
7. **Tools:** [scripts/size-inventory.cjs](scripts/size-inventory.cjs) — regenerate I_T^*, D_0 from params (single command, deterministic; see [scripts/README.md](scripts/README.md)). [scripts/validate-deployment-status.cjs](scripts/validate-deployment-status.cjs) — CI validation for deployment-status (phase 1 tokens when bridge=true; pool role/fee/k/tokens).
|
||||
8. **Sim scorecard:** Simulator output contract and pass/fail gates: [docs/12-sim-scorecard.md](docs/12-sim-scorecard.md), [config/scorecard-schema.json](config/scorecard-schema.json). Bridge edge with optional **latency risk** ρ(Δt): [docs/08-simulation-model.md](docs/08-simulation-model.md) §2.
|
||||
9. **Scenario contract:** Formal scenario input schema and three Phase 0 scenarios: [config/scenario-schema.json](config/scenario-schema.json), [config/scenarios/](config/scenarios/). **Routing exposure controls:** [config/routing-controls.json](config/routing-controls.json) (maxTradeSize, cooldown, minImprovementBps, publicRoutingEnabled). **Real sim (PR#1):** `node scripts/run-scenario.cjs hub_only_11` — graph from configs, PMM state, path enumeration + waterfilling, real scorecard (drain half-life, path concentration, capture, churn); see [spec/13-minimal-router-sim.md](spec/13-minimal-router-sim.md).
|
||||
10. **USD-wrapper support lane:** Gas-budgeted micro-trade policy for `cWUSDC` / `cWUSDT` and a runnable scenario: [docs/15-gas-budgeted-micro-trade-support.md](docs/15-gas-budgeted-micro-trade-support.md), [config/scenarios/micro_support_usd_wrappers_1_56_137.json](config/scenarios/micro_support_usd_wrappers_1_56_137.json).
|
||||
11. **Capital efficiency risk simulation:** Simulation-only Monte Carlo overlay for treasury allocation, leverage, peg pressure, volatility throttles, liquidation probability, and parameter optimization: [docs/16-capital-efficiency-risk-simulation.md](docs/16-capital-efficiency-risk-simulation.md), [docs/17-capital-efficiency-contract-blueprint-gate.md](docs/17-capital-efficiency-contract-blueprint-gate.md), [docs/18-capital-efficiency-risk-dashboard-and-runbook.md](docs/18-capital-efficiency-risk-dashboard-and-runbook.md), [docs/19-capital-efficiency-external-approval-evidence.md](docs/19-capital-efficiency-external-approval-evidence.md), [config/capital-efficiency-policy.json](config/capital-efficiency-policy.json), [config/treasury-liquidity-commitments.json](config/treasury-liquidity-commitments.json), stress scenarios under [config/scenarios/](config/scenarios/), and CI-style validation via `node scripts/validate-capital-efficiency.cjs`.
|
||||
|
||||
## Parent repo
|
||||
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Simulation-only capital efficiency policy for Chain 138/cW PMM treasury risk modeling. This is not a live leverage or mint/redemption contract configuration.",
|
||||
"version": "1.0.0",
|
||||
"updated": "2026-04-28",
|
||||
"defaults": {
|
||||
"paths": 1000,
|
||||
"epochs": 365,
|
||||
"seed": 138001,
|
||||
"initialCapital": 1000000,
|
||||
"alpha": 0.7,
|
||||
"leverage": 2,
|
||||
"spreadBps": 35,
|
||||
"volumeEfficiency": 2,
|
||||
"pmmK": 0.1,
|
||||
"liquidityTargetUnits": 1000000
|
||||
},
|
||||
"parameterBands": {
|
||||
"alphaBalanced": [0.65, 0.75],
|
||||
"alphaYieldDominant": [0.75, 0.9],
|
||||
"leverageSafe": [1, 1],
|
||||
"leverageStressDeployable": [1, 1.5],
|
||||
"leverageTarget": [2, 3],
|
||||
"leverageUnstableAbove": 4,
|
||||
"spreadCompetitiveBps": 10,
|
||||
"spreadTargetBps": [30, 50],
|
||||
"spreadVolumeRiskCeilingBps": 100,
|
||||
"volumeEfficiencyPassive": 1,
|
||||
"volumeEfficiencyActive": [2, 4],
|
||||
"volumeEfficiencyHighlyOptimizedAbove": 5
|
||||
},
|
||||
"treasury": {
|
||||
"yieldRatePerEpoch": 0.00035,
|
||||
"volatilityDragLambda": 0.35,
|
||||
"marketMakingScale": 0.04
|
||||
},
|
||||
"volatilityProcess": {
|
||||
"sigma0": 0.03,
|
||||
"sigmaBar": 0.03,
|
||||
"kappa": 0.08,
|
||||
"eta": 0.01
|
||||
},
|
||||
"pegDynamics": {
|
||||
"p0": 1,
|
||||
"beta": 0.25,
|
||||
"imbalanceStd": 0.01,
|
||||
"arbLiquidityCoefficient": 0.65
|
||||
},
|
||||
"risk": {
|
||||
"minExternalLiquidityPct": 0.2,
|
||||
"maxLtvBps": 6500,
|
||||
"hardMaxLtvBps": 7500,
|
||||
"liquidationLtvBps": 8000,
|
||||
"hardMaxLeverage": 4,
|
||||
"liquidationLossFraction": 0.08,
|
||||
"sigmaCrit": 0.08,
|
||||
"throttleLeverageMultiplier": 0.7,
|
||||
"throttleAlphaMultiplier": 0.9,
|
||||
"throttleSpreadBps": 50,
|
||||
"maxSpreadBps": 100,
|
||||
"circuitBreakerSpreadBps": 100,
|
||||
"pegCircuitBreakerBps": 200,
|
||||
"collateralVolatilityHaircut": 0.5,
|
||||
"bankRunRedemptionFeeBps": 100
|
||||
},
|
||||
"gates": {
|
||||
"maxDeployableLeverage": 1.5,
|
||||
"maxLiquidationProbability": 0.02,
|
||||
"maxDrawdownP95": 0.25,
|
||||
"maxPegDeviationFrequency": 0.05,
|
||||
"maxExternalLiquidityFloorViolations": 0
|
||||
},
|
||||
"optimizer": {
|
||||
"paths": 250,
|
||||
"maxCandidates": 250,
|
||||
"topN": 10
|
||||
},
|
||||
"liveExecutionGuard": {
|
||||
"status": "simulation_only",
|
||||
"requiresBeforeContractWork": [
|
||||
"smart_contract_audit_engagement",
|
||||
"governance_approval",
|
||||
"risk_dashboard",
|
||||
"operator_runbook",
|
||||
"treasury_liquidity_commitments"
|
||||
],
|
||||
"auditEngagementEvidence": null,
|
||||
"auditIntakeUrl": "https://cybersecur.d-bis.org/intake.html",
|
||||
"auditIntakeSubmission": {
|
||||
"submittedAt": null,
|
||||
"receiptOrReference": null,
|
||||
"contactEmail": null,
|
||||
"auditFirm": null,
|
||||
"evidenceDoc": "docs/19-capital-efficiency-external-approval-evidence.md"
|
||||
},
|
||||
"signedEngagement": {
|
||||
"firm": null,
|
||||
"engagementReference": null,
|
||||
"signedAt": null,
|
||||
"finalScopeSummary": null
|
||||
},
|
||||
"governanceApprovalEvidence": null,
|
||||
"riskDashboardEvidence": "docs/18-capital-efficiency-risk-dashboard-and-runbook.md",
|
||||
"operatorRunbookEvidence": "docs/18-capital-efficiency-risk-dashboard-and-runbook.md",
|
||||
"treasuryLiquidityCommitmentEvidence": "config/treasury-liquidity-commitments.json"
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Public chain IDs and hub stable per chain for cW* edge pools (EVM). Non-EVM networks used for interoperability are listed under nonEvm (no EIP-155 id) with their own execution/oracle topology references.",
|
||||
"version": "1.2.0",
|
||||
"updated": "2026-04-17",
|
||||
"nonEvm": [
|
||||
{
|
||||
"identifier": "Solana",
|
||||
"vmKind": "SVM",
|
||||
"caip2": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
||||
"hubStableNote": "Primary Solana hubs are SPL USDC then SPL USDT; deep liquidity and single-sided edge pools are defined in the dedicated Solana configs rather than the EVM DODO PMM matrix.",
|
||||
"adapter": "SolanaAdapter",
|
||||
"liquidityMode": "non_evm_deep_plus_single_sided_edge",
|
||||
"oraclePolicy": "cross-chain-pmm-lps/config/solana-oracle-policy.json",
|
||||
"poolMatrix": "cross-chain-pmm-lps/config/solana-pool-matrix.json"
|
||||
}
|
||||
],
|
||||
"description": "Public chain IDs and hub stable per chain for cW* edge pools.",
|
||||
"version": "1.0.0",
|
||||
"updated": "2026-02-26",
|
||||
"chains": [
|
||||
{ "chainId": 1, "name": "Ethereum Mainnet", "hubStable": "USDC" },
|
||||
{ "chainId": 10, "name": "Optimism", "hubStable": "USDC" },
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,10 +23,5 @@
|
||||
"eurUsesEurUsdFeed": true,
|
||||
"stalenessWidenBands": true,
|
||||
"stalenessPauseTrades": true
|
||||
},
|
||||
"gruPolicyIntegration": {
|
||||
"referencePrimacyDoc": "docs/04-configuration/GRU_REFERENCE_PRIMACY_AND_MESH_EXECUTION_MODEL.md",
|
||||
"meshExecutionRole": "public_pmm_corridor_implementation",
|
||||
"summary": "Peg bands and oracle settings bound bot behavior against external references; they do not define GRU/XDR/XAU policy. Parent-repo CI verifies this block via scripts/verify/check-gru-reference-primacy-integration.sh."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,317 +1 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Per-chain pool matrix: which cW* / stable pools to deploy. Single-sided on cW* side.",
|
||||
"version": "1.1.0",
|
||||
"updated": "2026-04-03",
|
||||
"cwTokens": [
|
||||
"cWUSDT",
|
||||
"cWUSDC",
|
||||
"cWAUSDT",
|
||||
"cWEURC",
|
||||
"cWEURT",
|
||||
"cWUSDW",
|
||||
"cWGBPC",
|
||||
"cWGBPT",
|
||||
"cWAUDC",
|
||||
"cWJPYC",
|
||||
"cWCHFC",
|
||||
"cWCADC",
|
||||
"cWXAUC",
|
||||
"cWXAUT"
|
||||
],
|
||||
"strategy": "hub_first",
|
||||
"notes": [
|
||||
"The first-tier public cW matrix now covers the full GRU Wave 1 wrapped set in addition to the older USD/AUSDT/USDW lanes.",
|
||||
"This file is a deployment plan, not proof of live liquidity. Live pool addresses still belong in cross-chain-pmm-lps/config/deployment-status.json."
|
||||
],
|
||||
"chains": {
|
||||
"1": {
|
||||
"name": "Ethereum Mainnet",
|
||||
"hubStable": "USDC",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDC/USDC",
|
||||
"cWEURC/USDC",
|
||||
"cWEURT/USDC",
|
||||
"cWGBPC/USDC",
|
||||
"cWGBPT/USDC",
|
||||
"cWAUDC/USDC",
|
||||
"cWJPYC/USDC",
|
||||
"cWCHFC/USDC",
|
||||
"cWCADC/USDC",
|
||||
"cWXAUC/USDC",
|
||||
"cWXAUT/USDC",
|
||||
"cWUSDW/USDC",
|
||||
"cWAUSDT/USDC"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDT/DAI",
|
||||
"cWUSDC/USDT",
|
||||
"cWUSDC/DAI"
|
||||
]
|
||||
},
|
||||
"56": {
|
||||
"name": "BSC",
|
||||
"hubStable": "USDT",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWEURC/USDT",
|
||||
"cWEURT/USDT",
|
||||
"cWGBPC/USDT",
|
||||
"cWGBPT/USDT",
|
||||
"cWAUDC/USDT",
|
||||
"cWJPYC/USDT",
|
||||
"cWCHFC/USDT",
|
||||
"cWCADC/USDT",
|
||||
"cWXAUC/USDT",
|
||||
"cWXAUT/USDT",
|
||||
"cWUSDW/USDT",
|
||||
"cWAUSDT/USDT"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDT/BUSD",
|
||||
"cWUSDC/USDC",
|
||||
"cWUSDC/BUSD"
|
||||
]
|
||||
},
|
||||
"137": {
|
||||
"name": "Polygon",
|
||||
"hubStable": "USDC",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDC/USDC",
|
||||
"cWEURC/USDC",
|
||||
"cWEURT/USDC",
|
||||
"cWGBPC/USDC",
|
||||
"cWGBPT/USDC",
|
||||
"cWAUDC/USDC",
|
||||
"cWJPYC/USDC",
|
||||
"cWCHFC/USDC",
|
||||
"cWCADC/USDC",
|
||||
"cWXAUC/USDC",
|
||||
"cWXAUT/USDC",
|
||||
"cWUSDW/USDC",
|
||||
"cWAUSDT/USDC"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWUSDT/DAI",
|
||||
"cWUSDC/DAI"
|
||||
]
|
||||
},
|
||||
"10": {
|
||||
"name": "Optimism",
|
||||
"hubStable": "USDC",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDC/USDC",
|
||||
"cWEURC/USDC",
|
||||
"cWEURT/USDC",
|
||||
"cWGBPC/USDC",
|
||||
"cWGBPT/USDC",
|
||||
"cWAUDC/USDC",
|
||||
"cWJPYC/USDC",
|
||||
"cWCHFC/USDC",
|
||||
"cWCADC/USDC",
|
||||
"cWXAUC/USDC",
|
||||
"cWXAUT/USDC",
|
||||
"cWUSDW/USDC",
|
||||
"cWAUSDT/USDC"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWUSDT/DAI",
|
||||
"cWUSDC/DAI"
|
||||
]
|
||||
},
|
||||
"100": {
|
||||
"name": "Gnosis",
|
||||
"hubStable": "USDC",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDC/USDC",
|
||||
"cWEURC/USDC",
|
||||
"cWEURT/USDC",
|
||||
"cWGBPC/USDC",
|
||||
"cWGBPT/USDC",
|
||||
"cWAUDC/USDC",
|
||||
"cWJPYC/USDC",
|
||||
"cWCHFC/USDC",
|
||||
"cWCADC/USDC",
|
||||
"cWXAUC/USDC",
|
||||
"cWXAUT/USDC",
|
||||
"cWUSDW/USDC",
|
||||
"cWAUSDT/USDC"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWUSDT/DAI",
|
||||
"cWUSDT/mUSD",
|
||||
"cWUSDC/mUSD"
|
||||
]
|
||||
},
|
||||
"25": {
|
||||
"name": "Cronos",
|
||||
"hubStable": "USDT",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWEURC/USDT",
|
||||
"cWEURT/USDT",
|
||||
"cWGBPC/USDT",
|
||||
"cWGBPT/USDT",
|
||||
"cWAUDC/USDT",
|
||||
"cWJPYC/USDT",
|
||||
"cWCHFC/USDT",
|
||||
"cWCADC/USDT",
|
||||
"cWXAUC/USDT",
|
||||
"cWXAUT/USDT",
|
||||
"cWUSDW/USDT",
|
||||
"cWAUSDT/USDT"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDT/BUSD",
|
||||
"cWUSDC/USDC",
|
||||
"cWUSDC/BUSD"
|
||||
]
|
||||
},
|
||||
"42220": {
|
||||
"name": "Celo",
|
||||
"hubStable": "USDC",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDC/USDC",
|
||||
"cWEURC/USDC",
|
||||
"cWEURT/USDC",
|
||||
"cWGBPC/USDC",
|
||||
"cWGBPT/USDC",
|
||||
"cWAUDC/USDC",
|
||||
"cWJPYC/USDC",
|
||||
"cWCHFC/USDC",
|
||||
"cWCADC/USDC",
|
||||
"cWXAUC/USDC",
|
||||
"cWXAUT/USDC",
|
||||
"cWUSDW/USDC",
|
||||
"cWAUSDT/USDC"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWUSDT/DAI",
|
||||
"cWUSDC/DAI"
|
||||
]
|
||||
},
|
||||
"43114": {
|
||||
"name": "Avalanche C-Chain",
|
||||
"hubStable": "USDC",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDC/USDC",
|
||||
"cWEURC/USDC",
|
||||
"cWEURT/USDC",
|
||||
"cWGBPC/USDC",
|
||||
"cWGBPT/USDC",
|
||||
"cWAUDC/USDC",
|
||||
"cWJPYC/USDC",
|
||||
"cWCHFC/USDC",
|
||||
"cWCADC/USDC",
|
||||
"cWXAUC/USDC",
|
||||
"cWXAUT/USDC",
|
||||
"cWUSDW/USDC",
|
||||
"cWAUSDT/USDC"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWUSDT/DAI",
|
||||
"cWUSDC/DAI"
|
||||
]
|
||||
},
|
||||
"42161": {
|
||||
"name": "Arbitrum One",
|
||||
"hubStable": "USDC",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDC/USDC",
|
||||
"cWEURC/USDC",
|
||||
"cWEURT/USDC",
|
||||
"cWGBPC/USDC",
|
||||
"cWGBPT/USDC",
|
||||
"cWAUDC/USDC",
|
||||
"cWJPYC/USDC",
|
||||
"cWCHFC/USDC",
|
||||
"cWCADC/USDC",
|
||||
"cWXAUC/USDC",
|
||||
"cWXAUT/USDC",
|
||||
"cWUSDW/USDC",
|
||||
"cWAUSDT/USDC"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWUSDT/DAI",
|
||||
"cWUSDC/DAI"
|
||||
]
|
||||
},
|
||||
"8453": {
|
||||
"name": "Base",
|
||||
"hubStable": "USDC",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDC/USDC",
|
||||
"cWEURC/USDC",
|
||||
"cWEURT/USDC",
|
||||
"cWGBPC/USDC",
|
||||
"cWGBPT/USDC",
|
||||
"cWAUDC/USDC",
|
||||
"cWJPYC/USDC",
|
||||
"cWCHFC/USDC",
|
||||
"cWCADC/USDC",
|
||||
"cWXAUC/USDC",
|
||||
"cWXAUT/USDC",
|
||||
"cWUSDW/USDC",
|
||||
"cWAUSDT/USDC"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWUSDT/DAI",
|
||||
"cWUSDC/DAI"
|
||||
]
|
||||
},
|
||||
"1111": {
|
||||
"name": "Wemix",
|
||||
"hubStable": "USDT",
|
||||
"poolsFirst": [
|
||||
"cWUSDT/USDT",
|
||||
"cWUSDC/USDT",
|
||||
"cWEURC/USDT",
|
||||
"cWEURT/USDT",
|
||||
"cWGBPC/USDT",
|
||||
"cWGBPT/USDT",
|
||||
"cWAUDC/USDT",
|
||||
"cWJPYC/USDT",
|
||||
"cWCHFC/USDT",
|
||||
"cWCADC/USDT",
|
||||
"cWXAUC/USDT",
|
||||
"cWXAUT/USDT",
|
||||
"cWUSDW/USDT",
|
||||
"cWAUSDT/USDT"
|
||||
],
|
||||
"poolsOptional": [
|
||||
"cWUSDT/USDC",
|
||||
"cWUSDT/BUSD",
|
||||
"cWUSDC/USDC",
|
||||
"cWUSDC/BUSD"
|
||||
]
|
||||
}
|
||||
},
|
||||
"liquiditySizingTargets": {}
|
||||
}
|
||||
{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Per-chain pool matrix: which cW* / stable pools to deploy. Single-sided on cW* side.","version":"1.0.0","updated":"2026-02-27","cwTokens":["cWUSDT","cWUSDC","cWAUSDT","cWEURC","cWEURT","cWUSDW"],"strategy":"hub_first","chains":{"1":{"name":"Ethereum Mainnet","hubStable":"USDC","poolsFirst":["cWUSDT/USDC","cWUSDC/USDC","cWEURC/USDC","cWEURT/USDC","cWUSDW/USDC","cWAUSDT/USDC"],"poolsOptional":["cWUSDT/USDT","cWUSDT/DAI","cWUSDC/USDT","cWUSDC/DAI"]},"56":{"name":"BSC","hubStable":"USDT","poolsFirst":["cWUSDT/USDT","cWUSDC/USDT","cWAUSDT/USDT","cWEURC/USDT","cWEURT/USDT","cWUSDW/USDT"],"poolsOptional":["cWUSDT/USDC","cWUSDT/BUSD","cWUSDC/USDC","cWUSDC/BUSD"]},"137":{"name":"Polygon","hubStable":"USDC","poolsFirst":["cWUSDT/USDC","cWUSDC/USDC","cWAUSDT/USDC","cWEURC/USDC","cWEURT/USDC","cWUSDW/USDC"],"poolsOptional":["cWUSDT/USDT","cWUSDC/USDT","cWUSDT/DAI","cWUSDC/DAI"]},"10":{"name":"Optimism","hubStable":"USDC","poolsFirst":["cWUSDT/USDC","cWUSDC/USDC","cWAUSDT/USDC","cWEURC/USDC","cWEURT/USDC","cWUSDW/USDC"],"poolsOptional":["cWUSDT/USDT","cWUSDC/USDT","cWUSDT/DAI","cWUSDC/DAI"]},"100":{"name":"Gnosis","hubStable":"USDC","poolsFirst":["cWUSDT/USDC","cWUSDC/USDC","cWAUSDT/USDC","cWEURC/USDC","cWEURT/USDC","cWUSDW/USDC"],"poolsOptional":["cWUSDT/USDT","cWUSDC/USDT","cWUSDT/DAI","cWUSDT/mUSD","cWUSDC/mUSD"]},"25":{"name":"Cronos","hubStable":"USDT","poolsFirst":["cWUSDT/USDT","cWUSDC/USDT","cWAUSDT/USDT","cWEURC/USDT","cWEURT/USDT","cWUSDW/USDT"],"poolsOptional":["cWUSDT/USDC","cWUSDT/BUSD","cWUSDC/USDC","cWUSDC/BUSD"]},"42220":{"name":"Celo","hubStable":"USDC","poolsFirst":["cWUSDT/USDC","cWUSDC/USDC","cWAUSDT/USDC","cWEURC/USDC","cWEURT/USDC","cWUSDW/USDC"],"poolsOptional":["cWUSDT/USDT","cWUSDC/USDT","cWUSDT/DAI","cWUSDC/DAI"]},"43114":{"name":"Avalanche C-Chain","hubStable":"USDC","poolsFirst":["cWUSDT/USDC","cWUSDC/USDC","cWAUSDT/USDC","cWEURC/USDC","cWEURT/USDC","cWUSDW/USDC"],"poolsOptional":["cWUSDT/USDT","cWUSDC/USDT","cWUSDT/DAI","cWUSDC/DAI"]},"42161":{"name":"Arbitrum One","hubStable":"USDC","poolsFirst":["cWUSDT/USDC","cWUSDC/USDC","cWAUSDT/USDC","cWEURC/USDC","cWEURT/USDC","cWUSDW/USDC"],"poolsOptional":["cWUSDT/USDT","cWUSDC/USDT","cWUSDT/DAI","cWUSDC/DAI"]},"8453":{"name":"Base","hubStable":"USDC","poolsFirst":["cWUSDT/USDC","cWUSDC/USDC","cWAUSDT/USDC","cWEURC/USDC","cWEURT/USDC","cWUSDW/USDC"],"poolsOptional":["cWUSDT/USDT","cWUSDC/USDT","cWUSDT/DAI","cWUSDC/DAI"]},"1111":{"name":"Wemix","hubStable":"USDT","poolsFirst":["cWUSDT/USDT","cWUSDC/USDT","cWAUSDT/USDT","cWEURC/USDT","cWEURT/USDT","cWUSDW/USDT"],"poolsOptional":["cWUSDT/USDC","cWUSDT/BUSD","cWUSDC/USDC","cWUSDC/BUSD"]}},"liquiditySizingTargets":{}}
|
||||
@@ -135,147 +135,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"microTradePolicy": {
|
||||
"type": "object",
|
||||
"description": "Optional gas-budgeted support trades for selected cW wrappers. Use to model tiny corrective or turnover-seeding trades without turning the PMM into a venue.",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"tokens": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Base cW tokens to support, e.g. [\"cWUSDT\", \"cWUSDC\"]"
|
||||
},
|
||||
"quoteTokens": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Allowed quote stables for the support lane, e.g. [\"USDT\", \"USDC\"]"
|
||||
},
|
||||
"preferMatchedQuote": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Prefer cWUSDT/USDT and cWUSDC/USDC when those rails exist; otherwise fall back to another allowed quote."
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"inventory",
|
||||
"alternate",
|
||||
"inventory_or_alternate",
|
||||
"buy_base",
|
||||
"sell_base"
|
||||
],
|
||||
"default": "inventory_or_alternate",
|
||||
"description": "inventory = only trade when pool inventory leaves the band; alternate = buy/sell flip-flop; inventory_or_alternate = inventory-corrective first, alternate only inside band."
|
||||
},
|
||||
"tradeSizeUnits": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"description": "Nominal base-token size per support trade."
|
||||
},
|
||||
"tradesPerEpoch": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Requested support trades per epoch before gas-budget clipping."
|
||||
},
|
||||
"gasCostPerTradeUnits": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"description": "Abstract gas cost per trade for scenario comparison; not chain-native gas units."
|
||||
},
|
||||
"gasBudgetPerEpochUnits": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"description": "Per-epoch gas budget for support trades. Maximum trades = floor(budget / gasCostPerTradeUnits) when gasCostPerTradeUnits > 0."
|
||||
},
|
||||
"inventoryBandFraction": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"default": 0.05,
|
||||
"description": "Keep support trades inventory-aware within +/- this fraction of I_T^* before alternating."
|
||||
},
|
||||
"maxFractionOfTarget": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"default": 0.01,
|
||||
"description": "Clamp each support trade to this fraction of I_T^*."
|
||||
}
|
||||
}
|
||||
},
|
||||
"seed": {
|
||||
"type": "integer",
|
||||
"description": "Optional RNG seed for deterministic runs; if omitted, derived from scenario name"
|
||||
},
|
||||
"capitalEfficiency": {
|
||||
"type": "object",
|
||||
"description": "Optional simulation-only treasury/risk Monte Carlo overlay. Does not configure live contracts.",
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean", "default": false },
|
||||
"paths": { "type": "integer", "minimum": 1 },
|
||||
"epochs": { "type": "integer", "minimum": 1 },
|
||||
"seed": { "type": "integer" },
|
||||
"initialCapital": { "type": "number", "minimum": 0 },
|
||||
"alpha": { "type": "number", "minimum": 0, "maximum": 1 },
|
||||
"leverage": { "type": "number", "minimum": 1 },
|
||||
"spreadBps": { "type": "number", "minimum": 0 },
|
||||
"volumeEfficiency": { "type": "number", "minimum": 0 },
|
||||
"pmmK": { "type": "number", "minimum": 0 },
|
||||
"liquidityTargetUnits": { "type": "number", "minimum": 0 },
|
||||
"treasury": { "type": "object" },
|
||||
"volatilityProcess": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sigma0": { "type": "number", "minimum": 0 },
|
||||
"sigmaBar": { "type": "number", "minimum": 0 },
|
||||
"kappa": { "type": "number", "minimum": 0 },
|
||||
"eta": { "type": "number", "minimum": 0 }
|
||||
}
|
||||
},
|
||||
"pegDynamics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"p0": { "type": "number" },
|
||||
"beta": { "type": "number" },
|
||||
"imbalanceStd": { "type": "number", "minimum": 0 },
|
||||
"arbLiquidityCoefficient": { "type": "number", "minimum": 0 }
|
||||
}
|
||||
},
|
||||
"risk": { "type": "object" },
|
||||
"stress": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"preset": {
|
||||
"type": "string",
|
||||
"enum": ["crash_40pct_external_asset", "high_vol_sigma_spike", "bank_run_redemption_spike", "bridge_shock"]
|
||||
},
|
||||
"epoch": { "type": "integer", "minimum": 0 },
|
||||
"durationEpochs": { "type": "integer", "minimum": 1 },
|
||||
"externalAssetReturn": { "type": "number" },
|
||||
"sigmaAdd": { "type": "number" },
|
||||
"redemptionFraction": { "type": "number", "minimum": 0 },
|
||||
"imbalanceAdd": { "type": "number" },
|
||||
"events": { "type": "array", "items": { "type": "object" } }
|
||||
}
|
||||
},
|
||||
"optimizer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean", "default": false },
|
||||
"paths": { "type": "integer", "minimum": 1 },
|
||||
"topN": { "type": "integer", "minimum": 1 },
|
||||
"maxCandidates": { "type": "integer", "minimum": 1 },
|
||||
"grid": { "type": "object" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"scenario": "bank_run_redemption_spike",
|
||||
"graphMode": "design",
|
||||
"topology": "hub",
|
||||
"chainsIncluded": ["1", "56", "137"],
|
||||
"tokensIncluded": ["cWUSDT", "cWUSDC"],
|
||||
"epochBlocks": 300,
|
||||
"epochs": 180,
|
||||
"orderflowModel": {
|
||||
"distribution": "pareto",
|
||||
"volumeMinUnits": 2500,
|
||||
"volumeMaxUnits": 80000,
|
||||
"tradesPerEpoch": 25,
|
||||
"paretoAlpha": 1.8
|
||||
},
|
||||
"oracleModel": "static",
|
||||
"latencyModel": {
|
||||
"finalityBlocksPerChainPair": {},
|
||||
"rhoPerBlockBps": 0.5
|
||||
},
|
||||
"capitalEfficiency": {
|
||||
"enabled": true,
|
||||
"paths": 1000,
|
||||
"epochs": 180,
|
||||
"seed": 138403,
|
||||
"initialCapital": 1000000,
|
||||
"alpha": 0.65,
|
||||
"leverage": 1.5,
|
||||
"spreadBps": 45,
|
||||
"volumeEfficiency": 2.5,
|
||||
"stress": {
|
||||
"preset": "bank_run_redemption_spike",
|
||||
"epoch": 30,
|
||||
"durationEpochs": 12,
|
||||
"redemptionFraction": 0.25
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"scenario": "chain138_deployed_capital_efficiency",
|
||||
"graphMode": "deployed",
|
||||
"topology": "hub",
|
||||
"chainsIncluded": ["138"],
|
||||
"tokensIncluded": ["cUSDT", "cUSDC", "cEURC", "cEURT", "cXAUC", "cXAUT"],
|
||||
"epochBlocks": 300,
|
||||
"epochs": 120,
|
||||
"orderflowModel": {
|
||||
"distribution": "uniform",
|
||||
"volumeMinUnits": 1000,
|
||||
"volumeMaxUnits": 25000,
|
||||
"tradesPerEpoch": 12
|
||||
},
|
||||
"oracleModel": "static",
|
||||
"latencyModel": {
|
||||
"finalityBlocksPerChainPair": {},
|
||||
"rhoPerBlockBps": 0.5
|
||||
},
|
||||
"capitalEfficiency": {
|
||||
"enabled": true,
|
||||
"paths": 500,
|
||||
"epochs": 120,
|
||||
"seed": 138405,
|
||||
"initialCapital": 1000000,
|
||||
"alpha": 0.7,
|
||||
"leverage": 2,
|
||||
"spreadBps": 35,
|
||||
"volumeEfficiency": 2
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"scenario": "crash_40pct_external_asset",
|
||||
"graphMode": "design",
|
||||
"topology": "hub",
|
||||
"chainsIncluded": ["1", "56", "137"],
|
||||
"tokensIncluded": ["cWUSDT", "cWUSDC"],
|
||||
"epochBlocks": 300,
|
||||
"epochs": 180,
|
||||
"orderflowModel": {
|
||||
"distribution": "uniform",
|
||||
"volumeMinUnits": 5000,
|
||||
"volumeMaxUnits": 30000,
|
||||
"tradesPerEpoch": 15
|
||||
},
|
||||
"oracleModel": "static",
|
||||
"latencyModel": {
|
||||
"finalityBlocksPerChainPair": {},
|
||||
"rhoPerBlockBps": 0.5
|
||||
},
|
||||
"capitalEfficiency": {
|
||||
"enabled": true,
|
||||
"paths": 1000,
|
||||
"epochs": 180,
|
||||
"seed": 138401,
|
||||
"initialCapital": 1000000,
|
||||
"alpha": 0.65,
|
||||
"leverage": 1.5,
|
||||
"spreadBps": 40,
|
||||
"volumeEfficiency": 2.5,
|
||||
"stress": {
|
||||
"preset": "crash_40pct_external_asset",
|
||||
"epoch": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"scenario": "high_vol_sigma_spike",
|
||||
"graphMode": "design",
|
||||
"topology": "hub",
|
||||
"chainsIncluded": ["1", "56", "137"],
|
||||
"tokensIncluded": ["cWUSDT", "cWUSDC"],
|
||||
"epochBlocks": 300,
|
||||
"epochs": 180,
|
||||
"orderflowModel": {
|
||||
"distribution": "lognormal",
|
||||
"volumeMinUnits": 1000,
|
||||
"volumeMaxUnits": 60000,
|
||||
"tradesPerEpoch": 20
|
||||
},
|
||||
"oracleModel": "static",
|
||||
"latencyModel": {
|
||||
"finalityBlocksPerChainPair": {},
|
||||
"rhoPerBlockBps": 0.5
|
||||
},
|
||||
"capitalEfficiency": {
|
||||
"enabled": true,
|
||||
"paths": 1000,
|
||||
"epochs": 180,
|
||||
"seed": 138402,
|
||||
"initialCapital": 1000000,
|
||||
"alpha": 0.65,
|
||||
"leverage": 1.5,
|
||||
"spreadBps": 40,
|
||||
"volumeEfficiency": 3,
|
||||
"stress": {
|
||||
"preset": "high_vol_sigma_spike",
|
||||
"epoch": 30,
|
||||
"durationEpochs": 36,
|
||||
"sigmaAdd": 0.08
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"scenario": "leverage_sweep_1x_to_4x",
|
||||
"graphMode": "design",
|
||||
"topology": "hub",
|
||||
"chainsIncluded": ["1", "56", "137"],
|
||||
"tokensIncluded": ["cWUSDT", "cWUSDC"],
|
||||
"epochBlocks": 300,
|
||||
"epochs": 180,
|
||||
"orderflowModel": {
|
||||
"distribution": "uniform",
|
||||
"volumeMinUnits": 5000,
|
||||
"volumeMaxUnits": 30000,
|
||||
"tradesPerEpoch": 15
|
||||
},
|
||||
"oracleModel": "static",
|
||||
"latencyModel": {
|
||||
"finalityBlocksPerChainPair": {},
|
||||
"rhoPerBlockBps": 0.5
|
||||
},
|
||||
"capitalEfficiency": {
|
||||
"enabled": true,
|
||||
"paths": 1000,
|
||||
"epochs": 180,
|
||||
"seed": 138404,
|
||||
"initialCapital": 1000000,
|
||||
"alpha": 0.7,
|
||||
"leverage": 2,
|
||||
"spreadBps": 40,
|
||||
"volumeEfficiency": 2.5,
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"paths": 250,
|
||||
"topN": 12,
|
||||
"grid": {
|
||||
"alpha": [0.65, 0.7, 0.75, 0.8],
|
||||
"leverage": [1, 1.25, 1.5, 2, 2.5, 3, 3.5, 4],
|
||||
"spreadBps": [30, 40, 50],
|
||||
"pmmK": [0.1, 0.15],
|
||||
"liquidityTargetUnits": [500000, 1000000]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"scenario": "micro_support_usd_wrappers_1_56_137",
|
||||
"graphMode": "design",
|
||||
"topology": "full_quote",
|
||||
"chainsIncluded": ["1", "56", "137"],
|
||||
"tokensIncluded": ["cWUSDT", "cWUSDC"],
|
||||
"epochBlocks": 300,
|
||||
"epochs": 720,
|
||||
"orderflowModel": {
|
||||
"distribution": "uniform",
|
||||
"volumeMinUnits": 1000,
|
||||
"volumeMaxUnits": 80000,
|
||||
"tradesPerEpoch": 35
|
||||
},
|
||||
"microTradePolicy": {
|
||||
"enabled": true,
|
||||
"tokens": ["cWUSDT", "cWUSDC"],
|
||||
"quoteTokens": ["USDT", "USDC"],
|
||||
"preferMatchedQuote": true,
|
||||
"mode": "inventory_or_alternate",
|
||||
"tradeSizeUnits": 250,
|
||||
"tradesPerEpoch": 8,
|
||||
"gasCostPerTradeUnits": 2,
|
||||
"gasBudgetPerEpochUnits": 16,
|
||||
"inventoryBandFraction": 0.05,
|
||||
"maxFractionOfTarget": 0.0025
|
||||
},
|
||||
"oracleModel": "static",
|
||||
"latencyModel": {
|
||||
"finalityBlocksPerChainPair": {},
|
||||
"rhoPerBlockBps": 0.5
|
||||
}
|
||||
}
|
||||
@@ -114,29 +114,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"micro_trade_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Count of support-lane micro-trades executed during the run"
|
||||
},
|
||||
"micro_trade_buy_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"micro_trade_sell_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"micro_trade_volume_total": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"description": "Base-token units traded through the support lane"
|
||||
},
|
||||
"micro_trade_gas_cost_total": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"description": "Abstract gas budget consumed by support trades"
|
||||
},
|
||||
"worst_pool_diagnostic": {
|
||||
"type": "object",
|
||||
"description": "Last-epoch worst pool at pre_arb, post_arb, post_bot",
|
||||
@@ -169,36 +146,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"capital_efficiency_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "True when the simulation-only capital efficiency Monte Carlo overlay was run"
|
||||
},
|
||||
"capital_efficiency_paths": { "type": "integer", "minimum": 1 },
|
||||
"capital_efficiency_epochs": { "type": "integer", "minimum": 1 },
|
||||
"initial_capital": { "type": "number", "minimum": 0 },
|
||||
"alpha": { "type": "number", "minimum": 0, "maximum": 1 },
|
||||
"leverage": { "type": "number", "minimum": 1 },
|
||||
"spread_bps": { "type": "number", "minimum": 0 },
|
||||
"volume_efficiency": { "type": "number", "minimum": 0 },
|
||||
"pmm_k": { "type": "number", "minimum": 0 },
|
||||
"liquidity_target_units": { "type": "number", "minimum": 0 },
|
||||
"roi_mean": { "type": "number" },
|
||||
"roi_p05": { "type": "number" },
|
||||
"roi_p95": { "type": "number" },
|
||||
"pnl_distribution": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"p05": { "type": "number" },
|
||||
"p50": { "type": "number" },
|
||||
"p95": { "type": "number" }
|
||||
}
|
||||
},
|
||||
"max_drawdown_p95": { "type": "number", "minimum": 0 },
|
||||
"liquidation_probability": { "type": "number", "minimum": 0, "maximum": 1 },
|
||||
"peg_deviation_frequency": { "type": "number", "minimum": 0, "maximum": 1 },
|
||||
"external_liquidity_floor_violations": { "type": "integer", "minimum": 0 },
|
||||
"volatility_throttle_events": { "type": "integer", "minimum": 0 },
|
||||
"spread_adjustment_events": { "type": "integer", "minimum": 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Solana execution-oracle policy for the GRU v2 launch set bridged from Chain 138. Reference primacy remains upstream of public execution pools.",
|
||||
"version": "1.0.0",
|
||||
"updated": "2026-04-17",
|
||||
"references": {
|
||||
"referencePrimacyDoc": "docs/04-configuration/GRU_REFERENCE_PRIMACY_AND_MESH_EXECUTION_MODEL.md",
|
||||
"solanaRuntimeConfig": "config/solana-relay-runtime.json",
|
||||
"lineupManifest": "config/solana-gru-bridge-lineup.json"
|
||||
},
|
||||
"hubStables": {
|
||||
"primary": {
|
||||
"symbol": "USDC",
|
||||
"mintEnv": "SOLANA_HUB_USDC_MINT",
|
||||
"mintFallback": "operator_fill_before_pool_deploy",
|
||||
"role": "deep_and_edge_primary_quote"
|
||||
},
|
||||
"secondary": {
|
||||
"symbol": "USDT",
|
||||
"mintEnv": "SOLANA_HUB_USDT_MINT",
|
||||
"mintFallback": "operator_fill_before_secondary_pool_deploy",
|
||||
"role": "overflow_quote_and_route_diversifier"
|
||||
}
|
||||
},
|
||||
"oracleClasses": {
|
||||
"weth_usd": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 30,
|
||||
"maxExecutionDeviationBps": 50,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_eth_usd",
|
||||
"chain138_wrapper_parity_eth_usd",
|
||||
"svm_market_sanity_eth_usd"
|
||||
]
|
||||
},
|
||||
"usd_parity": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 60,
|
||||
"maxExecutionDeviationBps": 25,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_usd_parity",
|
||||
"chain138_canonical_reserve_parity",
|
||||
"svm_market_sanity_stable_usd"
|
||||
]
|
||||
},
|
||||
"eur_usd": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 90,
|
||||
"maxExecutionDeviationBps": 50,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_eur_usd",
|
||||
"chain138_canonical_eur_usd",
|
||||
"institutional_fx_backup_eur_usd"
|
||||
]
|
||||
},
|
||||
"gbp_usd": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 90,
|
||||
"maxExecutionDeviationBps": 50,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_gbp_usd",
|
||||
"chain138_canonical_gbp_usd",
|
||||
"institutional_fx_backup_gbp_usd"
|
||||
]
|
||||
},
|
||||
"aud_usd": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 90,
|
||||
"maxExecutionDeviationBps": 50,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_aud_usd",
|
||||
"chain138_canonical_aud_usd",
|
||||
"institutional_fx_backup_aud_usd"
|
||||
]
|
||||
},
|
||||
"cad_usd": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 90,
|
||||
"maxExecutionDeviationBps": 50,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_cad_usd",
|
||||
"chain138_canonical_cad_usd",
|
||||
"institutional_fx_backup_cad_usd"
|
||||
]
|
||||
},
|
||||
"chf_usd": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 90,
|
||||
"maxExecutionDeviationBps": 50,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_chf_usd",
|
||||
"chain138_canonical_chf_usd",
|
||||
"institutional_fx_backup_chf_usd"
|
||||
]
|
||||
},
|
||||
"jpy_usd": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 90,
|
||||
"maxExecutionDeviationBps": 50,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_jpy_usd",
|
||||
"chain138_canonical_jpy_usd",
|
||||
"institutional_fx_backup_jpy_usd"
|
||||
]
|
||||
},
|
||||
"xau_usd": {
|
||||
"targetQuote": "USDC",
|
||||
"maxOracleAgeSeconds": 60,
|
||||
"maxExecutionDeviationBps": 40,
|
||||
"pauseOnStale": true,
|
||||
"sources": [
|
||||
"gru_reference_service_xau_usd",
|
||||
"chain138_canonical_xau_usd",
|
||||
"institutional_commodity_backup_xau_usd"
|
||||
]
|
||||
}
|
||||
},
|
||||
"assetOracleAssignments": {
|
||||
"WETH": "weth_usd",
|
||||
"WETH10": "weth_usd",
|
||||
"cWUSDT": "usd_parity",
|
||||
"cWUSDC": "usd_parity",
|
||||
"cWAUSDT": "usd_parity",
|
||||
"cWEURC": "eur_usd",
|
||||
"cWEURT": "eur_usd",
|
||||
"cWGBPC": "gbp_usd",
|
||||
"cWGBPT": "gbp_usd",
|
||||
"cWAUDC": "aud_usd",
|
||||
"cWCADC": "cad_usd",
|
||||
"cWCHFC": "chf_usd",
|
||||
"cWJPYC": "jpy_usd",
|
||||
"cWXAUC": "xau_usd",
|
||||
"cWXAUT": "xau_usd"
|
||||
},
|
||||
"operatorRequirements": {
|
||||
"requireFreshReference": true,
|
||||
"requireDualSourceForFiatAndGold": true,
|
||||
"pausePoolActionsWhenReferenceMissing": true,
|
||||
"recordOracleSnapshotWithEachPoolAction": true
|
||||
}
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Deep-pool and single-sided edge-pool topology for the Solana GRU v2 launch set.",
|
||||
"version": "1.0.0",
|
||||
"updated": "2026-04-26",
|
||||
"references": {
|
||||
"solanaRuntimeConfig": "config/solana-relay-runtime.json",
|
||||
"oraclePolicy": "cross-chain-pmm-lps/config/solana-oracle-policy.json",
|
||||
"lineupManifest": "config/solana-gru-bridge-lineup.json",
|
||||
"solanaRelayE2EReadiness": "scripts/verify/check-solana-relay-e2e-readiness.py"
|
||||
},
|
||||
"hubStables": [
|
||||
{
|
||||
"symbol": "USDC",
|
||||
"role": "primary",
|
||||
"mintEnv": "SOLANA_HUB_USDC_MINT"
|
||||
},
|
||||
{
|
||||
"symbol": "USDT",
|
||||
"role": "secondary",
|
||||
"mintEnv": "SOLANA_HUB_USDT_MINT"
|
||||
}
|
||||
],
|
||||
"deepVenueOrder": [
|
||||
"phoenix_clob",
|
||||
"orca_clmm"
|
||||
],
|
||||
"edgeVenueOrder": [
|
||||
"meteora_dlmm",
|
||||
"orca_clmm"
|
||||
],
|
||||
"assets": [
|
||||
{
|
||||
"symbol": "WETH",
|
||||
"oracleClass": "weth_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 500000,
|
||||
"singleSided": false
|
||||
},
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "orca_clmm",
|
||||
"targetDepthUsd": 250000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 100000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "WETH10",
|
||||
"oracleClass": "weth_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 250000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 75000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWUSDT",
|
||||
"oracleClass": "usd_parity",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 400000,
|
||||
"singleSided": false
|
||||
},
|
||||
{
|
||||
"quoteSymbol": "USDT",
|
||||
"venue": "orca_clmm",
|
||||
"targetDepthUsd": 300000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 125000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWUSDC",
|
||||
"oracleClass": "usd_parity",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 400000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDT",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 125000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWAUSDT",
|
||||
"oracleClass": "usd_parity",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 250000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 100000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWEURC",
|
||||
"oracleClass": "eur_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 200000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 75000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWEURT",
|
||||
"oracleClass": "eur_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 150000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 60000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWGBPC",
|
||||
"oracleClass": "gbp_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 150000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 60000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWGBPT",
|
||||
"oracleClass": "gbp_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 125000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 50000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWAUDC",
|
||||
"oracleClass": "aud_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 125000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 50000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWCADC",
|
||||
"oracleClass": "cad_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 125000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 50000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWCHFC",
|
||||
"oracleClass": "chf_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 125000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 50000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWJPYC",
|
||||
"oracleClass": "jpy_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 125000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 50000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWXAUC",
|
||||
"oracleClass": "xau_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 225000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 80000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"symbol": "cWXAUT",
|
||||
"oracleClass": "xau_usd",
|
||||
"deepPools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "phoenix_clob",
|
||||
"targetDepthUsd": 225000,
|
||||
"singleSided": false
|
||||
}
|
||||
],
|
||||
"edgePools": [
|
||||
{
|
||||
"quoteSymbol": "USDC",
|
||||
"venue": "meteora_dlmm",
|
||||
"targetDepthUsd": 80000,
|
||||
"singleSided": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"deploymentGate": {
|
||||
"status": "ready_for_external_pool_driver",
|
||||
"strictRelayCheck": "python3 scripts/verify/check-solana-relay-e2e-readiness.py",
|
||||
"poolReadinessCheck": "python3 scripts/verify/check-solana-pool-readiness.py",
|
||||
"executionPlanCheck": "python3 scripts/verify/build-solana-pool-execution-plan.py",
|
||||
"rule": "RCSC/cW* pools require every pool asset to have bridge-backed operator liquidity inventory, configured hub quote mints, and an explicit venue deployment driver."
|
||||
},
|
||||
"edgeQuoteAssets": [
|
||||
{
|
||||
"symbol": "RCSC",
|
||||
"mint": "6dhRQ2PQTR5FSxK1K45GamWeZj9ggu5NUdBbgjCuXB4g",
|
||||
"role": "reserve_currency_edge_quote",
|
||||
"network": "solana-mainnet-beta"
|
||||
}
|
||||
],
|
||||
"rcscEdgePoolPolicy": {
|
||||
"status": "planned_ready_for_external_pool_driver",
|
||||
"quoteSymbol": "RCSC",
|
||||
"quoteMint": "6dhRQ2PQTR5FSxK1K45GamWeZj9ggu5NUdBbgjCuXB4g",
|
||||
"venueOrder": [
|
||||
"meteora_dlmm",
|
||||
"orca_clmm"
|
||||
],
|
||||
"targetDepthUsdPerPool": 25000,
|
||||
"singleSided": true,
|
||||
"appliesToSymbols": [
|
||||
"cWAUSDT",
|
||||
"cWUSDT",
|
||||
"cWUSDC",
|
||||
"cWEURC",
|
||||
"cWEURT",
|
||||
"cWGBPC",
|
||||
"cWGBPT",
|
||||
"cWAUDC",
|
||||
"cWJPYC",
|
||||
"cWCHFC",
|
||||
"cWCADC",
|
||||
"cWXAUC",
|
||||
"cWXAUT"
|
||||
],
|
||||
"deploymentGate": "requires canonical mints, strict Solana relay E2E readiness pass, hub quote mints, bridge-backed operator cW/WETH inventory, and an explicit venue deployment driver"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "Simulation evidence file for treasury/liquidity commitments. Replace placeholder sources with signed commitments before any live leverage contract work.",
|
||||
"version": "1.0.0",
|
||||
"updated": "2026-04-27",
|
||||
"status": "simulation_only",
|
||||
"minimumExternalLiquidityPct": 0.2,
|
||||
"bridgeBufferPolicy": {
|
||||
"formula": "bridgeReserve >= peakBridgeOutflow * latency",
|
||||
"safetyFactor": 3
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"id": "SIM-POL-001",
|
||||
"type": "policy_floor",
|
||||
"description": "At least 20% of capital remains external/idle liquidity in every deployable profile.",
|
||||
"evidence": "config/capital-efficiency-policy.json",
|
||||
"signedCommitment": null
|
||||
}
|
||||
],
|
||||
"emergencyAuthority": {
|
||||
"multisig": null,
|
||||
"operatorGroup": null,
|
||||
"notes": "Must be populated with real authority evidence before live leverage deployment."
|
||||
},
|
||||
"liveDeploymentAllowed": false
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"updated": "2026-04-14",
|
||||
"homeChainId": 138,
|
||||
"chains": {
|
||||
"1": {
|
||||
"name": "Example Chain",
|
||||
"cwTokens": {
|
||||
"cWUSDT": "0x1111111111111111111111111111111111111111",
|
||||
"cWUSDC": "0x2222222222222222222222222222222222222222"
|
||||
},
|
||||
"anchorAddresses": {
|
||||
"USDC": "0x3333333333333333333333333333333333333333",
|
||||
"USDT": "0x4444444444444444444444444444444444444444"
|
||||
},
|
||||
"pmmPools": [
|
||||
{
|
||||
"base": "cWUSDT",
|
||||
"quote": "USDC",
|
||||
"poolAddress": "0xd011000000000000000000000000000000000001",
|
||||
"feeBps": 3,
|
||||
"k": 1,
|
||||
"role": "public_routing",
|
||||
"publicRoutingEnabled": true
|
||||
}
|
||||
],
|
||||
"gasQuoteAddresses": {
|
||||
"WETH": "0x5555555555555555555555555555555555555555",
|
||||
"USDC": "0x3333333333333333333333333333333333333333"
|
||||
},
|
||||
"gasPmmPools": [
|
||||
{
|
||||
"familyKey": "example",
|
||||
"base": "cWUSDT",
|
||||
"quote": "WETH",
|
||||
"poolAddress": "0xd021000000000000000000000000000000000001",
|
||||
"feeBps": 30,
|
||||
"k": 1,
|
||||
"role": "public_routing",
|
||||
"poolType": "wrapped_native",
|
||||
"venue": "dodo_pmm",
|
||||
"publicRoutingEnabled": true
|
||||
}
|
||||
],
|
||||
"gasReferenceVenues": [
|
||||
{
|
||||
"familyKey": "example",
|
||||
"protocol": "uniswap_v3",
|
||||
"base": "cWUSDT",
|
||||
"quote": "WETH",
|
||||
"venueAddress": "0x7111000000000000000000000000000000000001",
|
||||
"supported": true,
|
||||
"live": true,
|
||||
"routingVisible": true,
|
||||
"reference": true
|
||||
},
|
||||
{
|
||||
"familyKey": "example",
|
||||
"protocol": "1inch",
|
||||
"base": "cWUSDT",
|
||||
"quote": "USDC",
|
||||
"venueAddress": null,
|
||||
"supported": true,
|
||||
"live": true,
|
||||
"routingVisible": true,
|
||||
"aggregatorOnly": true,
|
||||
"dependsOn": [
|
||||
"dodo_pmm",
|
||||
"uniswap_v3"
|
||||
],
|
||||
"indexRequired": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"updated": "2026-04-14",
|
||||
"homeChainId": 138,
|
||||
"chains": {
|
||||
"1": {
|
||||
"name": "Example Chain",
|
||||
"cwTokens": {
|
||||
"cWUSDT": "0x1111111111111111111111111111111111111111",
|
||||
"cWUSDC": "0x2222222222222222222222222222222222222222"
|
||||
},
|
||||
"anchorAddresses": {
|
||||
"USDC": "0x3333333333333333333333333333333333333333",
|
||||
"USDT": "0x4444444444444444444444444444444444444444"
|
||||
},
|
||||
"pmmPools": [
|
||||
{
|
||||
"base": "cWUSDT",
|
||||
"quote": "USDC",
|
||||
"poolAddress": "0x1234567890123456789012345678901234567890",
|
||||
"feeBps": 3,
|
||||
"k": 1,
|
||||
"role": "public_routing",
|
||||
"publicRoutingEnabled": true
|
||||
}
|
||||
],
|
||||
"gasQuoteAddresses": {
|
||||
"WETH": "0x5555555555555555555555555555555555555555",
|
||||
"USDC": "0x3333333333333333333333333333333333333333"
|
||||
},
|
||||
"gasPmmPools": [
|
||||
{
|
||||
"familyKey": "example",
|
||||
"base": "cWUSDT",
|
||||
"quote": "WETH",
|
||||
"poolAddress": "0x6666666666666666666666666666666666666666",
|
||||
"feeBps": 30,
|
||||
"k": 1,
|
||||
"role": "public_routing",
|
||||
"poolType": "wrapped_native",
|
||||
"venue": "dodo_pmm",
|
||||
"publicRoutingEnabled": true
|
||||
},
|
||||
{
|
||||
"familyKey": "example",
|
||||
"base": "cWUSDT",
|
||||
"quote": "USDC",
|
||||
"poolAddress": "0x7777777777777777777777777777777777777777",
|
||||
"feeBps": 30,
|
||||
"k": 1,
|
||||
"role": "public_routing",
|
||||
"poolType": "stable_quote",
|
||||
"venue": "dodo_pmm",
|
||||
"publicRoutingEnabled": true
|
||||
}
|
||||
],
|
||||
"gasReferenceVenues": [
|
||||
{
|
||||
"familyKey": "example",
|
||||
"protocol": "uniswap_v3",
|
||||
"base": "cWUSDT",
|
||||
"quote": "WETH",
|
||||
"venueAddress": "0x8888888888888888888888888888888888888888",
|
||||
"supported": true,
|
||||
"live": false,
|
||||
"routingVisible": false,
|
||||
"reference": true
|
||||
},
|
||||
{
|
||||
"familyKey": "example",
|
||||
"protocol": "balancer",
|
||||
"base": "cWUSDT",
|
||||
"quote": "USDC",
|
||||
"venueAddress": null,
|
||||
"supported": true,
|
||||
"live": false,
|
||||
"routingVisible": false,
|
||||
"reference": false
|
||||
},
|
||||
{
|
||||
"familyKey": "example",
|
||||
"protocol": "curve",
|
||||
"base": "cWUSDT",
|
||||
"quote": "USDC",
|
||||
"venueAddress": null,
|
||||
"supported": true,
|
||||
"live": false,
|
||||
"routingVisible": false,
|
||||
"reference": false
|
||||
},
|
||||
{
|
||||
"familyKey": "example",
|
||||
"protocol": "1inch",
|
||||
"base": "cWUSDT",
|
||||
"quote": "USDC",
|
||||
"venueAddress": null,
|
||||
"supported": true,
|
||||
"live": false,
|
||||
"routingVisible": false,
|
||||
"aggregatorOnly": true,
|
||||
"dependsOn": [
|
||||
"dodo_pmm",
|
||||
"uniswap_v3"
|
||||
],
|
||||
"indexRequired": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,27 +33,8 @@ When multiple quotes exist (USDT/USDC/DAI), the bot routes to the best edge:
|
||||
|
||||
- If the bridge is congested or risk flags trip, bot widens bands / reduces exposure
|
||||
|
||||
### 5. Gas-budgeted micro-support lane
|
||||
|
||||
For selected wrapped USD rails, especially **cWUSDC** and **cWUSDT**, you can reserve a small gas budget for **micro-trades** against **USDC** / **USDT**:
|
||||
|
||||
- Prefer matched-quote rails when they exist: `cWUSDC/USDC`, `cWUSDT/USDT`
|
||||
- Keep each trade tiny relative to `I_T^*`
|
||||
- Use inventory-aware direction first: sell cW into the pool when inventory is too low; buy cW from the pool when inventory is too high
|
||||
- If the pool is already near its inventory target, alternate tiny buys/sells only within a separate gas budget
|
||||
|
||||
Purpose:
|
||||
|
||||
- Give wrappers **observable turnover**
|
||||
- Tighten route discovery and quote confidence
|
||||
- Keep the support lane small enough that the PMM remains a **corridor defense tool**, not the primary venue
|
||||
|
||||
Important: this improves **tradability and price discovery**, but it does **not** create backing or intrinsic value by itself.
|
||||
|
||||
## Peg bands
|
||||
|
||||
**GRU reference primacy (proxmox parent repo):** Bot deviation logic implements **corridors around oracle targets**; it does not replace institution-grade FX/GRU/XDR references. Canonical policy + integration checklist: proxmox `docs/04-configuration/GRU_REFERENCE_PRIMACY_AND_MESH_EXECUTION_MODEL.md`. The peg config carries a machine hook **`gruPolicyIntegration`** in [../config/peg-bands.json](../config/peg-bands.json) (validated by parent `scripts/verify/check-gru-reference-primacy-integration.sh`).
|
||||
|
||||
See [../config/peg-bands.json](../config/peg-bands.json) and [05-oracles.md](05-oracles.md). Summary:
|
||||
|
||||
- **USD-pegged**: Normal ±10–25 bps; stress ±50–150 bps; circuit break >2%
|
||||
|
||||
@@ -17,6 +17,4 @@
|
||||
- Do not trade against a stale oracle; treat staleness as a risk trigger.
|
||||
- Document fallback order (e.g. primary chainlink, secondary Pyth, tertiary internal).
|
||||
|
||||
Config and peg bands: [../config/peg-bands.json](../config/peg-bands.json) (includes **`gruPolicyIntegration`** linking proxmox `docs/04-configuration/GRU_REFERENCE_PRIMACY_AND_MESH_EXECUTION_MODEL.md` — oracle targets remain subordinate to institution-grade references).
|
||||
|
||||
Parent verifier: proxmox `scripts/verify/check-gru-reference-primacy-integration.sh`.
|
||||
Config and peg bands: [../config/peg-bands.json](../config/peg-bands.json).
|
||||
|
||||
@@ -29,22 +29,8 @@ Every run (hub-only, full-quote, bridge shock) should produce a scorecard with a
|
||||
| `intervention_cost_inject_total` | number | Bot inject (bridge-in) cost only |
|
||||
| `intervention_cost_withdraw_total` | number | Bot withdraw cost only |
|
||||
| `intervention_cost_by_chain` | object | Per chain: `{ inject, withdraw }` — which chains are liquidity sinks |
|
||||
| `micro_trade_count` | number | Count of gas-budgeted support trades executed |
|
||||
| `micro_trade_buy_count` | number | Support-lane buys of cW from the pool |
|
||||
| `micro_trade_sell_count` | number | Support-lane sells of cW into the pool |
|
||||
| `micro_trade_volume_total` | number | Base-token units rotated through the support lane |
|
||||
| `micro_trade_gas_cost_total` | number | Abstract gas budget consumed by support trades |
|
||||
| `scenario` | string | e.g. `hub_only_11`, `full_quote_1_56_137`, `bridge_shock_137_56` |
|
||||
| `runId` | string | Optional run identifier |
|
||||
| `roi_mean` | number | Mean capital-efficiency ROI when Monte Carlo is enabled |
|
||||
| `roi_p05` / `roi_p95` | number | 5th/95th percentile ROI band |
|
||||
| `pnl_distribution` | object | PnL percentiles `{p05,p50,p95}` |
|
||||
| `max_drawdown_p95` | number | 95th percentile max drawdown |
|
||||
| `liquidation_probability` | number | Fraction of Monte Carlo paths that liquidated |
|
||||
| `peg_deviation_frequency` | number | Fraction of path-epochs above peg circuit-break threshold |
|
||||
| `external_liquidity_floor_violations` | integer | Count of path-epochs below minimum external liquidity before clamp |
|
||||
| `volatility_throttle_events` | integer | Count of sigma-triggered deleverage/allocation throttle events |
|
||||
| `spread_adjustment_events` | integer | Count of volatility/liquidity/peg-driven spread adjustments |
|
||||
|
||||
**Example (minimal):**
|
||||
|
||||
@@ -88,17 +74,6 @@ From [10-behavioral-stability-analysis.md](10-behavioral-stability-analysis.md):
|
||||
**Pass:** All gates satisfied for the scenario.
|
||||
**Fail:** Any gate violated; do not treat scenario as deployable without parameter change or topology reduction.
|
||||
|
||||
Capital-efficiency scenarios also use `config/capital-efficiency-policy.json` gates:
|
||||
|
||||
| Gate | Default |
|
||||
|------|---------|
|
||||
| Liquidation probability | `<= 0.02` |
|
||||
| p95 max drawdown | `<= 0.25` |
|
||||
| Peg deviation frequency | `<= 0.05` |
|
||||
| External liquidity floor violations | `0` |
|
||||
| Deployable leverage | `<= 1.5x` |
|
||||
| Hard leverage ceiling | `<= 4x` |
|
||||
|
||||
---
|
||||
|
||||
## 3. Phase 0 comparison (three scenarios)
|
||||
@@ -117,14 +92,6 @@ Compare deltas:
|
||||
|
||||
If churn jumps >50% with full-quote → clear “don’t deploy full-quote” rule.
|
||||
|
||||
For **gas-budgeted support scenarios**, compare:
|
||||
|
||||
- **micro_trade_gas_cost_total** vs baseline intervention cost
|
||||
- **micro_trade_volume_total / micro_trade_count** to verify trades stay genuinely micro
|
||||
- **peak_deviation_bps_post_bot** vs the same scenario without micro-support
|
||||
|
||||
If support gas rises faster than the deviation improvement you get back, the support lane is too aggressive.
|
||||
|
||||
---
|
||||
|
||||
## 4. Phase 0: Runnable scenarios and knob guidance
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
# Gas-Budgeted Micro-Trade Support
|
||||
|
||||
Use a small, explicit gas budget to run **tiny support trades** in the most important wrapped USD rails:
|
||||
|
||||
- `cWUSDC` against `USDC`
|
||||
- `cWUSDT` against `USDT`
|
||||
- fallback to the chain hub stable only when the matched rail is unavailable
|
||||
|
||||
## What this is for
|
||||
|
||||
The goal is to give wrapped assets:
|
||||
|
||||
- observable turnover
|
||||
- fresher route discovery
|
||||
- tighter quote confidence
|
||||
- inventory-sensitive support when a pool drifts away from target stock
|
||||
|
||||
This is a **liquidity and price-discovery support lane**. It does **not** create reserve backing or intrinsic value on its own.
|
||||
|
||||
## Operating rule
|
||||
|
||||
Use the support lane only when all of these remain true:
|
||||
|
||||
- the trades stay **micro** relative to `I_T^*`
|
||||
- gas is capped per epoch or time window
|
||||
- matched rails are preferred: `cWUSDC/USDC`, `cWUSDT/USDT`
|
||||
- inventory correction happens first
|
||||
- alternating buys/sells are allowed only inside a small inventory band
|
||||
|
||||
## Suggested control surface
|
||||
|
||||
Model or enforce these knobs:
|
||||
|
||||
- `tradeSizeUnits`
|
||||
- `tradesPerEpoch`
|
||||
- `gasCostPerTradeUnits`
|
||||
- `gasBudgetPerEpochUnits`
|
||||
- `inventoryBandFraction`
|
||||
- `maxFractionOfTarget`
|
||||
- `preferMatchedQuote`
|
||||
|
||||
## Recommended interpretation
|
||||
|
||||
Think of this as paying a small amount of gas to keep the wrapper corridor **economically live**:
|
||||
|
||||
- If pool inventory is too low, sell a tiny amount of cW into the pool.
|
||||
- If pool inventory is too high, buy a tiny amount of cW from the pool.
|
||||
- If inventory is already near target, allow only very small alternating trades, still under the gas cap.
|
||||
|
||||
## Scenario
|
||||
|
||||
The repo includes a ready-made scenario for the first three high-signal public rails:
|
||||
|
||||
```bash
|
||||
node scripts/run-scenario.cjs micro_support_usd_wrappers_1_56_137
|
||||
```
|
||||
|
||||
This runs on chains `1`, `56`, and `137` with `cWUSDT` and `cWUSDC`, full-quote topology, and a separate micro-trade policy budget.
|
||||
@@ -1,145 +0,0 @@
|
||||
# Capital Efficiency Risk Simulation
|
||||
|
||||
This module extends the existing PMM routing simulator with a simulation-only treasury/risk overlay. It evaluates capital allocation, leverage, spread policy, peg pressure, volatility throttles, external liquidity floors, and liquidation probability before any live contract work.
|
||||
|
||||
The model is intentionally wired to the current ecosystem:
|
||||
|
||||
- PMM routing, capture, churn, intervention cost, and peg deviation still come from `scripts/run-scenario.cjs`.
|
||||
- Risk defaults live in `config/capital-efficiency-policy.json`.
|
||||
- Scenario-specific capital assumptions live under `capitalEfficiency` in `config/scenarios/*.json`.
|
||||
- `deployment-status.json` remains the deployed-graph source when `graphMode = deployed`.
|
||||
|
||||
This is not a live leverage configuration. Contract work remains gated by audit engagement evidence, governance approval, operational dashboards, and runbooks.
|
||||
|
||||
## Model
|
||||
|
||||
Each Monte Carlo path tracks:
|
||||
|
||||
- `T`: total capital
|
||||
- `alpha`: treasury/yield allocation
|
||||
- `L`: leverage
|
||||
- `sigma`: mean-reverting volatility
|
||||
- `P`: peg price around 1.0
|
||||
- external liquidity floor
|
||||
- collateral/debt liquidation state
|
||||
|
||||
Per epoch, capital updates with:
|
||||
|
||||
```text
|
||||
T_next = T + T * (yield + market_making - volatility_drag - intervention_drag - redemption_drag)
|
||||
```
|
||||
|
||||
Volatility follows:
|
||||
|
||||
```text
|
||||
sigma_next = sigma + kappa * (sigma_bar - sigma) + eta * N(0, 1)
|
||||
```
|
||||
|
||||
Peg dynamics follow a lightweight imbalance/arb model:
|
||||
|
||||
```text
|
||||
P_next = P + beta * imbalance - arb_liquidity_coefficient * (P - 1)
|
||||
```
|
||||
|
||||
If volatility exceeds `sigmaCrit`, the simulator reduces effective allocation/leverage and widens spread up to the configured ceiling. If external liquidity drops below the policy floor, the path records a violation and clamps allocation.
|
||||
|
||||
## Run
|
||||
|
||||
Baseline routing scenarios are unchanged:
|
||||
|
||||
```bash
|
||||
node scripts/run-scenario.cjs hub_only_11
|
||||
node scripts/run-scenario.cjs bridge_shock_137_56
|
||||
```
|
||||
|
||||
Capital stress scenarios:
|
||||
|
||||
```bash
|
||||
node scripts/run-scenario.cjs crash_40pct_external_asset
|
||||
node scripts/run-scenario.cjs high_vol_sigma_spike
|
||||
node scripts/run-scenario.cjs bank_run_redemption_spike
|
||||
```
|
||||
|
||||
Optimizer sweep:
|
||||
|
||||
```bash
|
||||
node scripts/run-scenario.cjs --optimizer leverage_sweep_1x_to_4x
|
||||
```
|
||||
|
||||
`leverage_sweep_1x_to_4x` also enables optimizer mode by default.
|
||||
|
||||
CI-style validation:
|
||||
|
||||
```bash
|
||||
node scripts/validate-capital-efficiency.cjs
|
||||
```
|
||||
|
||||
## Scorecard Additions
|
||||
|
||||
Capital-enabled scenarios emit:
|
||||
|
||||
- `roi_mean`, `roi_p05`, `roi_p95`
|
||||
- `pnl_distribution`
|
||||
- `max_drawdown_p95`
|
||||
- `liquidation_probability`
|
||||
- `peg_deviation_frequency`
|
||||
- `external_liquidity_floor_violations`
|
||||
- `volatility_throttle_events`
|
||||
- `spread_adjustment_events`
|
||||
|
||||
Optimizer output ranks parameter candidates by ROI penalized for liquidation, drawdown, and peg frequency. A candidate is deployable only if it passes the policy gates in `capital-efficiency-policy.json`.
|
||||
|
||||
## Institutional Defaults
|
||||
|
||||
The default posture is conservative:
|
||||
|
||||
- external liquidity floor: 20% of capital
|
||||
- stress-deployable leverage: 1-1.5x
|
||||
- target leverage for future audited optimization: 2-3x
|
||||
- deployable optimizer candidates capped at 1.5x until external approvals exist
|
||||
- hard leverage rejection above 4x
|
||||
- default max LTV: 65%
|
||||
- hard LTV ceiling: 75%
|
||||
- target spread: 30-50 bps
|
||||
- public PMM remains peg support, not the primary profit engine
|
||||
|
||||
Any later Solidity blueprint must consume the simulator outputs as evidence, not as authority to deploy leverage automatically.
|
||||
|
||||
## Latest Local Run
|
||||
|
||||
Generated on 2026-04-27 from the current configs:
|
||||
|
||||
| Scenario | ROI mean | Liquidation probability | p95 drawdown | Notes |
|
||||
|---|---:|---:|---:|---|
|
||||
| `chain138_deployed_capital_efficiency` | `0.0542` | `0` | `0` | Base deployed Chain 138 graph survives under defaults. |
|
||||
| `crash_40pct_external_asset` | `0.0577` | `0` | `0` | Uses conservative 1.5x stress-deployable leverage. |
|
||||
| `high_vol_sigma_spike` | `0.0409` | `0` | `0.0002` | Uses conservative 1.5x stress-deployable leverage. |
|
||||
| `bank_run_redemption_spike` | `-0.1762` | `0` | `0.2186` | Redemption stress survives below the 25% drawdown gate. |
|
||||
| `leverage_sweep_1x_to_4x` | `0.0679` top deployable | `0` | `0` | Top deployable candidate is capped at 1.5x by policy. |
|
||||
|
||||
Interpretation:
|
||||
|
||||
- Crash/high-volatility profiles require conservative leverage under current assumptions.
|
||||
- Bank-run defense must remain below the 25% p95 drawdown gate.
|
||||
- Optimizer may still simulate 2x-4x candidates, but policy prevents them from being marked deployable until governance/audit evidence changes the cap.
|
||||
|
||||
## Dashboard And Runbook Requirements
|
||||
|
||||
Before any live leverage contract work, operations must expose:
|
||||
|
||||
- ROI band: `roi_mean`, `roi_p05`, `roi_p95`
|
||||
- Drawdown: `max_drawdown_p95`
|
||||
- Liquidation: `liquidation_probability`
|
||||
- Liquidity floor: `external_liquidity_floor_violations`
|
||||
- Peg defense: `peg_deviation_frequency`
|
||||
- Throttles: `volatility_throttle_events`
|
||||
- Spread changes: `spread_adjustment_events`
|
||||
- Existing PMM health: capture, churn, intervention cost, and worst-pool diagnostics
|
||||
|
||||
Deployment remains blocked until:
|
||||
|
||||
- Smart contract audit engagement evidence exists.
|
||||
- Governance approval is recorded.
|
||||
- Risk dashboard and alerting are live.
|
||||
- Operator runbook covers deleverage, circuit breaker, redemption throttle, and treasury liquidity deployment.
|
||||
- Treasury/liquidity commitments are documented.
|
||||
@@ -1,27 +0,0 @@
|
||||
# Capital Efficiency Contract Blueprint Gate
|
||||
|
||||
Status: locally blocked only by external institutional prerequisites.
|
||||
|
||||
This document is the handoff point for the requested contract blueprint. It intentionally does not define deployable Solidity yet because live leverage remains blocked until audit engagement and governance evidence exist. The current local deployable simulation posture caps leverage at `1.5x`.
|
||||
|
||||
## Required Before Blueprint
|
||||
|
||||
- `node scripts/validate-capital-efficiency.cjs` passes.
|
||||
- Stress scenarios pass the policy gates in `config/capital-efficiency-policy.json`.
|
||||
- Audit engagement evidence is recorded.
|
||||
- Governance approval evidence is recorded.
|
||||
- Risk dashboard and alerting are specified in `docs/18-capital-efficiency-risk-dashboard-and-runbook.md` and operational before live deployment.
|
||||
- Operator runbook covers deleverage, circuit breaker, redemption throttle, and treasury liquidity deployment.
|
||||
- Treasury/liquidity commitments are documented.
|
||||
|
||||
## Blueprint Scope Once Unblocked
|
||||
|
||||
- Treasury engine: allocation caps, yield strategy adapter interface, idle/external liquidity floor.
|
||||
- Liquidity engine: PMM/DODO provider hooks, pool role separation, route exposure controls.
|
||||
- Leverage engine: LP-token collateral accounting, borrow/redeploy loop, max LTV checks.
|
||||
- Risk engine: volatility throttle, spread adjustment, peg circuit breaker, liquidation guard.
|
||||
- Keeper flow: rebalance, auto-deleverage, and dashboard event emission.
|
||||
|
||||
## Current Decision
|
||||
|
||||
Do not implement live leverage contracts. The local simulator gates can be satisfied at conservative leverage, but audit engagement and governance approval remain mandatory external prerequisites.
|
||||
@@ -1,78 +0,0 @@
|
||||
# Capital Efficiency Risk Dashboard And Operator Runbook
|
||||
|
||||
Status: simulation-only operational specification.
|
||||
|
||||
This document removes the local documentation blocker for dashboard and runbook requirements. It does not remove external approval blockers: audit engagement and governance approval still require signed evidence.
|
||||
|
||||
## Dashboard Metrics
|
||||
|
||||
Expose these metrics from the latest scorecards:
|
||||
|
||||
| Metric | Source field | Alert |
|
||||
|---|---|---|
|
||||
| ROI mean | `roi_mean` | Informational |
|
||||
| ROI lower band | `roi_p05` | Alert if negative in deployable profile |
|
||||
| ROI upper band | `roi_p95` | Informational |
|
||||
| p95 drawdown | `max_drawdown_p95` | Critical if `> 0.25` |
|
||||
| Liquidation probability | `liquidation_probability` | Critical if `> 0.02` |
|
||||
| Peg deviation frequency | `peg_deviation_frequency` | Critical if `> 0.05` |
|
||||
| External liquidity floor violations | `external_liquidity_floor_violations` | Critical if `> 0` |
|
||||
| Volatility throttle events | `volatility_throttle_events` | Warning if rising versus baseline |
|
||||
| Spread adjustment events | `spread_adjustment_events` | Warning if rising versus baseline |
|
||||
| PMM capture | `capture_mean` | Warning if sustained above target corridor |
|
||||
| Inventory churn | `churn_mean`, `churn_p95` | Warning if outside scorecard gates |
|
||||
| Intervention cost | `intervention_cost_per_1M_volume` | Warning if nonlinear versus volume |
|
||||
|
||||
## Operator Procedures
|
||||
|
||||
### Deleverage
|
||||
|
||||
1. Stop new leverage increases.
|
||||
2. Set deployable profile to `leverage <= 1.5`.
|
||||
3. Reduce `alpha` to preserve the external liquidity floor.
|
||||
4. Re-run:
|
||||
```bash
|
||||
node scripts/validate-capital-efficiency.cjs
|
||||
```
|
||||
5. Resume only when crash, high-volatility, and bank-run scenarios pass policy gates.
|
||||
|
||||
### Spread Widening
|
||||
|
||||
1. If volatility exceeds `sigmaCrit`, widen spread to `throttleSpreadBps`.
|
||||
2. If peg deviation exceeds circuit-break threshold, widen to `circuitBreakerSpreadBps`.
|
||||
3. Confirm `spread_adjustment_events` stabilizes and peg deviation frequency remains within gate.
|
||||
|
||||
### Redemption Throttle
|
||||
|
||||
1. Trigger when bank-run scorecard approaches drawdown gate.
|
||||
2. Increase redemption fee according to policy.
|
||||
3. Pause non-essential outflows.
|
||||
4. Deploy treasury liquidity only from documented sources in `config/treasury-liquidity-commitments.json`.
|
||||
|
||||
### Treasury Liquidity Deployment
|
||||
|
||||
1. Confirm external liquidity remains at or above 20%.
|
||||
2. Confirm bridge buffer satisfies `bridgeReserve >= peakBridgeOutflow * latency`.
|
||||
3. Execute only through approved operator/multisig authority.
|
||||
4. Record evidence before marking any live deployment gate complete.
|
||||
|
||||
### Peg Circuit Breaker
|
||||
|
||||
1. Stop adding public-routing liquidity to affected pools.
|
||||
2. Prefer defense-only rebalancing.
|
||||
3. Re-run deployed Chain 138 and stress scenarios.
|
||||
4. Re-enable only when peg deviation frequency is within gate.
|
||||
|
||||
## Evidence Status
|
||||
|
||||
| Gate | Local status |
|
||||
|---|---|
|
||||
| Risk dashboard specification | Complete in this document |
|
||||
| Operator runbook | Complete in this document |
|
||||
| Treasury/liquidity commitment structure | Complete in `config/treasury-liquidity-commitments.json` |
|
||||
| Audit engagement | External evidence required |
|
||||
| Governance approval | External evidence required |
|
||||
|
||||
## Current Deployable Posture
|
||||
|
||||
Until audit and governance evidence exists, the only locally gate-passing posture is simulation-only with deployable optimizer leverage capped at `1.5x`.
|
||||
@@ -1,92 +0,0 @@
|
||||
# Capital Efficiency External Approval Evidence
|
||||
|
||||
**Status:** intake path **ready** (CyberSecur Global form live). **Submission / signed engagement** still pending — this register tracks evidence as it arrives.
|
||||
|
||||
This file is the canonical evidence register for the remaining non-local blockers. Populate it with signed references before changing `liveExecutionGuard.status` away from `simulation_only`.
|
||||
|
||||
## Audit intake path (ready)
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Audit firm (requested) | CyberSecur Global |
|
||||
| Intake URL | `https://cybersecur.d-bis.org/intake.html` |
|
||||
| Security contact | `https://cybersecur.d-bis.org/.well-known/security.txt` |
|
||||
| Intake fields | Organization, contact email, repository URL, chains/deployments, timeline, notes |
|
||||
| Blocker class | ~~Intake path unknown~~ → **submission / engagement pending** |
|
||||
|
||||
### Requested scope (for manual submission)
|
||||
|
||||
Use normal browser submission (Web3Forms may reject scripted POSTs). Notes should reference at minimum:
|
||||
|
||||
- **Chain 138** deployments and RPC/explorer context you want reviewed.
|
||||
- **cW/c\* PMM mesh** — routing surfaces and reserves relevant to capital-efficiency claims.
|
||||
- **Capital-efficiency simulator** — this repo’s Monte Carlo overlay (`config/capital-efficiency-policy.json`, scenarios, validators).
|
||||
- **Future blueprint** — treasury / liquidity / leverage / risk / keeper alignment (design and audit-readiness; live leverage contracts remain gated).
|
||||
|
||||
## Intake submission record (pending)
|
||||
|
||||
Fill when submitted:
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Submission date | |
|
||||
| Intake receipt / reference | (email confirmation, ticket id, or Web3Forms reference if provided) |
|
||||
| Contact email | |
|
||||
| Audit firm name | CyberSecur Global (expected) |
|
||||
| Evidence URI / path | This file + policy JSON keys below |
|
||||
|
||||
## Audit engagement (signed)
|
||||
|
||||
Fill when a statement of work or engagement letter exists:
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Firm | |
|
||||
| Engagement reference | |
|
||||
| Signed date | |
|
||||
| Final scope (short) | |
|
||||
| Evidence URI | |
|
||||
|
||||
Historical placeholder row (superseded by tables above):
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Engagement reference | Pending external engagement |
|
||||
| Signed date | Pending external engagement |
|
||||
| Scope | Treasury engine, liquidity engine, leverage engine, risk engine, keeper/deleverage flow, oracle/circuit breaker integration |
|
||||
| Evidence URI | Pending submitted intake receipt / signed engagement |
|
||||
|
||||
## Audit Intake Package
|
||||
|
||||
Use the CyberSecur Global intake form to request the audit. The request should include:
|
||||
|
||||
- Organization: DBIS / Chain 138 capital-efficiency simulation and future leverage blueprint.
|
||||
- Repository URL: canonical repository or private review bundle URI.
|
||||
- Chains / deployments: Chain 138, cW/c* PMM mesh, DODO PMM surfaces, vault/reserve/liquidation references.
|
||||
- Timeline: simulation review first; Solidity blueprint review only after governance approval.
|
||||
- Notes: live leverage contracts are blocked; requested scope is design/risk/audit-readiness plus future contract blueprint review.
|
||||
|
||||
## Governance Approval
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Body | Pending governance action |
|
||||
| Resolution ID | Pending governance action |
|
||||
| Approval date | Pending governance action |
|
||||
| Approved policy cap | Simulation-only, max deployable leverage `1.5x` |
|
||||
| Evidence URI | Pending governance action |
|
||||
|
||||
## Current Enforcement
|
||||
|
||||
- `config/capital-efficiency-policy.json` keeps `liveExecutionGuard.status = simulation_only`.
|
||||
- `scripts/validate-capital-efficiency.cjs` requires dashboard, runbook, and liquidity commitment evidence paths.
|
||||
- Live leverage contracts remain blocked until the pending audit and governance evidence is real, dated, and reviewable.
|
||||
|
||||
## Operator checklist (Web3Forms)
|
||||
|
||||
1. Rotate access key in Web3Forms dashboard if needed.
|
||||
2. Set `CYBERSECUR_WEB3FORMS_ACCESS_KEY` in operator dotenv (see project `.env.master.example`).
|
||||
3. Redeploy static site: `scripts/deployment/sync-cybersecur-global-to-ct7810.sh` from proxmox repo (renders intake when key is set).
|
||||
4. Verify: `curl -I https://cybersecur.d-bis.org/intake.html` and `curl -I https://cybersecur.d-bis.org/.well-known/security.txt`.
|
||||
|
||||
**Proxmox repo parity (GitHub + Gitea):** Before audit submission, confirm parent **`proxmox`** `master` equals **`origin/master`** and **`gitea/master`** (same commit hash). See **`scripts/git/README-gitea-proxmox-sync.md`** in that repo (parity check section).
|
||||
@@ -30,9 +30,7 @@ Validates `config/deployment-status.json` for minimum viable deployed graph. Use
|
||||
**Rules:**
|
||||
|
||||
- If `bridgeAvailable === true` on a chain, `cwTokens` must include at least **cWUSDT** and **cWUSDC** (phase 1).
|
||||
- For each `pmmPool` and each `pmmPoolsVolatile[]` entry: `role` ∈ {defense, public_routing, truu_routing}; `feeBps` and `k` present; `base`/`quote` (or `tokenIn`/`tokenOut`) exist in `cwTokens` or `anchorAddresses` (e.g. mainnet **TRUU** under `anchorAddresses.TRUU`). Non-zero `poolAddress` must not be the zero address.
|
||||
- For each `gasReferenceVenues[]` entry: `supported` must be explicit, `aggregatorOnly` is only valid for `1inch`, `supported=false` means the lane is explicitly unsupported (and can never be live), and `supported=true` with `live=false` / `routingVisible=false` means the lane is planned or staged.
|
||||
- `aggregatorOnly=true` rows are routing overlays, not stand-alone live venues.
|
||||
- For each `pmmPool`: `role` ∈ {defense, public_routing}; `feeBps` and `k` present; `base`/`quote` (or `tokenIn`/`tokenOut`) exist in `cwTokens` or `anchorAddresses`.
|
||||
|
||||
**Run:**
|
||||
|
||||
@@ -42,25 +40,12 @@ node scripts/validate-deployment-status.cjs
|
||||
|
||||
**Exit code:** 0 if valid, 1 if invalid (errors to stderr).
|
||||
|
||||
**Native protocol-only examples:**
|
||||
|
||||
```bash
|
||||
node scripts/validate-deployment-status.cjs config/validation-examples/native-protocol-only.valid.json
|
||||
node scripts/validate-deployment-status.cjs config/validation-examples/native-protocol-only.invalid.json
|
||||
```
|
||||
|
||||
The invalid example intentionally includes a placeholder scaffold and a non-native live `1inch` row to exercise the rejection path.
|
||||
|
||||
**Parent proxmox repo:** live Mainnet cW/TRUU pool deploy and ratio-matched top-up scripts live under `scripts/deployment/` (`deploy-mainnet-pmm-cw-truu-pool.sh`, `add-mainnet-truu-pmm-topup.sh`); see `docs/03-deployment/MAINNET_PMM_TRUU_CWUSD_PEG_AND_BOT_RUNBOOK.md` §11.
|
||||
|
||||
---
|
||||
|
||||
## run-scenario.cjs
|
||||
|
||||
Builds the **real** routing graph from configs, runs epochs with PMM state updates, path enumeration + waterfilling, **arb step** (implied-price deviation, capped corrective trades, profit gate), **bot step** (inject/withdraw at 0.5×/1.5× I_T^* with intervention cost β/γ/ρ), and optional **bridge shock** trades. Emits a **real scorecard** (PR#2). Runs are **deterministic** when `scenario.seed` is set or derived from scenario name.
|
||||
|
||||
Scenarios may also include an optional **microTradePolicy** for gas-budgeted support trades in selected USD wrapper rails. This is meant to model tiny, controlled `cWUSDC` / `cWUSDT` turnover-seeding or inventory-corrective activity, not to turn the PMM into a venue.
|
||||
|
||||
**Configs used:** `simulation-params.json`, `token-map.json`, `routing-controls.json`; `deployment-status.json` only when `graphMode = deployed`. Pool topology from `pool-matrix.json` and scenario `topology` / `fullQuoteChains`.
|
||||
|
||||
**Tuning constants (in script):**
|
||||
@@ -79,33 +64,8 @@ Scenarios may also include an optional **microTradePolicy** for gas-budgeted sup
|
||||
node scripts/run-scenario.cjs hub_only_11
|
||||
node scripts/run-scenario.cjs --scenario full_quote_1_56_137
|
||||
node scripts/run-scenario.cjs bridge_shock_137_56
|
||||
node scripts/run-scenario.cjs micro_support_usd_wrappers_1_56_137
|
||||
node scripts/run-scenario.cjs crash_40pct_external_asset
|
||||
node scripts/run-scenario.cjs high_vol_sigma_spike
|
||||
node scripts/run-scenario.cjs bank_run_redemption_spike
|
||||
node scripts/run-scenario.cjs --optimizer leverage_sweep_1x_to_4x
|
||||
```
|
||||
|
||||
**Output:** JSON scorecard including: `capture_mean`, `churn_mean`, `drain_half_life_epochs`, `path_concentration_index`; `intervention_cost_total` / `intervention_cost_inject_total` / `intervention_cost_withdraw_total` / `intervention_cost_by_chain` / `intervention_cost_per_1M_volume`; `micro_trade_count` / `micro_trade_volume_total` / `micro_trade_gas_cost_total`; `peak_deviation_bps` (post-arb), `peak_deviation_bps_pre_arb`, `peak_deviation_bps_post_arb`, `peak_deviation_bps_post_bot`; `arb_volume_total`, `arb_profit_total` (execution-based, not mid). See [docs/12-sim-scorecard.md](../docs/12-sim-scorecard.md) and [config/scorecard-schema.json](../config/scorecard-schema.json).
|
||||
|
||||
When `capitalEfficiency.enabled = true`, output also includes Monte Carlo capital-risk metrics: `roi_mean`, `roi_p05`, `roi_p95`, `pnl_distribution`, `max_drawdown_p95`, `liquidation_probability`, `peg_deviation_frequency`, `external_liquidity_floor_violations`, `volatility_throttle_events`, and `spread_adjustment_events`. Optimizer mode emits ranked parameter candidates and never treats leverage above the configured hard ceiling as deployable.
|
||||
**Output:** JSON scorecard including: `capture_mean`, `churn_mean`, `drain_half_life_epochs`, `path_concentration_index`; `intervention_cost_total` / `intervention_cost_inject_total` / `intervention_cost_withdraw_total` / `intervention_cost_by_chain` / `intervention_cost_per_1M_volume`; `peak_deviation_bps` (post-arb), `peak_deviation_bps_pre_arb`, `peak_deviation_bps_post_arb`, `peak_deviation_bps_post_bot`; `arb_volume_total`, `arb_profit_total` (execution-based, not mid). See [docs/12-sim-scorecard.md](../docs/12-sim-scorecard.md) and [config/scorecard-schema.json](../config/scorecard-schema.json).
|
||||
|
||||
**Orderflow:** Trade sizes use `distribution: "uniform"` by default. Scenario schema supports `lognormal` / `pareto` for skewed (many small + occasional whale) flows; implement in `sampleTrade()` when needed.
|
||||
|
||||
---
|
||||
|
||||
## validate-capital-efficiency.cjs
|
||||
|
||||
Runs CI-style checks for the simulation-only capital efficiency overlay:
|
||||
|
||||
- JSON parse for policy, schemas, and scenarios
|
||||
- Baseline scenario remains capital-overlay free
|
||||
- Stress scenarios emit capital risk fields
|
||||
- Deterministic repeat check
|
||||
- Optimizer deployable candidates respect `maxDeployableLeverage`
|
||||
|
||||
**Run:**
|
||||
|
||||
```bash
|
||||
node scripts/validate-capital-efficiency.cjs
|
||||
```
|
||||
|
||||
@@ -48,32 +48,12 @@ function rng() {
|
||||
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
}
|
||||
function normalSample() {
|
||||
const u1 = Math.max(rng(), 1e-12);
|
||||
const u2 = Math.max(rng(), 1e-12);
|
||||
return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
||||
}
|
||||
function hashScenarioName(name) {
|
||||
let h = 0;
|
||||
for (let i = 0; i < name.length; i++) h = (Math.imul(31, h) + name.charCodeAt(i)) >>> 0;
|
||||
return h;
|
||||
}
|
||||
|
||||
function percentile(values, p) {
|
||||
if (!values.length) return 0;
|
||||
const sorted = [...values].sort((a, b) => a - b);
|
||||
const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1));
|
||||
return sorted[idx];
|
||||
}
|
||||
|
||||
function round2(n) {
|
||||
return Math.round((Number(n) || 0) * 100) / 100;
|
||||
}
|
||||
|
||||
function round4(n) {
|
||||
return Math.round((Number(n) || 0) * 10000) / 10000;
|
||||
}
|
||||
|
||||
function loadJson(p) {
|
||||
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
||||
}
|
||||
@@ -81,9 +61,6 @@ function loadJson(p) {
|
||||
function loadConfigs(scenario) {
|
||||
const simulationParams = loadJson(path.join(CONFIG_DIR, 'simulation-params.json'));
|
||||
const tokenMap = loadJson(path.join(CONFIG_DIR, 'token-map.json'));
|
||||
const capitalEfficiencyPolicy = fs.existsSync(path.join(CONFIG_DIR, 'capital-efficiency-policy.json'))
|
||||
? loadJson(path.join(CONFIG_DIR, 'capital-efficiency-policy.json'))
|
||||
: {};
|
||||
const routingControls = fs.existsSync(path.join(CONFIG_DIR, 'routing-controls.json'))
|
||||
? loadJson(path.join(CONFIG_DIR, 'routing-controls.json'))
|
||||
: { defaults: { publicRoutingEnabled: true, maxTradeSizeUnits: null } };
|
||||
@@ -95,25 +72,14 @@ function loadConfigs(scenario) {
|
||||
const poolMatrix = fs.existsSync(path.join(CONFIG_DIR, 'pool-matrix.json'))
|
||||
? loadJson(path.join(CONFIG_DIR, 'pool-matrix.json'))
|
||||
: { chains: {} };
|
||||
return { simulationParams, tokenMap, capitalEfficiencyPolicy, routingControls, deploymentStatus, poolMatrix };
|
||||
return { simulationParams, tokenMap, routingControls, deploymentStatus, poolMatrix };
|
||||
}
|
||||
|
||||
function buildGraph(scenario, configs) {
|
||||
const { simulationParams, tokenMap, poolMatrix, deploymentStatus } = configs;
|
||||
const { simulationParams, tokenMap, poolMatrix } = configs;
|
||||
const chains = simulationParams.chains || {};
|
||||
const publicChains = tokenMap.publicChains || {};
|
||||
const deployedChains = deploymentStatus?.chains || {};
|
||||
const deployedTokenSet = new Set();
|
||||
if (scenario.graphMode === 'deployed') {
|
||||
for (const chain of Object.values(deployedChains)) {
|
||||
for (const t of Object.keys(chain.cwTokens || {})) deployedTokenSet.add(t);
|
||||
for (const p of [...(chain.pmmPools || []), ...(chain.pmmPoolsVolatile || []), ...(chain.gasPmmPools || [])]) {
|
||||
if (p.base) deployedTokenSet.add(p.base);
|
||||
if (p.tokenIn) deployedTokenSet.add(p.tokenIn);
|
||||
}
|
||||
}
|
||||
}
|
||||
const cwTokens = scenario.tokensIncluded || (deployedTokenSet.size ? Array.from(deployedTokenSet) : tokenMap.bridgedSymbols) || ['cWUSDT', 'cWUSDC', 'cWAUSDT', 'cWEURC', 'cWEURT', 'cWUSDW'];
|
||||
const cwTokens = scenario.tokensIncluded || tokenMap.bridgedSymbols || ['cWUSDT', 'cWUSDC', 'cWAUSDT', 'cWEURC', 'cWEURT', 'cWUSDW'];
|
||||
const chainIds = scenario.chainsIncluded || Object.keys(chains);
|
||||
const topology = scenario.topology || 'hub';
|
||||
const fullQuoteChains = scenario.fullQuoteChains
|
||||
@@ -126,57 +92,13 @@ function buildGraph(scenario, configs) {
|
||||
|
||||
for (const chainId of chainIds) {
|
||||
const chainConf = chains[chainId] || {};
|
||||
const deployedChain = scenario.graphMode === 'deployed' ? deployedChains[chainId] : null;
|
||||
const pub = publicChains[chainId] || {};
|
||||
const hubStable = chainConf.hubStable || pub.hubStable || 'USDC';
|
||||
const anchorStables = deployedChain
|
||||
? Object.keys(deployedChain.anchorAddresses || {})
|
||||
: (pub.anchorStables || [hubStable]);
|
||||
const anchorStables = pub.anchorStables || [hubStable];
|
||||
|
||||
for (const t of cwTokens) nodes.add(`${chainId}:${t}`);
|
||||
for (const a of anchorStables) nodes.add(`${chainId}:${a}`);
|
||||
|
||||
if (deployedChain) {
|
||||
const deployedPools = [
|
||||
...(deployedChain.pmmPools || []),
|
||||
...(deployedChain.pmmPoolsVolatile || []),
|
||||
...(deployedChain.gasPmmPools || []),
|
||||
];
|
||||
for (const pool of deployedPools) {
|
||||
const base = pool.base || pool.tokenIn;
|
||||
const quote = pool.quote || pool.tokenOut;
|
||||
if (!base || !quote) continue;
|
||||
nodes.add(`${chainId}:${base}`);
|
||||
nodes.add(`${chainId}:${quote}`);
|
||||
pmmEdges.push({
|
||||
type: 'pmm',
|
||||
chainId,
|
||||
base,
|
||||
quote,
|
||||
key: `${chainId}:${base}:${quote}`,
|
||||
k: pool.k,
|
||||
feeBps: pool.feeBps,
|
||||
inventoryTargetUnits: pool.inventoryTargetUnits,
|
||||
depthD0Units: pool.depthD0Units,
|
||||
role: pool.role || 'public_routing',
|
||||
publicRoutingEnabled: pool.publicRoutingEnabled !== false,
|
||||
poolAddress: pool.poolAddress,
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < anchorStables.length; i++) {
|
||||
for (let j = i + 1; j < anchorStables.length; j++) {
|
||||
ammEdges.push({
|
||||
type: 'amm',
|
||||
chainId,
|
||||
tokenA: anchorStables[i],
|
||||
tokenB: anchorStables[j],
|
||||
key: `${chainId}:amm:${anchorStables[i]}:${anchorStables[j]}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const poolChains = poolMatrix.chains || {};
|
||||
const pc = poolChains[chainId] || {};
|
||||
const poolsFirst = pc.poolsFirst || [];
|
||||
@@ -260,11 +182,11 @@ function buildPMMState(graph, configs) {
|
||||
for (const e of graph.pmmEdges) {
|
||||
const chainConf = graph.chains[e.chainId] || {};
|
||||
const isEur = CW_EUR.includes(e.base);
|
||||
const k = e.k != null ? Number(e.k) : (isEur ? (eurDefaults.k ?? 0.2) : (chainConf.k ?? defaultPmm.k ?? 0.1));
|
||||
const feeBps = e.feeBps != null ? Number(e.feeBps) : (isEur ? (eurDefaults.feeBps ?? 35) : (chainConf.feeBps ?? defaultPmm.feeBps ?? 25));
|
||||
const k = isEur ? (eurDefaults.k ?? 0.2) : (chainConf.k ?? defaultPmm.k ?? 0.1);
|
||||
const feeBps = isEur ? (eurDefaults.feeBps ?? 35) : (chainConf.feeBps ?? defaultPmm.feeBps ?? 25);
|
||||
const fee = feeBps / 10000;
|
||||
const invTarget = parseInt(e.inventoryTargetUnits || chainConf.inventoryTargetUnits || defaultPmm.inventoryTargetUnits || '1000000', 10);
|
||||
const d0 = parseInt(e.depthD0Units || chainConf.depthD0Units || defaultPmm.depthD0Units || '500000', 10);
|
||||
const invTarget = parseInt(chainConf.inventoryTargetUnits || defaultPmm.inventoryTargetUnits || '1000000', 10);
|
||||
const d0 = parseInt(chainConf.depthD0Units || defaultPmm.depthD0Units || '500000', 10);
|
||||
const eurP = params.eurUsd != null ? Number(params.eurUsd) : (params.eurPegMultiplier != null && params.eurPegMultiplier !== 1 ? Number(params.eurPegMultiplier) : 1.1);
|
||||
const P = isEur ? eurP : 1;
|
||||
|
||||
@@ -433,173 +355,6 @@ function getBridgeRho(scenario, fromChain, toChain) {
|
||||
return (blocks ?? 10) * rhoPerBlock / 10000;
|
||||
}
|
||||
|
||||
function getMatchedQuoteForBase(base) {
|
||||
if (base === 'cWUSDT') return 'USDT';
|
||||
if (base === 'cWUSDC') return 'USDC';
|
||||
return null;
|
||||
}
|
||||
|
||||
function getMicroTradeCandidates(graph, policy) {
|
||||
const tokenFilter = new Set(policy.tokens || []);
|
||||
const quoteFilter = new Set(policy.quoteTokens || []);
|
||||
let candidates = graph.pmmEdges.filter((e) => {
|
||||
if (tokenFilter.size > 0 && !tokenFilter.has(e.base)) return false;
|
||||
if (quoteFilter.size > 0 && !quoteFilter.has(e.quote)) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
if (policy.preferMatchedQuote) {
|
||||
const perChainBase = new Map();
|
||||
for (const edge of candidates) {
|
||||
const key = `${edge.chainId}:${edge.base}`;
|
||||
const preferredQuote = getMatchedQuoteForBase(edge.base);
|
||||
const existing = perChainBase.get(key);
|
||||
if (!existing) {
|
||||
perChainBase.set(key, edge);
|
||||
continue;
|
||||
}
|
||||
const score = edge.quote === preferredQuote ? 0 : 1;
|
||||
const existingScore = existing.quote === preferredQuote ? 0 : 1;
|
||||
if (
|
||||
score < existingScore
|
||||
|| (score === existingScore && edge.quote.localeCompare(existing.quote) < 0)
|
||||
) {
|
||||
perChainBase.set(key, edge);
|
||||
}
|
||||
}
|
||||
candidates = Array.from(perChainBase.values());
|
||||
}
|
||||
|
||||
candidates.sort((a, b) => (
|
||||
a.chainId.localeCompare(b.chainId)
|
||||
|| a.base.localeCompare(b.base)
|
||||
|| a.quote.localeCompare(b.quote)
|
||||
));
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function resolveMicroTradeDirection(policy, poolState, epochIndex, tradeIndex) {
|
||||
const mode = policy.mode || 'inventory_or_alternate';
|
||||
const bandFrac = Number(policy.inventoryBandFraction ?? 0.05);
|
||||
const lower = (1 - bandFrac) * poolState.I_T_star;
|
||||
const upper = (1 + bandFrac) * poolState.I_T_star;
|
||||
|
||||
if (mode === 'inventory' || mode === 'inventory_or_alternate') {
|
||||
if (poolState.I_T < lower) return 'sell_base';
|
||||
if (poolState.I_T > upper) return 'buy_base';
|
||||
if (mode === 'inventory') return null;
|
||||
}
|
||||
|
||||
if (mode === 'alternate' || mode === 'inventory_or_alternate') {
|
||||
return ((epochIndex + tradeIndex) % 2 === 0) ? 'buy_base' : 'sell_base';
|
||||
}
|
||||
if (mode === 'buy_base') return 'buy_base';
|
||||
if (mode === 'sell_base') return 'sell_base';
|
||||
return null;
|
||||
}
|
||||
|
||||
function runMicroTradeSupportStep(graph, state, scenario, epochIndex) {
|
||||
const policy = scenario.microTradePolicy || {};
|
||||
if (policy.enabled === false || Object.keys(policy).length === 0) {
|
||||
return {
|
||||
state,
|
||||
microTradeCount: 0,
|
||||
microTradeBuyCount: 0,
|
||||
microTradeSellCount: 0,
|
||||
microTradeVolumeTotal: 0,
|
||||
microTradeGasCostTotal: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const requestedTrades = Math.max(0, parseInt(policy.tradesPerEpoch ?? 0, 10));
|
||||
if (requestedTrades === 0) {
|
||||
return {
|
||||
state,
|
||||
microTradeCount: 0,
|
||||
microTradeBuyCount: 0,
|
||||
microTradeSellCount: 0,
|
||||
microTradeVolumeTotal: 0,
|
||||
microTradeGasCostTotal: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const candidates = getMicroTradeCandidates(graph, policy);
|
||||
if (candidates.length === 0) {
|
||||
return {
|
||||
state,
|
||||
microTradeCount: 0,
|
||||
microTradeBuyCount: 0,
|
||||
microTradeSellCount: 0,
|
||||
microTradeVolumeTotal: 0,
|
||||
microTradeGasCostTotal: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const gasCostPerTrade = Number(policy.gasCostPerTradeUnits ?? 0);
|
||||
const gasBudgetPerEpoch = Number(policy.gasBudgetPerEpochUnits ?? 0);
|
||||
let tradesAllowed = requestedTrades;
|
||||
if (gasCostPerTrade > 0 && gasBudgetPerEpoch > 0) {
|
||||
tradesAllowed = Math.min(tradesAllowed, Math.floor(gasBudgetPerEpoch / gasCostPerTrade));
|
||||
}
|
||||
if (tradesAllowed <= 0) {
|
||||
return {
|
||||
state,
|
||||
microTradeCount: 0,
|
||||
microTradeBuyCount: 0,
|
||||
microTradeSellCount: 0,
|
||||
microTradeVolumeTotal: 0,
|
||||
microTradeGasCostTotal: 0,
|
||||
};
|
||||
}
|
||||
|
||||
let curState = state;
|
||||
let microTradeCount = 0;
|
||||
let microTradeBuyCount = 0;
|
||||
let microTradeSellCount = 0;
|
||||
let microTradeVolumeTotal = 0;
|
||||
let microTradeGasCostTotal = 0;
|
||||
|
||||
for (let i = 0; i < tradesAllowed; i++) {
|
||||
const edge = candidates[(epochIndex * tradesAllowed + i) % candidates.length];
|
||||
const poolState = curState[edge.key];
|
||||
if (!poolState || poolState.I_T_star == null) continue;
|
||||
|
||||
const rawTradeSize = Number(policy.tradeSizeUnits ?? 0);
|
||||
const maxFractionOfTarget = Number(policy.maxFractionOfTarget ?? 0.01);
|
||||
const maxTradeSize = maxFractionOfTarget > 0 ? poolState.I_T_star * maxFractionOfTarget : rawTradeSize;
|
||||
const tradeSize = Math.max(0, Math.min(rawTradeSize, maxTradeSize || rawTradeSize));
|
||||
if (tradeSize <= 0) continue;
|
||||
|
||||
const direction = resolveMicroTradeDirection(policy, poolState, epochIndex, i);
|
||||
if (!direction) continue;
|
||||
|
||||
if (direction === 'sell_base') {
|
||||
const { newState } = pmmSellT(edge.key, tradeSize, curState);
|
||||
curState = newState;
|
||||
microTradeSellCount += 1;
|
||||
microTradeVolumeTotal += tradeSize;
|
||||
} else {
|
||||
const quoteSpend = tradeSize * (poolState.P || 1);
|
||||
const { outputT, newState } = pmmBuyT(edge.key, quoteSpend, curState);
|
||||
curState = newState;
|
||||
microTradeBuyCount += 1;
|
||||
microTradeVolumeTotal += outputT;
|
||||
}
|
||||
|
||||
microTradeCount += 1;
|
||||
microTradeGasCostTotal += gasCostPerTrade;
|
||||
}
|
||||
|
||||
return {
|
||||
state: curState,
|
||||
microTradeCount,
|
||||
microTradeBuyCount,
|
||||
microTradeSellCount,
|
||||
microTradeVolumeTotal,
|
||||
microTradeGasCostTotal,
|
||||
};
|
||||
}
|
||||
|
||||
function runBotStep(graph, state, scenario, configs) {
|
||||
const chains = graph.chains;
|
||||
let curState = state;
|
||||
@@ -764,7 +519,6 @@ function getCandidatePaths(chainId, fromToken, toToken, graph, state, probeSize,
|
||||
paths = paths.filter((p) => {
|
||||
const first = p[0];
|
||||
if (first.type === 'pmm' && publicEnabled === false) return false;
|
||||
if (first.type === 'pmm' && first.publicRoutingEnabled === false) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -871,11 +625,6 @@ function runEpoch(scenario, graph, state, configs, epochIndex) {
|
||||
let pmmVolume = 0;
|
||||
let churnSum = 0;
|
||||
const I_T_start = {};
|
||||
let microTradeCount = 0;
|
||||
let microTradeBuyCount = 0;
|
||||
let microTradeSellCount = 0;
|
||||
let microTradeVolumeTotal = 0;
|
||||
let microTradeGasCostTotal = 0;
|
||||
|
||||
for (const k of Object.keys(state)) {
|
||||
if (state[k].I_T_star != null) I_T_start[k] = state[k].I_T;
|
||||
@@ -933,15 +682,6 @@ function runEpoch(scenario, graph, state, configs, epochIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
const micro = runMicroTradeSupportStep(graph, curState, scenario, epochIndex);
|
||||
curState = micro.state;
|
||||
microTradeCount += micro.microTradeCount || 0;
|
||||
microTradeBuyCount += micro.microTradeBuyCount || 0;
|
||||
microTradeSellCount += micro.microTradeSellCount || 0;
|
||||
microTradeVolumeTotal += micro.microTradeVolumeTotal || 0;
|
||||
microTradeGasCostTotal += micro.microTradeGasCostTotal || 0;
|
||||
totalVolume += micro.microTradeVolumeTotal || 0;
|
||||
|
||||
const peakDeviationBpsPreArb = maxDeviationBpsOverPools(graph, curState);
|
||||
const worstPreArb = getWorstPoolDiagnostic(graph, curState);
|
||||
const { state: afterArb, arbVolumeTotal, arbProfitTotal, peakDeviationBps: peakDeviationBpsPostArb } = runArbStep(graph, curState, configs);
|
||||
@@ -962,7 +702,7 @@ function runEpoch(scenario, graph, state, configs, epochIndex) {
|
||||
const totalPathVol = Object.values(pathShares).reduce((a, b) => a + b, 0);
|
||||
if (totalPathVol > 0) {
|
||||
pmmVolume = Object.entries(pathShares)
|
||||
.filter(([pk]) => pk.split('|').some((part) => !part.includes(':amm:')))
|
||||
.filter(([pk]) => pk.includes(':cW'))
|
||||
.reduce((a, [, v]) => a + v, 0);
|
||||
}
|
||||
|
||||
@@ -977,11 +717,6 @@ function runEpoch(scenario, graph, state, configs, epochIndex) {
|
||||
interventionCostInject: interventionCostInject || 0,
|
||||
interventionCostWithdraw: interventionCostWithdraw || 0,
|
||||
interventionCostByChain: interventionCostByChain || {},
|
||||
microTradeCount,
|
||||
microTradeBuyCount,
|
||||
microTradeSellCount,
|
||||
microTradeVolumeTotal,
|
||||
microTradeGasCostTotal,
|
||||
peakDeviationBpsPreArb: peakDeviationBpsPreArb || 0,
|
||||
peakDeviationBpsPostArb: peakDeviationBpsPostArb || 0,
|
||||
peakDeviationBpsPostBot: peakDeviationBpsPostBot || 0,
|
||||
@@ -1018,11 +753,6 @@ function computeScorecard(scenario, scenarioName, graph, initialState, epochResu
|
||||
let peakDeviationBpsPreArb = 0;
|
||||
let peakDeviationBpsPostArb = 0;
|
||||
let peakDeviationBpsPostBot = 0;
|
||||
let microTradeCountTotal = 0;
|
||||
let microTradeBuyCountTotal = 0;
|
||||
let microTradeSellCountTotal = 0;
|
||||
let microTradeVolumeTotal = 0;
|
||||
let microTradeGasCostTotal = 0;
|
||||
|
||||
for (const r of epochResults) {
|
||||
totalVolume += r.totalVolume;
|
||||
@@ -1033,11 +763,6 @@ function computeScorecard(scenario, scenarioName, graph, initialState, epochResu
|
||||
arbProfitTotal += r.arbProfitTotal || 0;
|
||||
interventionCostInjectTotal += r.interventionCostInject || 0;
|
||||
interventionCostWithdrawTotal += r.interventionCostWithdraw || 0;
|
||||
microTradeCountTotal += r.microTradeCount || 0;
|
||||
microTradeBuyCountTotal += r.microTradeBuyCount || 0;
|
||||
microTradeSellCountTotal += r.microTradeSellCount || 0;
|
||||
microTradeVolumeTotal += r.microTradeVolumeTotal || 0;
|
||||
microTradeGasCostTotal += r.microTradeGasCostTotal || 0;
|
||||
for (const [chainId, v] of Object.entries(r.interventionCostByChain || {})) {
|
||||
if (!interventionCostByChain[chainId]) interventionCostByChain[chainId] = { inject: 0, withdraw: 0 };
|
||||
interventionCostByChain[chainId].inject += v.inject || 0;
|
||||
@@ -1102,7 +827,7 @@ function computeScorecard(scenario, scenarioName, graph, initialState, epochResu
|
||||
|
||||
return {
|
||||
scenario: scenario.scenario || scenarioName,
|
||||
runId: `run-${scenario.seed != null ? Number(scenario.seed) : hashScenarioName(scenarioName)}`,
|
||||
runId: `run-${Date.now()}`,
|
||||
capture_mean: Math.min(1, Math.max(0, captureMean)),
|
||||
capture_p95: Math.min(1, captureMean * 1.2),
|
||||
churn_mean: churnMeanNorm,
|
||||
@@ -1113,11 +838,6 @@ function computeScorecard(scenario, scenarioName, graph, initialState, epochResu
|
||||
intervention_cost_withdraw_total: Math.round(interventionCostWithdrawTotal),
|
||||
intervention_cost_by_chain: interventionCostByChain,
|
||||
intervention_cost_per_1M_volume: Math.round(interventionPer1M * 100) / 100,
|
||||
micro_trade_count: Math.round(microTradeCountTotal),
|
||||
micro_trade_buy_count: Math.round(microTradeBuyCountTotal),
|
||||
micro_trade_sell_count: Math.round(microTradeSellCountTotal),
|
||||
micro_trade_volume_total: Math.round(microTradeVolumeTotal),
|
||||
micro_trade_gas_cost_total: Math.round(microTradeGasCostTotal * 100) / 100,
|
||||
peak_deviation_bps: Math.round(Number.isFinite(peakDeviationBpsPostArb) ? peakDeviationBpsPostArb : 0),
|
||||
peak_deviation_bps_pre_arb: Math.round(peakDeviationBpsPreArb),
|
||||
peak_deviation_bps_post_arb: Math.round(peakDeviationBpsPostArb),
|
||||
@@ -1131,334 +851,9 @@ function computeScorecard(scenario, scenarioName, graph, initialState, epochResu
|
||||
};
|
||||
}
|
||||
|
||||
function mergeCapitalConfig(policy, scenarioConfig = {}, overrides = {}) {
|
||||
const defaults = policy.defaults || {};
|
||||
const risk = { ...(policy.risk || {}), ...(scenarioConfig.risk || {}), ...(overrides.risk || {}) };
|
||||
const treasury = { ...(policy.treasury || {}), ...(scenarioConfig.treasury || {}), ...(overrides.treasury || {}) };
|
||||
const volatility = { ...(policy.volatilityProcess || {}), ...(scenarioConfig.volatilityProcess || {}), ...(overrides.volatilityProcess || {}) };
|
||||
const peg = { ...(policy.pegDynamics || {}), ...(scenarioConfig.pegDynamics || {}), ...(overrides.pegDynamics || {}) };
|
||||
const stress = { ...(scenarioConfig.stress || {}), ...(overrides.stress || {}) };
|
||||
return {
|
||||
enabled: scenarioConfig.enabled === true || overrides.enabled === true,
|
||||
paths: Number(overrides.paths ?? scenarioConfig.paths ?? defaults.paths ?? 1000),
|
||||
epochs: Number(overrides.epochs ?? scenarioConfig.epochs ?? defaults.epochs ?? 365),
|
||||
seed: Number(overrides.seed ?? scenarioConfig.seed ?? defaults.seed ?? 138001),
|
||||
initialCapital: Number(overrides.initialCapital ?? scenarioConfig.initialCapital ?? defaults.initialCapital ?? 1000000),
|
||||
alpha: Number(overrides.alpha ?? scenarioConfig.alpha ?? defaults.alpha ?? 0.7),
|
||||
leverage: Number(overrides.leverage ?? scenarioConfig.leverage ?? defaults.leverage ?? 2),
|
||||
spreadBps: Number(overrides.spreadBps ?? scenarioConfig.spreadBps ?? defaults.spreadBps ?? 35),
|
||||
volumeEfficiency: Number(overrides.volumeEfficiency ?? scenarioConfig.volumeEfficiency ?? defaults.volumeEfficiency ?? 2),
|
||||
pmmK: Number(overrides.pmmK ?? scenarioConfig.pmmK ?? defaults.pmmK ?? 0.1),
|
||||
liquidityTargetUnits: Number(overrides.liquidityTargetUnits ?? scenarioConfig.liquidityTargetUnits ?? defaults.liquidityTargetUnits ?? 1000000),
|
||||
treasury,
|
||||
volatility,
|
||||
peg,
|
||||
risk,
|
||||
stress,
|
||||
};
|
||||
}
|
||||
|
||||
function getStressForEpoch(stress, epoch, initialCapital) {
|
||||
const out = {
|
||||
externalAssetReturn: 0,
|
||||
volatilityAdd: 0,
|
||||
redemptionFraction: 0,
|
||||
imbalanceAdd: 0,
|
||||
liquidationLossFraction: 0,
|
||||
};
|
||||
const events = Array.isArray(stress.events) ? stress.events : [];
|
||||
for (const ev of events) {
|
||||
const at = Number(ev.epoch ?? 0);
|
||||
const duration = Math.max(1, Number(ev.durationEpochs ?? 1));
|
||||
if (epoch < at || epoch >= at + duration) continue;
|
||||
if (ev.type === 'crash') out.externalAssetReturn += Number(ev.externalAssetReturn ?? -0.4) / duration;
|
||||
if (ev.type === 'high_volatility') out.volatilityAdd += Number(ev.sigmaAdd ?? 0.05);
|
||||
if (ev.type === 'bank_run') out.redemptionFraction += Number(ev.redemptionFraction ?? 0.1) / duration;
|
||||
if (ev.type === 'bridge_shock') out.imbalanceAdd += Number(ev.imbalanceAdd ?? 0.05);
|
||||
if (ev.type === 'liquidation_loss') out.liquidationLossFraction += Number(ev.lossFraction ?? 0.05);
|
||||
}
|
||||
if (stress.preset === 'crash_40pct_external_asset' && epoch === Number(stress.epoch ?? 24)) {
|
||||
out.externalAssetReturn -= 0.4;
|
||||
}
|
||||
if (stress.preset === 'high_vol_sigma_spike') {
|
||||
const at = Number(stress.epoch ?? 24);
|
||||
const duration = Math.max(1, Number(stress.durationEpochs ?? 24));
|
||||
if (epoch >= at && epoch < at + duration) out.volatilityAdd += Number(stress.sigmaAdd ?? 0.08);
|
||||
}
|
||||
if (stress.preset === 'bank_run_redemption_spike') {
|
||||
const at = Number(stress.epoch ?? 24);
|
||||
const duration = Math.max(1, Number(stress.durationEpochs ?? 12));
|
||||
if (epoch >= at && epoch < at + duration) out.redemptionFraction += Number(stress.redemptionFraction ?? 0.25) / duration;
|
||||
}
|
||||
if (stress.preset === 'bridge_shock') {
|
||||
const at = Number(stress.epoch ?? 24);
|
||||
const duration = Math.max(1, Number(stress.durationEpochs ?? 24));
|
||||
if (epoch >= at && epoch < at + duration) out.imbalanceAdd += Number(stress.imbalanceAdd ?? 0.05);
|
||||
}
|
||||
if (initialCapital <= 0) out.redemptionFraction = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
function simulateCapitalPath(config, baselineScorecard) {
|
||||
const risk = config.risk || {};
|
||||
const treasury = config.treasury || {};
|
||||
const vol = config.volatility || {};
|
||||
const peg = config.peg || {};
|
||||
const spreadRate = config.spreadBps / 10000;
|
||||
const minExternalLiquidityPct = Number(risk.minExternalLiquidityPct ?? 0.2);
|
||||
const maxLtv = Number(risk.maxLtvBps ?? 6500) / 10000;
|
||||
const hardMaxLtv = Number(risk.hardMaxLtvBps ?? 7500) / 10000;
|
||||
const hardMaxLeverage = Number(risk.hardMaxLeverage ?? 4);
|
||||
const liquidationLtv = Math.min(hardMaxLtv, Number(risk.liquidationLtvBps ?? 8000) / 10000);
|
||||
const sigmaCrit = Number(risk.sigmaCrit ?? 0.08);
|
||||
const throttleLeverageMultiplier = Number(risk.throttleLeverageMultiplier ?? 0.7);
|
||||
const throttleAlphaMultiplier = Number(risk.throttleAlphaMultiplier ?? 0.9);
|
||||
const pegCircuitBreakerBps = Number(risk.pegCircuitBreakerBps ?? 200);
|
||||
const volDrag = Number(treasury.volatilityDragLambda ?? 0.35);
|
||||
const yieldRate = Number(treasury.yieldRatePerEpoch ?? 0.00035);
|
||||
const mmScale = Number(treasury.marketMakingScale ?? 0.04);
|
||||
const liquidationLossFraction = Number(risk.liquidationLossFraction ?? 0.08);
|
||||
const redemptionFeeBps = Number(risk.bankRunRedemptionFeeBps ?? 100);
|
||||
|
||||
let T = config.initialCapital;
|
||||
let highWater = T;
|
||||
let maxDrawdown = 0;
|
||||
let sigma = Number(vol.sigma0 ?? vol.sigmaBar ?? 0.03);
|
||||
let P = Number(peg.p0 ?? 1);
|
||||
let liquidated = false;
|
||||
let pegDeviationEpochs = 0;
|
||||
let externalLiquidityFloorViolations = 0;
|
||||
let volatilityThrottleEvents = 0;
|
||||
let spreadAdjustmentEvents = 0;
|
||||
let liquidationEvents = 0;
|
||||
let totalPnl = 0;
|
||||
const baselineInterventionDrag = Math.min(0.002, Number(baselineScorecard.intervention_cost_per_1M_volume || 0) / 1e9);
|
||||
const baselinePegPressure = Math.min(0.1, Number(baselineScorecard.peak_deviation_bps || 0) / 10000);
|
||||
|
||||
for (let e = 0; e < config.epochs; e++) {
|
||||
if (liquidated || T <= 0) break;
|
||||
const stress = getStressForEpoch(config.stress || {}, e, T);
|
||||
const z = normalSample();
|
||||
sigma = Math.max(0, sigma + Number(vol.kappa ?? 0.08) * (Number(vol.sigmaBar ?? 0.03) - sigma) + Number(vol.eta ?? 0.01) * z + stress.volatilityAdd);
|
||||
|
||||
let effectiveAlpha = config.alpha;
|
||||
let effectiveLeverage = config.leverage;
|
||||
let effectiveSpreadBps = config.spreadBps;
|
||||
if (sigma > sigmaCrit) {
|
||||
effectiveAlpha *= throttleAlphaMultiplier;
|
||||
effectiveLeverage = Math.max(1, effectiveLeverage * throttleLeverageMultiplier);
|
||||
effectiveSpreadBps = Math.min(Number(risk.maxSpreadBps ?? 100), Math.max(effectiveSpreadBps, Number(risk.throttleSpreadBps ?? 50)));
|
||||
volatilityThrottleEvents += 1;
|
||||
spreadAdjustmentEvents += 1;
|
||||
}
|
||||
if (effectiveLeverage > hardMaxLeverage) {
|
||||
liquidated = true;
|
||||
liquidationEvents += 1;
|
||||
T = Math.max(0, T * (1 - liquidationLossFraction));
|
||||
maxDrawdown = Math.max(maxDrawdown, highWater > 0 ? (highWater - T) / highWater : 0);
|
||||
break;
|
||||
}
|
||||
|
||||
const externalLiquidity = (1 - effectiveAlpha) * T;
|
||||
if (externalLiquidity < minExternalLiquidityPct * T) {
|
||||
externalLiquidityFloorViolations += 1;
|
||||
effectiveAlpha = Math.min(effectiveAlpha, 1 - minExternalLiquidityPct);
|
||||
spreadAdjustmentEvents += 1;
|
||||
}
|
||||
|
||||
const grossLeveredExposure = effectiveLeverage * effectiveAlpha * T;
|
||||
const debt = Math.max(0, (effectiveLeverage - 1) * effectiveAlpha * T);
|
||||
const collateralShock = 1 + stress.externalAssetReturn - sigma * Number(risk.collateralVolatilityHaircut ?? 0.5);
|
||||
const collateral = Math.max(0, grossLeveredExposure * collateralShock);
|
||||
const ltv = collateral > 0 ? debt / collateral : 1;
|
||||
if (ltv > liquidationLtv || ltv > maxLtv * Number(risk.maxLtvBreachMultiplier ?? 1.2)) {
|
||||
liquidated = true;
|
||||
liquidationEvents += 1;
|
||||
T = Math.max(0, T * (1 - liquidationLossFraction - stress.liquidationLossFraction));
|
||||
maxDrawdown = Math.max(maxDrawdown, highWater > 0 ? (highWater - T) / highWater : 0);
|
||||
break;
|
||||
}
|
||||
|
||||
const imbalance = Number(peg.imbalanceStd ?? 0.01) * normalSample() + stress.imbalanceAdd + baselinePegPressure;
|
||||
const arbFlow = Number(peg.arbLiquidityCoefficient ?? 0.65) * (P - 1);
|
||||
P = P + Number(peg.beta ?? 0.25) * imbalance - arbFlow;
|
||||
const pegDeviationBps = Math.abs(P - 1) * 10000;
|
||||
if (pegDeviationBps > pegCircuitBreakerBps) {
|
||||
pegDeviationEpochs += 1;
|
||||
effectiveSpreadBps = Math.min(Number(risk.maxSpreadBps ?? 100), Math.max(effectiveSpreadBps, Number(risk.circuitBreakerSpreadBps ?? 100)));
|
||||
spreadAdjustmentEvents += 1;
|
||||
}
|
||||
|
||||
const sqrtAllocation = Math.sqrt(Math.max(0, effectiveAlpha * (1 - effectiveAlpha)));
|
||||
const yieldComponent = yieldRate * effectiveAlpha * effectiveLeverage;
|
||||
const mmComponent = (effectiveSpreadBps / 10000) * config.volumeEfficiency * sqrtAllocation * sigma * mmScale;
|
||||
const volatilityDrag = volDrag * sigma * Math.max(0.25, effectiveLeverage - 0.5) / 365;
|
||||
const redemptionDrag = stress.redemptionFraction * (1 - redemptionFeeBps / 10000);
|
||||
const pnlRate = yieldComponent + mmComponent - volatilityDrag - baselineInterventionDrag - redemptionDrag;
|
||||
const pnl = T * pnlRate;
|
||||
totalPnl += pnl;
|
||||
T = Math.max(0, T + pnl);
|
||||
highWater = Math.max(highWater, T);
|
||||
maxDrawdown = Math.max(maxDrawdown, highWater > 0 ? (highWater - T) / highWater : 0);
|
||||
}
|
||||
|
||||
return {
|
||||
endingCapital: T,
|
||||
roi: config.initialCapital > 0 ? (T - config.initialCapital) / config.initialCapital : 0,
|
||||
totalPnl,
|
||||
maxDrawdown,
|
||||
liquidated,
|
||||
liquidationEvents,
|
||||
pegDeviationEpochs,
|
||||
externalLiquidityFloorViolations,
|
||||
volatilityThrottleEvents,
|
||||
spreadAdjustmentEvents,
|
||||
};
|
||||
}
|
||||
|
||||
function runCapitalMonteCarlo(scenario, scenarioName, configs, baselineScorecard, overrides = {}) {
|
||||
const capitalConfig = mergeCapitalConfig(configs.capitalEfficiencyPolicy || {}, scenario.capitalEfficiency || {}, overrides);
|
||||
if (!capitalConfig.enabled) return null;
|
||||
const baseSeed = capitalConfig.seed != null ? Number(capitalConfig.seed) : hashScenarioName(`${scenarioName}:capital`);
|
||||
const rois = [];
|
||||
const pnls = [];
|
||||
const drawdowns = [];
|
||||
let liquidationCount = 0;
|
||||
let pegDeviationEpochs = 0;
|
||||
let externalLiquidityFloorViolations = 0;
|
||||
let volatilityThrottleEvents = 0;
|
||||
let spreadAdjustmentEvents = 0;
|
||||
|
||||
for (let p = 0; p < capitalConfig.paths; p++) {
|
||||
seedRng((baseSeed + Math.imul(p + 1, 2654435761)) >>> 0);
|
||||
const result = simulateCapitalPath(capitalConfig, baselineScorecard);
|
||||
rois.push(result.roi);
|
||||
pnls.push(result.totalPnl);
|
||||
drawdowns.push(result.maxDrawdown);
|
||||
if (result.liquidated) liquidationCount += 1;
|
||||
pegDeviationEpochs += result.pegDeviationEpochs;
|
||||
externalLiquidityFloorViolations += result.externalLiquidityFloorViolations;
|
||||
volatilityThrottleEvents += result.volatilityThrottleEvents;
|
||||
spreadAdjustmentEvents += result.spreadAdjustmentEvents;
|
||||
}
|
||||
|
||||
const totalEpochs = Math.max(1, capitalConfig.paths * capitalConfig.epochs);
|
||||
return {
|
||||
capital_efficiency_enabled: true,
|
||||
capital_efficiency_paths: capitalConfig.paths,
|
||||
capital_efficiency_epochs: capitalConfig.epochs,
|
||||
initial_capital: Math.round(capitalConfig.initialCapital),
|
||||
alpha: round4(capitalConfig.alpha),
|
||||
leverage: round4(capitalConfig.leverage),
|
||||
spread_bps: round2(capitalConfig.spreadBps),
|
||||
volume_efficiency: round4(capitalConfig.volumeEfficiency),
|
||||
pmm_k: round4(capitalConfig.pmmK),
|
||||
liquidity_target_units: Math.round(capitalConfig.liquidityTargetUnits),
|
||||
roi_mean: round4(rois.reduce((a, b) => a + b, 0) / Math.max(1, rois.length)),
|
||||
roi_p05: round4(percentile(rois, 5)),
|
||||
roi_p95: round4(percentile(rois, 95)),
|
||||
pnl_distribution: {
|
||||
p05: round2(percentile(pnls, 5)),
|
||||
p50: round2(percentile(pnls, 50)),
|
||||
p95: round2(percentile(pnls, 95)),
|
||||
},
|
||||
max_drawdown_p95: round4(percentile(drawdowns, 95)),
|
||||
liquidation_probability: round4(liquidationCount / Math.max(1, capitalConfig.paths)),
|
||||
peg_deviation_frequency: round4(pegDeviationEpochs / totalEpochs),
|
||||
external_liquidity_floor_violations: externalLiquidityFloorViolations,
|
||||
volatility_throttle_events: volatilityThrottleEvents,
|
||||
spread_adjustment_events: spreadAdjustmentEvents,
|
||||
};
|
||||
}
|
||||
|
||||
function passesCapitalGates(metrics, policy, overrides = {}) {
|
||||
const gates = { ...(policy.gates || {}), ...(overrides.gates || {}) };
|
||||
return (
|
||||
metrics.leverage <= Number((policy.risk || {}).hardMaxLeverage ?? 4)
|
||||
&& metrics.leverage <= Number(gates.maxDeployableLeverage ?? (policy.risk || {}).hardMaxLeverage ?? 4)
|
||||
&& metrics.liquidation_probability <= Number(gates.maxLiquidationProbability ?? 0.02)
|
||||
&& metrics.max_drawdown_p95 <= Number(gates.maxDrawdownP95 ?? 0.25)
|
||||
&& metrics.peg_deviation_frequency <= Number(gates.maxPegDeviationFrequency ?? 0.05)
|
||||
&& metrics.external_liquidity_floor_violations <= Number(gates.maxExternalLiquidityFloorViolations ?? 0)
|
||||
);
|
||||
}
|
||||
|
||||
function buildOptimizerGrid(policy, scenarioConfig) {
|
||||
const opt = scenarioConfig.optimizer || {};
|
||||
const grid = opt.grid || {};
|
||||
const alphas = grid.alpha || [0.65, 0.7, 0.75, 0.8, 0.85];
|
||||
const leverages = grid.leverage || [1, 2, 2.5, 3];
|
||||
const spreads = grid.spreadBps || [30, 40, 50];
|
||||
const pmmKs = grid.pmmK || [0.1, 0.15, 0.2];
|
||||
const liquidityTargets = grid.liquidityTargetUnits || [500000, 1000000, 1500000];
|
||||
const out = [];
|
||||
for (const alpha of alphas) {
|
||||
for (const leverage of leverages) {
|
||||
for (const spreadBps of spreads) {
|
||||
for (const pmmK of pmmKs) {
|
||||
for (const liquidityTargetUnits of liquidityTargets) {
|
||||
out.push({ alpha, leverage, spreadBps, pmmK, liquidityTargetUnits });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const limit = Number(opt.maxCandidates ?? (policy.optimizer || {}).maxCandidates ?? 250);
|
||||
return out.slice(0, limit);
|
||||
}
|
||||
|
||||
function capitalBand(initialCapital) {
|
||||
if (initialCapital < 500000) return "sub_500k";
|
||||
if (initialCapital < 2000000) return "500k_2m";
|
||||
if (initialCapital < 10000000) return "2m_10m";
|
||||
return "10m_plus";
|
||||
}
|
||||
|
||||
function runOptimizer(scenario, scenarioName, configs, baselineScorecard) {
|
||||
const policy = configs.capitalEfficiencyPolicy || {};
|
||||
const scenarioConfig = scenario.capitalEfficiency || {};
|
||||
const grid = buildOptimizerGrid(policy, scenarioConfig);
|
||||
const candidates = [];
|
||||
for (let i = 0; i < grid.length; i++) {
|
||||
const overrides = {
|
||||
enabled: true,
|
||||
...grid[i],
|
||||
paths: Number((scenarioConfig.optimizer || {}).paths ?? (policy.optimizer || {}).paths ?? 250),
|
||||
seed: (Number(scenarioConfig.seed ?? (policy.defaults || {}).seed ?? 138001) + i * 9973) >>> 0,
|
||||
};
|
||||
const metrics = runCapitalMonteCarlo(scenario, scenarioName, configs, baselineScorecard, overrides);
|
||||
const deployable = passesCapitalGates(metrics, policy, scenarioConfig.optimizer || {});
|
||||
candidates.push({
|
||||
rank_score: round4(metrics.roi_mean - metrics.liquidation_probability * 2 - metrics.max_drawdown_p95 * 0.5 - metrics.peg_deviation_frequency),
|
||||
deployable,
|
||||
...metrics,
|
||||
});
|
||||
}
|
||||
candidates.sort((a, b) => {
|
||||
if (a.deployable !== b.deployable) return a.deployable ? -1 : 1;
|
||||
return b.rank_score - a.rank_score;
|
||||
});
|
||||
const top = candidates.slice(0, Number((scenarioConfig.optimizer || {}).topN ?? 10));
|
||||
return {
|
||||
scenario: `${scenario.scenario || scenarioName}:optimizer`,
|
||||
runId: `optimizer-${scenarioConfig.seed != null ? Number(scenarioConfig.seed) : hashScenarioName(`${scenarioName}:optimizer`)}`,
|
||||
optimizer_enabled: true,
|
||||
capital_band: capitalBand(Number((scenarioConfig.initialCapital ?? (policy.defaults || {}).initialCapital) || 0)),
|
||||
candidates_evaluated: candidates.length,
|
||||
deployable_candidates: candidates.filter((c) => c.deployable).length,
|
||||
top_candidates: top,
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
const idx = process.argv.indexOf('--scenario');
|
||||
const optimizerMode = process.argv.includes('--optimizer') || process.argv.includes('--optimize');
|
||||
const positional = process.argv.slice(2).filter((arg, i, args) => {
|
||||
if (arg.startsWith('--')) return false;
|
||||
const prev = args[i - 1];
|
||||
return prev !== '--scenario';
|
||||
});
|
||||
const scenarioName = idx >= 0 && process.argv[idx + 1] ? process.argv[idx + 1] : positional[0] || 'hub_only_11';
|
||||
const scenarioName = idx >= 0 && process.argv[idx + 1] ? process.argv[idx + 1] : process.argv[2] || 'hub_only_11';
|
||||
const scenarioPath = path.join(SCENARIOS_DIR, `${scenarioName}.json`);
|
||||
if (!fs.existsSync(scenarioPath)) {
|
||||
process.stderr.write(`Scenario not found: ${scenarioPath}\n`);
|
||||
@@ -1482,13 +877,7 @@ function main() {
|
||||
}
|
||||
|
||||
const scorecard = computeScorecard(scenario, scenarioName, graph, initialState, epochResults);
|
||||
const capitalMetrics = runCapitalMonteCarlo(scenario, scenarioName, configs, scorecard);
|
||||
const fullScorecard = capitalMetrics ? { ...scorecard, ...capitalMetrics } : scorecard;
|
||||
if (optimizerMode || scenario.capitalEfficiency?.optimizer?.enabled === true) {
|
||||
console.log(JSON.stringify(runOptimizer(scenario, scenarioName, configs, fullScorecard), null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(JSON.stringify(fullScorecard, null, 2));
|
||||
console.log(JSON.stringify(scorecard, null, 2));
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* CI-style validation for the simulation-only capital efficiency overlay.
|
||||
*
|
||||
* Checks:
|
||||
* - JSON configs and scenario files parse.
|
||||
* - Baseline scenario stays capital-overlay free.
|
||||
* - Capital stress scenarios emit required risk metrics.
|
||||
* - Deterministic seeds produce byte-identical scorecards.
|
||||
* - Optimizer never marks leverage above policy gates as deployable.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const CONFIG_DIR = path.join(ROOT, 'config');
|
||||
const SCENARIOS_DIR = path.join(CONFIG_DIR, 'scenarios');
|
||||
const RUNNER = path.join(ROOT, 'scripts', 'run-scenario.cjs');
|
||||
|
||||
const REQUIRED_CAPITAL_FIELDS = [
|
||||
'roi_mean',
|
||||
'roi_p05',
|
||||
'roi_p95',
|
||||
'pnl_distribution',
|
||||
'max_drawdown_p95',
|
||||
'liquidation_probability',
|
||||
'peg_deviation_frequency',
|
||||
'external_liquidity_floor_violations',
|
||||
'volatility_throttle_events',
|
||||
'spread_adjustment_events',
|
||||
];
|
||||
|
||||
const STRESS_SCENARIOS = [
|
||||
'crash_40pct_external_asset',
|
||||
'high_vol_sigma_spike',
|
||||
'bank_run_redemption_spike',
|
||||
];
|
||||
|
||||
function readJson(file) {
|
||||
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
}
|
||||
|
||||
function runScenario(name, extraArgs = []) {
|
||||
const out = execFileSync(process.execPath, [RUNNER, ...extraArgs, name], {
|
||||
cwd: ROOT,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
return { raw: out, json: JSON.parse(out) };
|
||||
}
|
||||
|
||||
function fail(msg) {
|
||||
process.stderr.write(`[capital-efficiency] ${msg}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function assert(cond, msg) {
|
||||
if (!cond) fail(msg);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const files = [
|
||||
path.join(CONFIG_DIR, 'scenario-schema.json'),
|
||||
path.join(CONFIG_DIR, 'scorecard-schema.json'),
|
||||
path.join(CONFIG_DIR, 'capital-efficiency-policy.json'),
|
||||
...fs.readdirSync(SCENARIOS_DIR)
|
||||
.filter((f) => f.endsWith('.json'))
|
||||
.map((f) => path.join(SCENARIOS_DIR, f)),
|
||||
];
|
||||
for (const file of files) readJson(file);
|
||||
|
||||
const policy = readJson(path.join(CONFIG_DIR, 'capital-efficiency-policy.json'));
|
||||
const maxDeployableLeverage = Number(policy.gates?.maxDeployableLeverage ?? policy.risk?.hardMaxLeverage ?? 4);
|
||||
const gates = policy.gates || {};
|
||||
const maxLiquidationProbability = Number(gates.maxLiquidationProbability ?? 0.02);
|
||||
const maxDrawdownP95 = Number(gates.maxDrawdownP95 ?? 0.25);
|
||||
const maxPegDeviationFrequency = Number(gates.maxPegDeviationFrequency ?? 0.05);
|
||||
const maxExternalLiquidityFloorViolations = Number(gates.maxExternalLiquidityFloorViolations ?? 0);
|
||||
const guard = policy.liveExecutionGuard || {};
|
||||
|
||||
assert(guard.status === 'simulation_only', 'liveExecutionGuard.status must remain simulation_only until external approvals exist');
|
||||
assert(guard.riskDashboardEvidence, 'risk dashboard evidence path is required');
|
||||
assert(guard.operatorRunbookEvidence, 'operator runbook evidence path is required');
|
||||
assert(guard.treasuryLiquidityCommitmentEvidence, 'treasury liquidity commitment evidence path is required');
|
||||
for (const evidencePath of [
|
||||
guard.riskDashboardEvidence,
|
||||
guard.operatorRunbookEvidence,
|
||||
guard.treasuryLiquidityCommitmentEvidence,
|
||||
]) {
|
||||
assert(fs.existsSync(path.join(ROOT, evidencePath)), `evidence path missing: ${evidencePath}`);
|
||||
}
|
||||
|
||||
const baseline = runScenario('hub_only_11').json;
|
||||
assert(baseline.capital_efficiency_enabled !== true, 'hub_only_11 unexpectedly enabled capital overlay');
|
||||
|
||||
for (const scenario of [
|
||||
'chain138_deployed_capital_efficiency',
|
||||
...STRESS_SCENARIOS,
|
||||
]) {
|
||||
const scorecard = runScenario(scenario).json;
|
||||
assert(scorecard.capital_efficiency_enabled === true, `${scenario} did not emit capital overlay`);
|
||||
for (const field of REQUIRED_CAPITAL_FIELDS) {
|
||||
assert(Object.prototype.hasOwnProperty.call(scorecard, field), `${scenario} missing ${field}`);
|
||||
}
|
||||
if (STRESS_SCENARIOS.includes(scenario)) {
|
||||
assert(scorecard.liquidation_probability <= maxLiquidationProbability, `${scenario} liquidation probability exceeds gate`);
|
||||
assert(scorecard.max_drawdown_p95 <= maxDrawdownP95, `${scenario} p95 drawdown exceeds gate`);
|
||||
assert(scorecard.peg_deviation_frequency <= maxPegDeviationFrequency, `${scenario} peg deviation frequency exceeds gate`);
|
||||
assert(scorecard.external_liquidity_floor_violations <= maxExternalLiquidityFloorViolations, `${scenario} external liquidity floor violations exceed gate`);
|
||||
}
|
||||
}
|
||||
|
||||
const a = runScenario('crash_40pct_external_asset').raw;
|
||||
const b = runScenario('crash_40pct_external_asset').raw;
|
||||
assert(a === b, 'deterministic seed repeat check failed for crash_40pct_external_asset');
|
||||
|
||||
const optimizer = runScenario('leverage_sweep_1x_to_4x', ['--optimizer']).json;
|
||||
assert(optimizer.optimizer_enabled === true, 'optimizer did not emit optimizer payload');
|
||||
for (const candidate of optimizer.top_candidates || []) {
|
||||
if (candidate.deployable) {
|
||||
assert(candidate.leverage <= maxDeployableLeverage, `deployable candidate exceeds maxDeployableLeverage: ${candidate.leverage}`);
|
||||
}
|
||||
}
|
||||
|
||||
process.stdout.write('capital efficiency validation OK\n');
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -5,11 +5,8 @@
|
||||
*
|
||||
* Rules:
|
||||
* - If bridgeAvailable === true on a chain, cwTokens must include at least cWUSDT and cWUSDC (phase 1).
|
||||
* - For each pmmPool / pmmPoolsVolatile[]: role in {defense, public_routing, truu_routing},
|
||||
* feeBps and k present, base/quote (or tokenIn/tokenOut) exist in cwTokens or anchorAddresses.
|
||||
* TRUU must be listed under anchorAddresses when used as quote (e.g. mainnet chain 1).
|
||||
* - Any row marked live/routingVisible/publicRoutingEnabled must use a native protocol contract,
|
||||
* not a placeholder scaffold or aggregator-only/non-native lane.
|
||||
* - For each pmmPool: role in {defense, public_routing}, feeBps and k present,
|
||||
* base/quote (or tokenIn/tokenOut) exist in cwTokens or anchorAddresses.
|
||||
*
|
||||
* Exit code: 0 if valid, 1 if invalid (and prints errors to stderr).
|
||||
*/
|
||||
@@ -18,113 +15,27 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const CONFIG_DIR = path.join(__dirname, '..', 'config');
|
||||
let DEPLOYMENT_STATUS_PATH = path.join(CONFIG_DIR, 'deployment-status.json');
|
||||
const DEPLOYMENT_STATUS_PATH = path.join(CONFIG_DIR, 'deployment-status.json');
|
||||
|
||||
const PHASE1_CW = ['cWUSDT', 'cWUSDC'];
|
||||
const VALID_ROLES = ['defense', 'public_routing', 'truu_routing'];
|
||||
const VALID_REFERENCE_PROTOCOLS = ['uniswap_v3', 'balancer', 'curve', '1inch'];
|
||||
const VALID_NATIVE_REFERENCE_PROTOCOLS = ['uniswap_v3', 'balancer', 'curve'];
|
||||
const HOME_CHAIN_CANONICAL_PREFIXES = ['c'];
|
||||
|
||||
function looksPlaceholderAddress(address) {
|
||||
if (!address || typeof address !== 'string') return false;
|
||||
const normalized = address.toLowerCase();
|
||||
if (!/^0x[0-9a-f]{40}$/.test(normalized)) return false;
|
||||
if (normalized === '0x0000000000000000000000000000000000000000') return true;
|
||||
const body = normalized.slice(2);
|
||||
const placeholderPrefixes = ['d0', '71', 'ba', 'c7'];
|
||||
const zeroCount = body.split('0').length - 1;
|
||||
return placeholderPrefixes.some((prefix) => body.startsWith(prefix)) && zeroCount >= 24;
|
||||
}
|
||||
|
||||
function isLiveRow(row) {
|
||||
return row?.live === true || row?.routingVisible === true || row?.publicRoutingEnabled === true;
|
||||
}
|
||||
const VALID_ROLES = ['defense', 'public_routing'];
|
||||
|
||||
function loadJson(p) {
|
||||
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
||||
}
|
||||
|
||||
function validatePoolEntries(chainId, pools, listLabel, knownTokens, errors) {
|
||||
for (let i = 0; i < pools.length; i++) {
|
||||
const pool = pools[i];
|
||||
const base = pool.base ?? pool.tokenIn;
|
||||
const quote = pool.quote ?? pool.tokenOut;
|
||||
|
||||
if (!VALID_ROLES.includes(pool.role)) {
|
||||
errors.push(`Chain ${chainId} ${listLabel}[${i}]: role must be one of ${VALID_ROLES.join(', ')}`);
|
||||
}
|
||||
if (pool.feeBps == null || pool.k == null) {
|
||||
errors.push(`Chain ${chainId} ${listLabel}[${i}]: feeBps and k required`);
|
||||
}
|
||||
if (base && !knownTokens.has(base)) {
|
||||
errors.push(`Chain ${chainId} ${listLabel}[${i}]: base/tokenIn "${base}" not in cwTokens or anchorAddresses`);
|
||||
}
|
||||
if (quote && !knownTokens.has(quote)) {
|
||||
errors.push(`Chain ${chainId} ${listLabel}[${i}]: quote/tokenOut "${quote}" not in cwTokens or anchorAddresses`);
|
||||
}
|
||||
const addr = pool.poolAddress;
|
||||
if (addr != null && addr !== '') {
|
||||
const z = String(addr).toLowerCase();
|
||||
if (z === '0x0000000000000000000000000000000000000000') {
|
||||
errors.push(`Chain ${chainId} ${listLabel}[${i}]: poolAddress must not be zero when set`);
|
||||
}
|
||||
if (pool.publicRoutingEnabled === true && looksPlaceholderAddress(z)) {
|
||||
errors.push(`Chain ${chainId} ${listLabel}[${i}]: live public routing poolAddress must use a native protocol contract, not a placeholder scaffold (${addr})`);
|
||||
}
|
||||
}
|
||||
if (pool.publicRoutingEnabled === true && pool.venue && pool.venue !== 'dodo_pmm') {
|
||||
errors.push(`Chain ${chainId} ${listLabel}[${i}]: public routing rows must use the native dodo_pmm venue, not "${pool.venue}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateUniswapV2Entries(chainId, pools, knownTokens, errors) {
|
||||
for (let i = 0; i < pools.length; i++) {
|
||||
const pool = pools[i];
|
||||
const base = pool.base;
|
||||
const quote = pool.quote;
|
||||
|
||||
if (!base || !knownTokens.has(base)) {
|
||||
errors.push(`Chain ${chainId} uniswapV2Pools[${i}]: base "${base}" not in cwTokens or anchorAddresses`);
|
||||
}
|
||||
if (!quote || !knownTokens.has(quote)) {
|
||||
errors.push(`Chain ${chainId} uniswapV2Pools[${i}]: quote "${quote}" not in cwTokens or anchorAddresses`);
|
||||
}
|
||||
if (!pool.poolAddress || looksPlaceholderAddress(String(pool.poolAddress).toLowerCase())) {
|
||||
errors.push(`Chain ${chainId} uniswapV2Pools[${i}]: poolAddress must use a real deployed pair address`);
|
||||
}
|
||||
if (pool.venue && pool.venue !== 'uniswap_v2_pair') {
|
||||
errors.push(`Chain ${chainId} uniswapV2Pools[${i}]: venue must be "uniswap_v2_pair" when set`);
|
||||
}
|
||||
if (pool.publicRoutingEnabled === true && (!pool.factoryAddress || !pool.routerAddress)) {
|
||||
errors.push(`Chain ${chainId} uniswapV2Pools[${i}]: publicRoutingEnabled rows require factoryAddress and routerAddress`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const status = loadJson(DEPLOYMENT_STATUS_PATH);
|
||||
const chains = status.chains || {};
|
||||
const homeChainId = String(status.homeChainId || '');
|
||||
const errors = [];
|
||||
|
||||
for (const [chainId, chain] of Object.entries(chains)) {
|
||||
const cwTokens = chain.cwTokens || {};
|
||||
const gasMirrors = chain.gasMirrors || {};
|
||||
const anchorAddresses = chain.anchorAddresses || {};
|
||||
const gasQuoteAddresses = chain.gasQuoteAddresses || {};
|
||||
const pmmPools = chain.pmmPools || [];
|
||||
const uniswapV2Pools = chain.uniswapV2Pools || [];
|
||||
const pmmPoolsVolatile = chain.pmmPoolsVolatile || [];
|
||||
const gasPmmPools = chain.gasPmmPools || [];
|
||||
const gasReferenceVenues = chain.gasReferenceVenues || [];
|
||||
const bridgeAvailable = chain.bridgeAvailable;
|
||||
|
||||
const isHomeChain = chainId === homeChainId;
|
||||
const skipPhase1Requirement = isHomeChain || chainId === '651940';
|
||||
|
||||
if (bridgeAvailable === true && !skipPhase1Requirement) {
|
||||
if (bridgeAvailable === true) {
|
||||
for (const sym of PHASE1_CW) {
|
||||
if (!cwTokens[sym] || typeof cwTokens[sym] !== 'string' || !cwTokens[sym].trim()) {
|
||||
errors.push(`Chain ${chainId} (${chain.name}): bridgeAvailable=true but cwTokens.${sym} missing or empty`);
|
||||
@@ -132,97 +43,24 @@ function main() {
|
||||
}
|
||||
}
|
||||
|
||||
const knownTokens = new Set([
|
||||
...Object.keys(cwTokens),
|
||||
...Object.keys(gasMirrors),
|
||||
...Object.keys(anchorAddresses),
|
||||
...Object.keys(gasQuoteAddresses),
|
||||
]);
|
||||
const knownTokens = new Set([...Object.keys(cwTokens), ...Object.keys(anchorAddresses)]);
|
||||
|
||||
if (isHomeChain) {
|
||||
for (const pool of [...pmmPools, ...pmmPoolsVolatile]) {
|
||||
const base = pool.base ?? pool.tokenIn;
|
||||
const quote = pool.quote ?? pool.tokenOut;
|
||||
if (typeof base === 'string' && HOME_CHAIN_CANONICAL_PREFIXES.some((prefix) => base.startsWith(prefix))) {
|
||||
knownTokens.add(base);
|
||||
}
|
||||
if (typeof quote === 'string' && HOME_CHAIN_CANONICAL_PREFIXES.some((prefix) => quote.startsWith(prefix))) {
|
||||
knownTokens.add(quote);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < pmmPools.length; i++) {
|
||||
const pool = pmmPools[i];
|
||||
const base = pool.base ?? pool.tokenIn;
|
||||
const quote = pool.quote ?? pool.tokenOut;
|
||||
|
||||
validatePoolEntries(chainId, pmmPools, 'pmmPools', knownTokens, errors);
|
||||
validateUniswapV2Entries(chainId, uniswapV2Pools, knownTokens, errors);
|
||||
validatePoolEntries(chainId, pmmPoolsVolatile, 'pmmPoolsVolatile', knownTokens, errors);
|
||||
validatePoolEntries(chainId, gasPmmPools, 'gasPmmPools', knownTokens, errors);
|
||||
|
||||
const gasPoolsByFamily = new Map();
|
||||
for (const pool of gasPmmPools) {
|
||||
if (!pool.familyKey || typeof pool.familyKey !== 'string') {
|
||||
errors.push(`Chain ${chainId} gasPmmPools entry is missing familyKey`);
|
||||
continue;
|
||||
if (!VALID_ROLES.includes(pool.role)) {
|
||||
errors.push(`Chain ${chainId} pmmPools[${i}]: role must be one of ${VALID_ROLES.join(', ')}`);
|
||||
}
|
||||
if (!gasPoolsByFamily.has(pool.familyKey)) gasPoolsByFamily.set(pool.familyKey, []);
|
||||
gasPoolsByFamily.get(pool.familyKey).push(pool);
|
||||
}
|
||||
|
||||
for (const [familyKey, pools] of gasPoolsByFamily.entries()) {
|
||||
const poolTypes = new Set(pools.map((pool) => pool.poolType));
|
||||
if (!poolTypes.has('wrapped_native')) {
|
||||
errors.push(`Chain ${chainId} gas family ${familyKey}: missing wrapped_native DODO pool`);
|
||||
if (pool.feeBps == null || pool.k == null) {
|
||||
errors.push(`Chain ${chainId} pmmPools[${i}]: feeBps and k required`);
|
||||
}
|
||||
if (!poolTypes.has('stable_quote')) {
|
||||
errors.push(`Chain ${chainId} gas family ${familyKey}: missing stable_quote DODO pool`);
|
||||
if (base && !knownTokens.has(base)) {
|
||||
errors.push(`Chain ${chainId} pmmPools[${i}]: base/tokenIn "${base}" not in cwTokens or anchorAddresses`);
|
||||
}
|
||||
}
|
||||
|
||||
const referenceVenuesByFamily = new Map();
|
||||
for (let i = 0; i < gasReferenceVenues.length; i++) {
|
||||
const venue = gasReferenceVenues[i];
|
||||
if (!VALID_REFERENCE_PROTOCOLS.includes(venue.protocol)) {
|
||||
errors.push(`Chain ${chainId} gasReferenceVenues[${i}]: protocol must be one of ${VALID_REFERENCE_PROTOCOLS.join(', ')}`);
|
||||
}
|
||||
if (typeof venue.supported !== 'boolean') {
|
||||
errors.push(`Chain ${chainId} gasReferenceVenues[${i}]: supported must be set explicitly to true or false`);
|
||||
}
|
||||
if (!venue.familyKey || typeof venue.familyKey !== 'string') {
|
||||
errors.push(`Chain ${chainId} gasReferenceVenues[${i}]: familyKey required`);
|
||||
continue;
|
||||
}
|
||||
if (!referenceVenuesByFamily.has(venue.familyKey)) referenceVenuesByFamily.set(venue.familyKey, []);
|
||||
referenceVenuesByFamily.get(venue.familyKey).push(venue);
|
||||
if (venue.aggregatorOnly === true && venue.protocol !== '1inch') {
|
||||
errors.push(`Chain ${chainId} gasReferenceVenues[${i}]: aggregatorOnly rows must use protocol "1inch"`);
|
||||
}
|
||||
if (isLiveRow(venue)) {
|
||||
if (!VALID_NATIVE_REFERENCE_PROTOCOLS.includes(venue.protocol)) {
|
||||
errors.push(`Chain ${chainId} gasReferenceVenues[${i}]: live/routingVisible rows must use a native protocol contract, not "${venue.protocol}"`);
|
||||
}
|
||||
if (looksPlaceholderAddress(venue.venueAddress)) {
|
||||
errors.push(`Chain ${chainId} gasReferenceVenues[${i}]: live/routingVisible venueAddress must use a native protocol contract, not a placeholder scaffold (${venue.venueAddress})`);
|
||||
}
|
||||
}
|
||||
if (venue.supported === false && isLiveRow(venue)) {
|
||||
errors.push(`Chain ${chainId} gasReferenceVenues[${i}]: supported=false rows cannot be live/routingVisible`);
|
||||
}
|
||||
if (venue.supported === false && venue.protocol === '1inch' && venue.aggregatorOnly !== true) {
|
||||
errors.push(`Chain ${chainId} gasReferenceVenues[${i}]: unsupported 1inch rows must be marked aggregatorOnly=true`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [familyKey, venues] of referenceVenuesByFamily.entries()) {
|
||||
const protocols = new Set(venues.map((venue) => venue.protocol));
|
||||
if (!protocols.has('uniswap_v3')) {
|
||||
errors.push(`Chain ${chainId} gas family ${familyKey}: missing uniswap_v3 reference venue`);
|
||||
}
|
||||
const oneInch = venues.find((venue) => venue.protocol === '1inch');
|
||||
if (oneInch?.routingVisible === true || oneInch?.live === true) {
|
||||
const hasUniswap = venues.some((venue) => venue.protocol === 'uniswap_v3' && venue.live === true);
|
||||
const hasDodo = (gasPoolsByFamily.get(familyKey) || []).some((pool) => pool.publicRoutingEnabled === true);
|
||||
if (!hasUniswap || !hasDodo) {
|
||||
errors.push(`Chain ${chainId} gas family ${familyKey}: 1inch cannot be live/routingVisible before DODO and Uniswap venues are live`);
|
||||
}
|
||||
if (quote && !knownTokens.has(quote)) {
|
||||
errors.push(`Chain ${chainId} pmmPools[${i}]: quote/tokenOut "${quote}" not in cwTokens or anchorAddresses`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,33 +72,4 @@ function main() {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function resolveInputPath(argv) {
|
||||
const candidate = argv[2];
|
||||
if (!candidate || candidate === '-h' || candidate === '--help') {
|
||||
return DEPLOYMENT_STATUS_PATH;
|
||||
}
|
||||
return path.isAbsolute(candidate) ? candidate : path.join(process.cwd(), candidate);
|
||||
}
|
||||
|
||||
function printUsage() {
|
||||
process.stdout.write('Usage: node scripts/validate-deployment-status.cjs [path/to/deployment-status.json]\n');
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
const argv = process.argv;
|
||||
if (argv[2] === '-h' || argv[2] === '--help') {
|
||||
printUsage();
|
||||
process.exit(0);
|
||||
}
|
||||
if (argv[2]) {
|
||||
DEPLOYMENT_STATUS_PATH = resolveInputPath(argv);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
looksPlaceholderAddress,
|
||||
isLiveRow,
|
||||
validatePoolEntries,
|
||||
main,
|
||||
};
|
||||
main();
|
||||
|
||||
@@ -22,10 +22,9 @@ For each epoch:
|
||||
2. **Compute candidate paths** (e.g. k-shortest paths or enumerate swap+bridge combos).
|
||||
3. **Allocate flow** by marginal-equalization heuristic (waterfilling): split volume across paths so marginal output equalizes.
|
||||
4. **Update PMM inventories** and implied prices (use inventory-sensitive depth D = D_0·min(1, I_T/I_T^*)).
|
||||
5. **Micro-support step (optional):** run gas-budgeted support trades for selected rails (e.g. `cWUSDC`, `cWUSDT`) to model tiny turnover-seeding or inventory-corrective activity.
|
||||
6. **Arb step (optional):** agents that trade toward oracle when profitable; update inventories again.
|
||||
7. **Bot intervention step:** apply policy from config (or keep exogenous); record intervention cost.
|
||||
8. **Emit scorecard** (or accumulate for end-of-run scorecard).
|
||||
5. **Arb step (optional):** agents that trade toward oracle when profitable; update inventories again.
|
||||
6. **Bot intervention step:** apply policy from config (or keep exogenous); record intervention cost.
|
||||
7. **Emit scorecard** (or accumulate for end-of-run scorecard).
|
||||
|
||||
---
|
||||
|
||||
@@ -47,10 +46,9 @@ AMM edges can be mocked (e.g. fixed 5 bps spread); relative behavior of PMM vs a
|
||||
- **PMM state:** Per-pool `I_T`, `D = D_0·min(1, I_T/I_T^*)`, sell/buy with documented formula; routing controls applied.
|
||||
- **Routing:** Candidate paths (same-chain, length ≤3), top-K by cost; waterfilling by chunk (5%), marginal-equalization.
|
||||
- **Arb step (PR#2):** Implied price (sell/buy probe) vs oracle P; deviation δ; if |δ| > DELTA_ARB_BPS, trade in corrective direction with size min(x_max, α·|δ|·I_T^*); profit gate (skip if profit ≤ 0). Tuning: `DELTA_ARB_BPS`, `ARB_ALPHA`, `ARB_MAX_FRACTION_OF_TARGET`.
|
||||
- **Micro-support step:** When `scenario.microTradePolicy` is enabled, run tiny support trades on selected cW rails before arb. Policy can prefer matched quotes (`cWUSDC/USDC`, `cWUSDT/USDT`), stay inside an inventory band, alternate when inside band, and clip the trade count by a per-epoch gas budget.
|
||||
- **Bot step (PR#2):** If I_T < 0.5·I_T^* inject; if I_T > 1.5·I_T^* withdraw; action clipped to `BOT_MAX_FRACTION_OF_TARGET`·I_T^*; intervention cost = |u|·(β+ρ)+γ (bridge params + latency ρ from scenario `latencyModel`).
|
||||
- **Bridge shock:** When `scenario.bridgeShock` is set, extra trades (sell cW on fromChain, buy cW on toChain) over `durationEpochs` at `magnitudeFraction` of baseline.
|
||||
- **Determinism:** RNG seeded from `scenario.seed` or hash of scenario name.
|
||||
- **Scorecard:** All PR#1 metrics plus `peak_deviation_bps`, `intervention_cost_*`, `arb_volume_total`, `arb_profit_total`, and support-lane `micro_trade_*` metrics.
|
||||
- **Scorecard:** All PR#1 metrics plus `peak_deviation_bps`, `intervention_cost_*`, `arb_volume_total`, `arb_profit_total`.
|
||||
|
||||
Tuning: see constants in script and [scripts/README.md](../scripts/README.md).
|
||||
|
||||
@@ -33,6 +33,5 @@ for each pool (cW* / quote):
|
||||
- **Bridge throttle**: If bridge backlog or risk flag, widen bands or skip trade.
|
||||
- **Global budget**: Before any trade, check per-token trade budget for current window; skip if exhausted.
|
||||
- **Min improvement**: Only trade if expected |δ| improvement net of fees ≥ minImprovementBps.
|
||||
- **Gas-budgeted micro-support**: For selected USD wrappers (especially `cWUSDC`, `cWUSDT`), allow tiny matched-quote trades (`cWUSDC/USDC`, `cWUSDT/USDT` when available) under a separate gas budget so wrappers stay economically live without converting the PMM into the destination venue.
|
||||
|
||||
Thresholds and bands from [../config/peg-bands.json](../config/peg-bands.json). Mesh reflexivity from [../docs/07-mesh-reflexivity.md](../docs/07-mesh-reflexivity.md).
|
||||
|
||||
Reference in New Issue
Block a user