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.
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.
Run:
scripts/build-emode-cu-binaries.sh
PATH=/tmp/node-official/bin:$PATH scripts/run-emode-cu-comparison.jsMost 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.
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 --syntheticThat 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.
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.jsonFor 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 20To 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 20Latest 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,723hitmin-active-balances + min-assets + min-liabilities71,335hitmin-active-balances + min-liabilities12,890hit onlymin-liabilities693hitmin-active-balances + min-assets89hit onlymin-assets
Structured outputs for a full rerun live under the benchmark snapshot directory, for example:
benchmarks/emode-mainnet-snapshots/<full-run-dir>/aggregate-summary.jsonbenchmarks/emode-mainnet-snapshots/<full-run-dir>/aggregate-failures.jsonbenchmarks/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.jsondirectly 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_updatefield, not inferred from bank cache timestamps. - Chunked full reruns should pass a fixed
--reference-timestampso every recency window (within 1d / 7d / 14d / ...) is measured against the same wall clock. - The
--syntheticand--self-testmodes 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
24uniqueStakedWithPythPushbanks whose captured LST supply was non-positive. - The chunked full run produced
0comparable 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.
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:
A1againstL1; - Account 2:
A2againstL2.
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.
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.
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.jsonOnly the last two commands above are account-based report modes. The first two are synthetic checks.