Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Optimal E-Mode Risk Engine Report

This is the report/demo layer for the optimal e-mode experiment. It builds on feat/emode-optimal-cu-comparison, which contains the implementation, CU benchmark harness, and binary comparison runner.

The repository is currently arranged as a wrapper root plus the actual Anchor workspace in marginfi-v2/. The scripts in scripts/ auto-detect that nested workspace, so you should run the commands below from this top-level directory.

What Changed

The legacy e-mode path folds all liability configs into an overlap config: only collateral tags present on every liability are usable, and each shared tag receives the minimum e-mode weight across those liabilities.

The current optimal path works as follows:

  • every asset starts at a fallback floor of max(base_weight, legacy_shared_intersection_weight);
  • each liability still contributes a finite capacity equal to its weighted liability value;
  • each compatible asset/liability tag edge can improve only the slices that do better than that floor;
  • fractional allocation is still allowed, so the incremental uplift above the floor remains a linear program.

This matters because the model uses the shared-intersection floor when it applies and then allocates only the incremental gain above that floor. In single-liability shapes and other cases, that preserves the valid floor treatment instead of discarding it.

The implementation uses a general revised-simplex solver for the LP. It also has two small fast paths for simple cases:

  • one asset and many liabilities;
  • many assets and one liability.

Compute Unit Results

Run:

scripts/build-emode-cu-binaries.sh
PATH=/tmp/node-official/bin:$PATH scripts/run-emode-cu-comparison.js

Most recently verified side-by-side CU snapshot:

Scenario Optimal Legacy Delta
dense_2x2_healthy 320,100 CU 199,416 CU +120,684 CU / +60.52%
dense_4x4_healthy 943,134 CU 387,251 CU +555,883 CU / +143.55%
dense_8x8_healthy setup failed during borrow at 1,400,000 CU 766,532 CU n/a
dense_8x8_high_borrow_setup setup failed during borrow at 1,400,000 CU setup failed with risk error 0x1779 n/a
dense_8x8_unhealthy_reweight setup failed during borrow at 1,400,000 CU 754,924 CU n/a
dense_8x8_liquidation_start_end setup failed during borrow at 1,400,000 CU operation failed with 0xbbf n/a

The important result is not subtle: even after replacing repeated dense LP solves with revised simplex, the optimal engine is too expensive for dense max-balance accounts on the normal borrow path. It improves capital efficiency, but it does not currently fit the worst-case CU budget.

The CU harness was not fully re-verified in this session because the benchmark path still depends on additional generated test artifacts (target/idl/mocks.json and target/types/mocks.ts) in this workspace state. The table above is therefore the last verified comparison snapshot, not a fresh rerun.

Capital Efficiency

The optimal path can produce strictly better health than the least-favorable shared-tag rule because it keeps liability capacities separate while using the shared-intersection floor where that floor is part of the valid formulation.

Run the deterministic synthetic demo:

PATH=/tmp/node-official/bin:$PATH node scripts/emode-mainnet-health-scan.js --synthetic

That example has three collateral tags and three liability configs with overlapping but non-identical tag support. There is no shared useful legacy-intersection floor in this toy shape, so the single-account legacy rule falls back to base weight for every asset, while the optimal solver pairs each collateral slice to the liability capacity where it is most useful:

Case Asset value
Legacy single account 130.000000
Optimal single account 212.400752
Delta 82.400752
Delta pct 63.39%

The same script also prints the split-account witness: each positive optimal allocation can be represented as a separate account containing the collateral slice and the liability bank that gives that slice its e-mode weight, the same idea can be extended to more general cases.

It is separate from the actual mainnet account report path below.

Full Mainnet Analysis

The scanner can fetch real marginfi accounts from mainnet, recompute initial/maintenance health off-chain using both engines, and rank the accounts by health improvement.

This mainnet report path does not fabricate accounts or synthesize missing state. It only evaluates accounts whose bank and oracle inputs can be reconstructed from fetched mainnet data. If an account or oracle setup cannot be handled exactly with the data available, that case is skipped and the reason is reported.

The RPC key is intentionally not stored in this repository. Use an environment variable. For a reproducible report, write both a result file and a raw snapshot:

MARGINFI_RPC_URL="https://mainnet.helius-rpc.com/?api-key=<key>" \
  PATH=/tmp/node-official/bin:$PATH \
  node scripts/emode-mainnet-health-scan.js \
  --limit 5000 \
  --requirement both \
  --top 20 \
  --write-addresses /tmp/emode-addresses.json \
  --snapshot-output benchmarks/emode-mainnet-snapshots/mainnet-5000.json.gz \
  --output /tmp/emode-health-scan-5000-both.json

For repeat runs, reuse the cached address list and avoid another full account-discovery GPA:

MARGINFI_RPC_URL="https://mainnet.helius-rpc.com/?api-key=<key>" \
  PATH=/tmp/node-official/bin:$PATH \
  node scripts/emode-mainnet-health-scan.js \
  --addresses-file /tmp/emode-addresses.json \
  --limit 5000 \
  --requirement both \
  --top 20

To replay an exact historical state without RPC, use the snapshot:

PATH=/tmp/node-official/bin:$PATH \
  node scripts/emode-mainnet-health-scan.js \
  --snapshot-input benchmarks/emode-mainnet-snapshots/mainnet-5000.json.gz \
  --requirement both \
  --top 20

Latest full rerun over all 510,080 discovered marginfi accounts, using the fixed recency reference timestamp 2026-05-11T01:25:00Z:

Requirement Scanned Filtered Comparable Improved Unchanged Regressed Improved / Comparable Comparable / Scanned Total unlocked weighted asset value
Initial 510,080 430,730 79,350 3,677 75,673 0 4.63% 15.56% 98,542.579135
Maintenance 510,080 430,730 79,350 3,661 75,689 0 4.61% 15.56% 58,202.060027

Improvement buckets from the full rerun:

  • Initial: >0% 3,677, >=1% 2,540, >=5% 2,063, >=10% 1,753, >=20% 1,403, >=50% 976, >=100% 644, >=200% 355, >=500% 156, >=1000% 65.
  • Maintenance: >0% 3,661, >=1% 2,285, >=5% 1,593, >=10% 1,219, >=20% 844, >=50% 416, >=100% 166, >=200% 79, >=500% 38, >=1000% 23.

Cumulative recency windows from the same full rerun:

Within Filtered Initial unchanged Initial improved Initial unlocked value Maintenance unchanged Maintenance improved Maintenance unlocked value
1 day 206 185 12 1,106.772863 185 12 734.368671
7 days 697 717 99 7,699.448857 717 99 4,678.734366
14 days 1,173 1,008 109 7,922.001706 1,008 109 4,812.091876
28 days 2,229 1,578 140 10,628.287485 1,578 140 6,536.743086
90 days 7,147 3,782 350 45,106.178596 3,784 348 27,051.540234
180 days 21,361 8,041 842 67,025.700569 8,044 839 40,160.241899
365 days 27,645 9,447 898 70,355.317581 9,452 893 42,051.405500

Filtered accounts from the full rerun:

  • 345,723 hit min-active-balances + min-assets + min-liabilities
  • 71,335 hit min-active-balances + min-liabilities
  • 12,890 hit only min-liabilities
  • 693 hit min-active-balances + min-assets
  • 89 hit only min-assets

Structured outputs for a full rerun live under the benchmark snapshot directory, for example:

  • benchmarks/emode-mainnet-snapshots/<full-run-dir>/aggregate-summary.json
  • benchmarks/emode-mainnet-snapshots/<full-run-dir>/aggregate-failures.json
  • benchmarks/emode-mainnet-snapshots/<full-run-dir>/chunks/

The aggregate summary JSON includes the full percentage-band × recency matrices for improved accounts (>0%, >=1%, >=5%, >=10%, >=20%, >=50%, >=100%, >=200%, >=500%, >=1000%) under both initial and maintenance requirements.

Notes on the scan:

  • The script uses target/idl/marginfi.json directly because the installed JS client bundle cannot decode these production bank accounts with its bundled IDL.
  • Recency is measured from the marginfi account's own last_update field, not inferred from bank cache timestamps.
  • Chunked full reruns should pass a fixed --reference-timestamp so every recency window (within 1d / 7d / 14d / ...) is measured against the same wall clock.
  • The --synthetic and --self-test modes are not mainnet analyses. They are only deterministic checks for the solver logic and split-witness example.
  • The scanner now covers the exact derived-price paths for Kamino, Drift, Solend, JupLend, and the fixed-derived variants. The remaining exact unresolved set is 24 unique StakedWithPythPush banks whose captured LST supply was non-positive.
  • The chunked full run produced 0 comparable regressed accounts for both initial and maintenance, which is the expected outcome of the optimal formulation.
  • Per-account outputs are structured by status (filtered, analyzed, partial, error) and by per-requirement comparison bucket (improved, unchanged, regressed).
  • Per-requirement summaries now include cumulative recency windows, percentage-band × recency matrices for improved accounts, and total added asset-side weighted value for improved accounts.
  • The solver is still general for all asset/liability e-mode graph shapes. The only special cases are exact one-asset or one-liability fast paths, which are the same LP reduced to one dimension.

Account Splitting Argument

The least-favorable rule is conservative inside one account, but it does not eliminate the economic exposure if users can split positions across accounts.

If one account has assets A1, A2 and liabilities L1, L2, legacy forces the collateral tags to be valid for all liabilities at once. A user can instead create:

  • Account 1: A1 against L1;
  • Account 2: A2 against L2.

Each account now sees only the liability it is paired with, so the legacy intersection rule becomes less restrictive. The optimal single-account solver captures that same separable pairing directly.

The mainnet scan prints a split witness for the top account in each table. For example, in the synthetic split-equivalence demo, the solver emits:

asset_bank DMoqjm...adC5 -> liability_bank Bohoc1...KYM8
tag 1571, value slice 0.108868, e-mode weight 0.880000
asset_bank CCKtUs...LYGh -> liability_bank Bohoc1...KYM8
tag 501, value slice 0.023556, e-mode weight 0.900000

That witness is the concrete on-chain-account version of the synthetic splitting example.

Why Not Ellipsoid?

The ellipsoid method is polynomial-time, but it is a poor fit for on-chain execution:

  • it has large constants and many iterations;
  • it needs numerically delicate fixed-point matrix/vector updates;
  • it is designed for theoretical polynomial guarantees, not small deterministic CU budgets.

Interior-point methods have similar issues: they are excellent off-chain, but expensive and numerically fussy on-chain.

For this problem size, a specialized network-flow implementation or revised simplex is much more realistic than ellipsoid. The revised-simplex implementation is still too expensive for dense max-balance accounts, which strongly suggests that the best production answer is something close to what is already being used.

Mainnet Health Scan

The mainnet health scan is implemented in scripts/emode-mainnet-health-scan.js.

Useful commands:

PATH=/tmp/node-official/bin:$PATH node scripts/emode-mainnet-health-scan.js --self-test
PATH=/tmp/node-official/bin:$PATH node scripts/emode-mainnet-health-scan.js --synthetic
MARGINFI_RPC_URL=<rpc> PATH=/tmp/node-official/bin:$PATH node scripts/emode-mainnet-health-scan.js --limit 5000 --requirement both --top 20 --reference-timestamp 2026-05-11T01:25:00Z
node scripts/emode-mainnet-health-scan.js --snapshot-input <snapshot.json.gz> --requirement both --reference-timestamp 2026-05-11T01:25:00Z
node scripts/aggregate-emode-mainnet-health-chunks.js --chunks-dir benchmarks/emode-mainnet-snapshots/full-mainnet-<date>-batch<chunk-size>/chunks --summary-output benchmarks/emode-mainnet-snapshots/full-mainnet-<date>-batch<chunk-size>/aggregate-summary.json --failures-output benchmarks/emode-mainnet-snapshots/full-mainnet-<date>-batch<chunk-size>/aggregate-failures.json

Only the last two commands above are account-based report modes. The first two are synthetic checks.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages