From 21857ae8caec61c584acb87b6bf2bfbc3abdd923 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 4 Feb 2026 20:58:30 +0000 Subject: [PATCH] feat: protocol stats display + parameter sweep fuzzing infrastructure (#106) Co-Authored-By: Claude Opus 4.6 --- onchain/analysis/PARAMETER_SEARCH_RESULTS.md | 361 ++++++++++++++++ onchain/analysis/ParameterSweepFuzzing.s.sol | 306 ++++++++++++++ onchain/analysis/StreamlinedFuzzing.s.sol | 420 +++++++++++-------- onchain/analysis/helpers/SwapExecutor.sol | 124 ++---- onchain/analysis/run-fuzzing.sh | 102 ++--- onchain/analysis/run-parameter-sweep.sh | 264 ++++++++++++ onchain/analysis/sweep-FULL-summary.csv | 253 +++++++++++ onchain/test/ReplayProfitableScenario.t.sol | 4 +- onchain/test/mocks/ConfigurableOptimizer.sol | 45 ++ web-app/src/views/CheatsView.vue | 21 + 10 files changed, 1592 insertions(+), 308 deletions(-) create mode 100644 onchain/analysis/PARAMETER_SEARCH_RESULTS.md create mode 100644 onchain/analysis/ParameterSweepFuzzing.s.sol create mode 100755 onchain/analysis/run-parameter-sweep.sh create mode 100644 onchain/analysis/sweep-FULL-summary.csv create mode 100644 onchain/test/mocks/ConfigurableOptimizer.sol diff --git a/onchain/analysis/PARAMETER_SEARCH_RESULTS.md b/onchain/analysis/PARAMETER_SEARCH_RESULTS.md new file mode 100644 index 0000000..404f1ab --- /dev/null +++ b/onchain/analysis/PARAMETER_SEARCH_RESULTS.md @@ -0,0 +1,361 @@ +# KRAIKEN Parameter Search Results + +## Objective +Find optimizer parameters where the LiquidityManager NEVER loses ETH regardless of trade sequence. + +## Methodology +- **Mode**: boundary +- **Runs per parameter combination**: 5 +- **Trades per run**: 30 +- **Uncapped swaps**: true (trades push through position boundaries) +- **Trade size range**: 20-80 ETH (vs 100 ETH LM balance) +- **VWAP accumulation**: Environment reused across runs per combo +- **Buy biases tested**: 80% +- **Total combinations tested**: 252 +- **Elapsed time**: 64s + +### Parameters Searched +| Parameter | Values | Description | +|-----------|--------|-------------| +| capitalInefficiency | 0%, 30%, 50%, 80% | Capital buffer (0=aggressive, 1e18=conservative) | +| anchorShare | 10.0%, 20.0%, 30.0%, 40.0%, 50.0%, 70.0%, 100.0% | ETH allocation to anchor vs floor | +| anchorWidth | 30,50,80 | Anchor position width (1-100) | +| discoveryDepth | 20%, 50%, 100% | Discovery liquidity density | +| buyBias | 80% | Adversarial trade mix (80=buy-heavy, 100=buy-only) | + +### Key Metrics +- **Trader PnL < 0**: LM gained ETH (SAFE). Trader lost money. +- **Trader PnL > 0**: LM lost ETH (UNSAFE). Trader extracted ETH. + +## Results Summary +- **Safe combinations**: 1 / 252 +- **Unsafe combinations**: 251 / 252 + +## UNSAFE Parameters (Trader Profited = LM Lost ETH) + +| CI | AnchorShare | AnchorWidth | DiscoveryDepth | BuyBias | Worst Trader PnL (ETH) | +|-----|-------------|-------------|----------------|---------|------------------------| +| 0% | 10.0% | 30 | 20% | 80 | +44.4865 | +| 0% | 10.0% | 30 | 50% | 80 | +167.0544 | +| 0% | 10.0% | 30 | 100% | 80 | +173.8168 | +| 0% | 10.0% | 50 | 20% | 80 | +195.4837 | +| 0% | 10.0% | 50 | 50% | 80 | +151.0647 | +| 0% | 10.0% | 50 | 100% | 80 | +32.3425 | +| 0% | 10.0% | 80 | 50% | 80 | +194.9101 | +| 0% | 10.0% | 80 | 100% | 80 | +142.8975 | +| 0% | 20.0% | 30 | 20% | 80 | +155.1874 | +| 0% | 20.0% | 30 | 50% | 80 | +163.5719 | +| 0% | 20.0% | 30 | 100% | 80 | +165.1913 | +| 0% | 20.0% | 50 | 20% | 80 | +160.7820 | +| 0% | 20.0% | 50 | 50% | 80 | +45.7948 | +| 0% | 20.0% | 50 | 100% | 80 | +158.7578 | +| 0% | 20.0% | 80 | 20% | 80 | +194.8064 | +| 0% | 20.0% | 80 | 50% | 80 | +195.2834 | +| 0% | 20.0% | 80 | 100% | 80 | +111.9274 | +| 0% | 30.0% | 30 | 20% | 80 | +151.7137 | +| 0% | 30.0% | 30 | 50% | 80 | +159.1207 | +| 0% | 30.0% | 30 | 100% | 80 | +171.0853 | +| 0% | 30.0% | 50 | 20% | 80 | +129.2839 | +| 0% | 30.0% | 50 | 50% | 80 | +141.0922 | +| 0% | 30.0% | 50 | 100% | 80 | +153.9969 | +| 0% | 30.0% | 80 | 20% | 80 | +194.9104 | +| 0% | 30.0% | 80 | 50% | 80 | +167.4619 | +| 0% | 30.0% | 80 | 100% | 80 | +98.0605 | +| 0% | 40.0% | 30 | 20% | 80 | +150.5747 | +| 0% | 40.0% | 30 | 50% | 80 | +156.1247 | +| 0% | 40.0% | 30 | 100% | 80 | +164.0883 | +| 0% | 40.0% | 50 | 20% | 80 | +101.9849 | +| 0% | 40.0% | 50 | 50% | 80 | +93.0508 | +| 0% | 40.0% | 50 | 100% | 80 | +146.2112 | +| 0% | 40.0% | 80 | 20% | 80 | +195.2850 | +| 0% | 40.0% | 80 | 50% | 80 | +126.8223 | +| 0% | 40.0% | 80 | 100% | 80 | +103.2776 | +| 0% | 50.0% | 30 | 20% | 80 | +160.1651 | +| 0% | 50.0% | 30 | 50% | 80 | +165.6169 | +| 0% | 50.0% | 30 | 100% | 80 | +167.6367 | +| 0% | 50.0% | 50 | 20% | 80 | +137.2574 | +| 0% | 50.0% | 50 | 50% | 80 | +150.7772 | +| 0% | 50.0% | 50 | 100% | 80 | +154.5785 | +| 0% | 50.0% | 80 | 20% | 80 | +195.4837 | +| 0% | 50.0% | 80 | 50% | 80 | +181.7083 | +| 0% | 50.0% | 80 | 100% | 80 | +147.8093 | +| 0% | 70.0% | 30 | 20% | 80 | +172.0906 | +| 0% | 70.0% | 30 | 50% | 80 | +170.8340 | +| 0% | 70.0% | 30 | 100% | 80 | +167.3951 | +| 0% | 70.0% | 50 | 20% | 80 | +156.1362 | +| 0% | 70.0% | 50 | 50% | 80 | +163.3642 | +| 0% | 70.0% | 50 | 100% | 80 | +169.0484 | +| 0% | 70.0% | 80 | 20% | 80 | +120.1484 | +| 0% | 70.0% | 80 | 50% | 80 | +127.5211 | +| 0% | 70.0% | 80 | 100% | 80 | +142.2562 | +| 0% | 100.0% | 30 | 20% | 80 | +159.4093 | +| 0% | 100.0% | 30 | 50% | 80 | +162.4528 | +| 0% | 100.0% | 30 | 100% | 80 | +159.8911 | +| 0% | 100.0% | 50 | 20% | 80 | +164.0498 | +| 0% | 100.0% | 50 | 50% | 80 | +166.4621 | +| 0% | 100.0% | 50 | 100% | 80 | +163.1331 | +| 0% | 100.0% | 80 | 20% | 80 | +139.6734 | +| 0% | 100.0% | 80 | 50% | 80 | +142.3217 | +| 0% | 100.0% | 80 | 100% | 80 | +147.6998 | +| 30% | 10.0% | 30 | 20% | 80 | +90.1073 | +| 30% | 10.0% | 30 | 50% | 80 | +115.8428 | +| 30% | 10.0% | 30 | 100% | 80 | +119.9304 | +| 30% | 10.0% | 50 | 20% | 80 | +126.4138 | +| 30% | 10.0% | 50 | 50% | 80 | +121.1845 | +| 30% | 10.0% | 50 | 100% | 80 | +103.7924 | +| 30% | 10.0% | 80 | 20% | 80 | +71.6892 | +| 30% | 10.0% | 80 | 50% | 80 | +113.3565 | +| 30% | 10.0% | 80 | 100% | 80 | +110.1272 | +| 30% | 20.0% | 30 | 20% | 80 | +99.5726 | +| 30% | 20.0% | 30 | 50% | 80 | +109.5590 | +| 30% | 20.0% | 30 | 100% | 80 | +102.5265 | +| 30% | 20.0% | 50 | 20% | 80 | +120.0764 | +| 30% | 20.0% | 50 | 50% | 80 | +88.3542 | +| 30% | 20.0% | 50 | 100% | 80 | +105.7466 | +| 30% | 20.0% | 80 | 20% | 80 | +115.8770 | +| 30% | 20.0% | 80 | 50% | 80 | +112.3954 | +| 30% | 20.0% | 80 | 100% | 80 | +102.0245 | +| 30% | 30.0% | 30 | 20% | 80 | +88.9205 | +| 30% | 30.0% | 30 | 50% | 80 | +97.1584 | +| 30% | 30.0% | 30 | 100% | 80 | +102.9767 | +| 30% | 30.0% | 50 | 20% | 80 | +113.3466 | +| 30% | 30.0% | 50 | 50% | 80 | +81.5380 | +| 30% | 30.0% | 50 | 100% | 80 | +91.2719 | +| 30% | 30.0% | 80 | 20% | 80 | +113.7442 | +| 30% | 30.0% | 80 | 50% | 80 | +105.2492 | +| 30% | 30.0% | 80 | 100% | 80 | +98.0605 | +| 30% | 40.0% | 30 | 20% | 80 | +80.5371 | +| 30% | 40.0% | 30 | 50% | 80 | +87.1861 | +| 30% | 40.0% | 30 | 100% | 80 | +89.2654 | +| 30% | 40.0% | 50 | 20% | 80 | +101.9849 | +| 30% | 40.0% | 50 | 50% | 80 | +93.0508 | +| 30% | 40.0% | 50 | 100% | 80 | +82.1692 | +| 30% | 40.0% | 80 | 20% | 80 | +112.1467 | +| 30% | 40.0% | 80 | 50% | 80 | +98.4444 | +| 30% | 40.0% | 80 | 100% | 80 | +81.6585 | +| 30% | 50.0% | 30 | 20% | 80 | +81.1150 | +| 30% | 50.0% | 30 | 50% | 80 | +87.9297 | +| 30% | 50.0% | 30 | 100% | 80 | +85.4227 | +| 30% | 50.0% | 50 | 20% | 80 | +64.9790 | +| 30% | 50.0% | 50 | 50% | 80 | +82.5178 | +| 30% | 50.0% | 50 | 100% | 80 | +85.9601 | +| 30% | 50.0% | 80 | 20% | 80 | +106.5547 | +| 30% | 50.0% | 80 | 50% | 80 | +104.7799 | +| 30% | 50.0% | 80 | 100% | 80 | +99.1856 | +| 30% | 70.0% | 30 | 20% | 80 | +81.5207 | +| 30% | 70.0% | 30 | 50% | 80 | +79.0134 | +| 30% | 70.0% | 30 | 100% | 80 | +72.5387 | +| 30% | 70.0% | 50 | 20% | 80 | +69.8455 | +| 30% | 70.0% | 50 | 50% | 80 | +80.6623 | +| 30% | 70.0% | 50 | 100% | 80 | +86.1583 | +| 30% | 70.0% | 80 | 20% | 80 | +65.6840 | +| 30% | 70.0% | 80 | 50% | 80 | +58.3039 | +| 30% | 70.0% | 80 | 100% | 80 | +65.6792 | +| 30% | 100.0% | 30 | 20% | 80 | +48.0308 | +| 30% | 100.0% | 30 | 50% | 80 | +46.0619 | +| 30% | 100.0% | 30 | 100% | 80 | +45.0256 | +| 30% | 100.0% | 50 | 20% | 80 | +61.2679 | +| 30% | 100.0% | 50 | 50% | 80 | +64.0892 | +| 30% | 100.0% | 50 | 100% | 80 | +54.6580 | +| 30% | 100.0% | 80 | 20% | 80 | +59.1616 | +| 30% | 100.0% | 80 | 50% | 80 | +54.4745 | +| 30% | 100.0% | 80 | 100% | 80 | +58.5477 | +| 50% | 10.0% | 30 | 20% | 80 | +44.4865 | +| 50% | 10.0% | 30 | 50% | 80 | +36.2061 | +| 50% | 10.0% | 30 | 100% | 80 | +39.7368 | +| 50% | 10.0% | 50 | 20% | 80 | +78.7116 | +| 50% | 10.0% | 50 | 50% | 80 | +74.2340 | +| 50% | 10.0% | 50 | 100% | 80 | +32.3425 | +| 50% | 10.0% | 80 | 20% | 80 | +72.2638 | +| 50% | 10.0% | 80 | 50% | 80 | +72.8687 | +| 50% | 10.0% | 80 | 100% | 80 | +63.3909 | +| 50% | 20.0% | 30 | 20% | 80 | +31.0651 | +| 50% | 20.0% | 30 | 50% | 80 | +30.5473 | +| 50% | 20.0% | 30 | 100% | 80 | +29.6514 | +| 50% | 20.0% | 50 | 20% | 80 | +75.2892 | +| 50% | 20.0% | 50 | 50% | 80 | +45.7948 | +| 50% | 20.0% | 50 | 100% | 80 | +27.4948 | +| 50% | 20.0% | 80 | 20% | 80 | +72.8613 | +| 50% | 20.0% | 80 | 50% | 80 | +67.6330 | +| 50% | 20.0% | 80 | 100% | 80 | +61.9827 | +| 50% | 30.0% | 30 | 20% | 80 | +34.5388 | +| 50% | 30.0% | 30 | 50% | 80 | +27.1038 | +| 50% | 30.0% | 30 | 100% | 80 | +35.0315 | +| 50% | 30.0% | 50 | 20% | 80 | +70.7938 | +| 50% | 30.0% | 50 | 50% | 80 | +45.1601 | +| 50% | 30.0% | 50 | 100% | 80 | +32.2557 | +| 50% | 30.0% | 80 | 20% | 80 | +70.9767 | +| 50% | 30.0% | 80 | 50% | 80 | +62.9839 | +| 50% | 30.0% | 80 | 100% | 80 | +60.1060 | +| 50% | 40.0% | 30 | 20% | 80 | +35.6779 | +| 50% | 40.0% | 30 | 50% | 80 | +30.1036 | +| 50% | 40.0% | 30 | 100% | 80 | +22.1643 | +| 50% | 40.0% | 50 | 20% | 80 | +66.2748 | +| 50% | 40.0% | 50 | 50% | 80 | +62.6375 | +| 50% | 40.0% | 50 | 100% | 80 | +40.0414 | +| 50% | 40.0% | 80 | 20% | 80 | +67.5835 | +| 50% | 40.0% | 80 | 50% | 80 | +61.6885 | +| 50% | 40.0% | 80 | 100% | 80 | +54.1124 | +| 50% | 50.0% | 30 | 20% | 80 | +26.0875 | +| 50% | 50.0% | 30 | 50% | 80 | +20.6095 | +| 50% | 50.0% | 30 | 100% | 80 | +18.6159 | +| 50% | 50.0% | 50 | 20% | 80 | +48.9952 | +| 50% | 50.0% | 50 | 50% | 80 | +35.4577 | +| 50% | 50.0% | 50 | 100% | 80 | +31.6740 | +| 50% | 50.0% | 80 | 20% | 80 | +70.3513 | +| 50% | 50.0% | 80 | 50% | 80 | +68.3633 | +| 50% | 50.0% | 80 | 100% | 80 | +62.8821 | +| 50% | 70.0% | 30 | 20% | 80 | +14.6729 | +| 50% | 70.0% | 30 | 50% | 80 | +15.8832 | +| 50% | 70.0% | 30 | 100% | 80 | +19.7497 | +| 50% | 70.0% | 50 | 20% | 80 | +30.1163 | +| 50% | 70.0% | 50 | 50% | 80 | +22.8615 | +| 50% | 70.0% | 50 | 100% | 80 | +17.2041 | +| 50% | 70.0% | 80 | 20% | 80 | +56.9112 | +| 50% | 70.0% | 80 | 50% | 80 | +55.4916 | +| 50% | 70.0% | 80 | 100% | 80 | +43.9963 | +| 50% | 100.0% | 30 | 20% | 80 | +14.4888 | +| 50% | 100.0% | 30 | 50% | 80 | +17.3528 | +| 50% | 100.0% | 30 | 100% | 80 | +20.0627 | +| 50% | 100.0% | 50 | 20% | 80 | +22.2027 | +| 50% | 100.0% | 50 | 50% | 80 | +19.7685 | +| 50% | 100.0% | 50 | 100% | 80 | +23.5007 | +| 50% | 100.0% | 80 | 20% | 80 | +44.1949 | +| 50% | 100.0% | 80 | 50% | 80 | +41.6010 | +| 50% | 100.0% | 80 | 100% | 80 | +38.5527 | +| 80% | 10.0% | 30 | 20% | 80 | +36.9053 | +| 80% | 10.0% | 30 | 50% | 80 | +19.1955 | +| 80% | 10.0% | 30 | 100% | 80 | +13.3382 | +| 80% | 10.0% | 50 | 20% | 80 | +38.0580 | +| 80% | 10.0% | 50 | 50% | 80 | +36.8523 | +| 80% | 10.0% | 50 | 100% | 80 | +32.3425 | +| 80% | 10.0% | 80 | 20% | 80 | +22.1459 | +| 80% | 10.0% | 80 | 50% | 80 | +22.4193 | +| 80% | 10.0% | 80 | 100% | 80 | +17.4677 | +| 80% | 20.0% | 30 | 20% | 80 | +31.0651 | +| 80% | 20.0% | 30 | 50% | 80 | +22.6756 | +| 80% | 20.0% | 30 | 100% | 80 | +21.0613 | +| 80% | 20.0% | 50 | 20% | 80 | +32.2943 | +| 80% | 20.0% | 50 | 50% | 80 | +31.7863 | +| 80% | 20.0% | 50 | 100% | 80 | +27.4948 | +| 80% | 20.0% | 80 | 20% | 80 | +23.7564 | +| 80% | 20.0% | 80 | 50% | 80 | +22.6200 | +| 80% | 20.0% | 80 | 100% | 80 | +18.4964 | +| 80% | 30.0% | 30 | 20% | 80 | +27.9013 | +| 80% | 30.0% | 30 | 50% | 80 | +27.1038 | +| 80% | 30.0% | 30 | 100% | 80 | +15.1672 | +| 80% | 30.0% | 50 | 20% | 80 | +31.9832 | +| 80% | 30.0% | 50 | 50% | 80 | +28.1646 | +| 80% | 30.0% | 50 | 100% | 80 | +25.4829 | +| 80% | 30.0% | 80 | 20% | 80 | +23.0357 | +| 80% | 30.0% | 80 | 50% | 80 | +29.0780 | +| 80% | 30.0% | 80 | 100% | 80 | +18.5361 | +| 80% | 40.0% | 30 | 20% | 80 | +26.2602 | +| 80% | 40.0% | 30 | 50% | 80 | +23.8902 | +| 80% | 40.0% | 30 | 100% | 80 | +22.1643 | +| 80% | 40.0% | 50 | 20% | 80 | +28.8204 | +| 80% | 40.0% | 50 | 50% | 80 | +24.2234 | +| 80% | 40.0% | 50 | 100% | 80 | +23.4347 | +| 80% | 40.0% | 80 | 20% | 80 | +31.4300 | +| 80% | 40.0% | 80 | 50% | 80 | +28.4225 | +| 80% | 40.0% | 80 | 100% | 80 | +26.1570 | +| 80% | 50.0% | 30 | 20% | 80 | +20.1297 | +| 80% | 50.0% | 30 | 50% | 80 | +17.7192 | +| 80% | 50.0% | 30 | 100% | 80 | +18.6159 | +| 80% | 50.0% | 50 | 20% | 80 | +21.8346 | +| 80% | 50.0% | 50 | 50% | 80 | +20.5705 | +| 80% | 50.0% | 50 | 100% | 80 | +19.6272 | +| 80% | 50.0% | 80 | 20% | 80 | +30.2614 | +| 80% | 50.0% | 80 | 50% | 80 | +25.7402 | +| 80% | 50.0% | 80 | 100% | 80 | +24.5946 | +| 80% | 70.0% | 30 | 20% | 80 | +9.4713 | +| 80% | 70.0% | 30 | 50% | 80 | +10.6575 | +| 80% | 70.0% | 30 | 100% | 80 | +19.7497 | +| 80% | 70.0% | 50 | 20% | 80 | +14.8283 | +| 80% | 70.0% | 50 | 50% | 80 | +12.9254 | +| 80% | 70.0% | 50 | 100% | 80 | +12.3710 | +| 80% | 70.0% | 80 | 20% | 80 | +23.0548 | +| 80% | 70.0% | 80 | 50% | 80 | +21.2336 | +| 80% | 70.0% | 80 | 100% | 80 | +18.9539 | +| 80% | 100.0% | 30 | 20% | 80 | +12.7994 | +| 80% | 100.0% | 30 | 50% | 80 | +13.8975 | +| 80% | 100.0% | 30 | 100% | 80 | +16.5736 | +| 80% | 100.0% | 50 | 20% | 80 | +7.8091 | +| 80% | 100.0% | 50 | 50% | 80 | +7.6161 | +| 80% | 100.0% | 50 | 100% | 80 | +18.5219 | +| 80% | 100.0% | 80 | 20% | 80 | +18.8946 | +| 80% | 100.0% | 80 | 50% | 80 | +18.7205 | +| 80% | 100.0% | 80 | 100% | 80 | +16.0340 | + +## SAFE Parameters + +All tested parameter combinations where the LM never lost ETH: + +### Safe Ranges by Buy Bias + +- **BuyBias=80%**: 1/252 safe + +### Top 20 Safest Combinations (most negative trader PnL = hardest to exploit) + +| CI | AnchorShare | AnchorWidth | DiscoveryDepth | BuyBias | Worst Trader PnL (ETH) | +|-----|-------------|-------------|----------------|---------|------------------------| +| 0% | 10.0% | 80 | 20% | 80 | -114.4822 | + +## Exploitation Mechanism + +### Where Trader Profit ETH Comes From +The trader's profit WETH is extracted from the Uniswap pool's liquidity, which is backed by +the LM's positions. Since the LM is the sole liquidity provider: +1. Trader buys KRAIKEN: sends WETH to pool, receives KRAIKEN from LM's positions +2. Recenter fires: LM scrapes positions, repositions at new price, mints fresh KRAIKEN +3. Trader sells KRAIKEN: receives WETH from pool (from LM's repositioned liquidity) +4. Net: trader extracts WETH from LM's position value + +This is **impermanent loss (IL)** from dynamic liquidity management. Each recenter +repositions liquidity at the current price, "absorbing" the price impact. The trader +exploits this by pushing price, triggering recenter, then reversing. + +### VWAP is NOT the Attack Vector +VWAP tracks historical anchor prices weighted by ETH fee volume. `getAdjustedVWAP(CI)` +returns `(70% * VWAP) + (CI * VWAP)`, which places the floor BELOW average prices +when CI is low (bull) and ABOVE when CI is high (bear). VWAP **anchors the floor to +history** — it is a safety mechanism that prevents the floor from chasing price up, +not an exploitation surface. + +### Measurement Notes +- **Trader PnL** is the reliable safety metric: WETH balance before vs after trading+liquidation +- **lm_eth_delta** in the CSV uses `lm_balance + pool_WETH` as a rough proxy for total LM ETH. + This metric is noisy because pool WETH mixes LM position ETH with trader buy WETH in transit. + It is informational only — trader PnL is the authoritative signal. + +### Confirmed: Capped Swaps Prevent Exploitation +With LiquidityBoundaryHelper caps enabled (production behavior): trader ALWAYS loses money. +Capped swaps prevent the trader from pushing through position boundaries, eliminating the +IL-based extraction. The question is whether caps restrict legitimate large trades. + +## Recommendations + +**251 of 252 tested parameter combinations are UNSAFE with uncapped swaps.** + +The exploitation stems from the LM acting as a repositioning market maker: each recenter +resets position geometry around the current price, allowing a trader to repeatedly extract +IL by moving the price and triggering repositioning. + +### capitalInefficiency Effect +| CI | Unsafe Rate | Avg Trader Profit | +|----|-------------|-------------------| +| 0% (bull) | 98% | ~100+ ETH | +| 30% | 100% | ~85 ETH | +| 50% | 100% | ~40 ETH | +| 80% (bear) | 100% | ~22 ETH | + +CI reduces exploitation magnitude but does not eliminate it. + +### Mitigation Directions (Require Code Changes) +1. **Keep swap caps**: LiquidityBoundaryHelper prevents exploitation entirely +2. **Recenter cooldown**: Prevent rapid buy→recenter→sell→recenter cycles +3. **Price impact detection**: Detect when recenter is triggered by a single actor's trades +4. **Position geometry hardening**: Widen positions to increase slippage cost of round-trips + +--- +*Generated: 2026-02-07* diff --git a/onchain/analysis/ParameterSweepFuzzing.s.sol b/onchain/analysis/ParameterSweepFuzzing.s.sol new file mode 100644 index 0000000..f0d1b4a --- /dev/null +++ b/onchain/analysis/ParameterSweepFuzzing.s.sol @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { Kraiken } from "../src/Kraiken.sol"; + +import { LiquidityManager } from "../src/LiquidityManager.sol"; +import { Stake } from "../src/Stake.sol"; +import { ThreePositionStrategy } from "../src/abstracts/ThreePositionStrategy.sol"; +import { UniswapHelpers } from "../src/helpers/UniswapHelpers.sol"; +import { IWETH9 } from "../src/interfaces/IWETH9.sol"; +import { TestEnvironment } from "../test/helpers/TestBase.sol"; + +import { ConfigurableOptimizer } from "../test/mocks/ConfigurableOptimizer.sol"; +import { SwapExecutor } from "./helpers/SwapExecutor.sol"; +import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; +import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; +import "forge-std/Script.sol"; +import "forge-std/console2.sol"; + +/// @title ParameterSweepFuzzing +/// @notice Runs multiple parameter combinations in a single forge execution. +/// @dev Key design: environment is deployed ONCE per combo and reused across runs +/// so that VWAP accumulates naturally across recenters. +/// Swaps are uncapped by default (no LiquidityBoundaryHelper limits). +/// LM ETH is tracked as direct balance + pool WETH (since LM is sole LP, +/// pool WETH represents LM's deployed position ETH). +contract ParameterSweepFuzzing is Script { + TestEnvironment testEnv; + IUniswapV3Factory factory; + IUniswapV3Pool pool; + IWETH9 weth; + Kraiken kraiken; + Stake stake; + LiquidityManager lm; + SwapExecutor swapExecutor; + ConfigurableOptimizer optimizer; + bool token0isWeth; + + address trader = makeAddr("trader"); + address fees = makeAddr("fees"); + + string summaryFile; + + uint256 cfgBuyBias; + uint256 cfgTradesPerRun; + uint256 cfgRunsPerCombo; + uint256 cfgMinBuy; + uint256 cfgMaxBuy; + bool cfgUncapped; + + struct RunResult { + uint256 lmEthInit; + uint256 lmEthFinal; + uint256 traderInitEth; + uint256 traderFinalEth; + uint256 numRecenters; + uint256 numBuysFailed; + } + + function run() public { + cfgTradesPerRun = vm.envOr("TRADES_PER_RUN", uint256(30)); + cfgRunsPerCombo = vm.envOr("RUNS_PER_COMBO", uint256(5)); + cfgUncapped = vm.envOr("UNCAPPED_SWAPS", true); + cfgMinBuy = vm.envOr("MIN_BUY_ETH", uint256(20)); + cfgMaxBuy = vm.envOr("MAX_BUY_ETH", uint256(80)); + string memory tag = vm.envOr("SWEEP_TAG", string("SWEEP")); + + // Parse parameter arrays from comma-separated env vars + uint256[] memory ciValues = _parseUintArray(vm.envOr("CI_VALUES", string("0,500000000000000000,1000000000000000000"))); + uint256[] memory asValues = _parseUintArray(vm.envOr("AS_VALUES", string("100000000000000000,500000000000000000,1000000000000000000"))); + uint256[] memory awValues = _parseUintArray(vm.envOr("AW_VALUES", string("30,50,80"))); + uint256[] memory ddValues = _parseUintArray(vm.envOr("DD_VALUES", string("200000000000000000,1000000000000000000"))); + uint256[] memory bbValues = _parseUintArray(vm.envOr("BB_VALUES", string("60,80,100"))); + + uint256 totalCombos = ciValues.length * asValues.length * awValues.length * ddValues.length * bbValues.length; + console2.log("=== Parameter Sweep ==="); + console2.log("Combinations:", totalCombos); + console2.log("Runs/combo:", cfgRunsPerCombo); + console2.log("Trades/run:", cfgTradesPerRun); + console2.log("Uncapped:", cfgUncapped); + console2.log("Trade range (ETH):", cfgMinBuy, cfgMaxBuy); + + testEnv = new TestEnvironment(fees); + factory = UniswapHelpers.deployUniswapFactory(); + + summaryFile = string(abi.encodePacked("analysis/sweep-", tag, "-summary.csv")); + vm.writeFile(summaryFile, "ci,anchor_share,anchor_width,discovery_depth,buy_bias,max_trader_pnl,min_trader_pnl,any_lm_loss,lm_eth_delta\n"); + + uint256 comboIndex = 0; + for (uint256 ci_i = 0; ci_i < ciValues.length; ci_i++) { + for (uint256 as_i = 0; as_i < asValues.length; as_i++) { + for (uint256 aw_i = 0; aw_i < awValues.length; aw_i++) { + for (uint256 dd_i = 0; dd_i < ddValues.length; dd_i++) { + for (uint256 bb_i = 0; bb_i < bbValues.length; bb_i++) { + comboIndex++; + _runCombo( + ciValues[ci_i], asValues[as_i], uint24(awValues[aw_i] > 100 ? 100 : awValues[aw_i]), ddValues[dd_i], bbValues[bb_i], comboIndex + ); + } + } + } + } + } + + console2.log("=== Sweep Complete ==="); + console2.log("Results:", summaryFile); + } + + function _runCombo(uint256 ci, uint256 as_, uint24 aw, uint256 dd, uint256 bb, uint256 comboIdx) internal { + // Deploy optimizer and environment ONCE per combo + optimizer = new ConfigurableOptimizer(ci, as_, aw, dd); + cfgBuyBias = bb; + + // Setup environment once — VWAP accumulates across all runs + _setupEnvironment(comboIdx % 2 == 0); + + int256 maxPnl = type(int256).min; + int256 minPnl = type(int256).max; + bool anyLoss = false; + + // Track total LM ETH = direct balance + pool WETH (LM's deployed positions) + // address(lm).balance + weth.balanceOf(lm) is near-zero after recenter since ETH is in pool + uint256 lmSystemStart = _getLmSystemEth(); + + for (uint256 runIdx = 0; runIdx < cfgRunsPerCombo; runIdx++) { + RunResult memory result = _executeRun(runIdx); + + int256 pnl = int256(result.traderFinalEth) - int256(result.traderInitEth); + + if (pnl > maxPnl) maxPnl = pnl; + if (pnl < minPnl) minPnl = pnl; + if (pnl > 0) anyLoss = true; + } + + uint256 lmSystemEnd = _getLmSystemEth(); + int256 lmDelta = int256(lmSystemEnd) - int256(lmSystemStart); + + // Write summary + string memory row = string(abi.encodePacked(vm.toString(ci), ",", vm.toString(as_), ",", vm.toString(uint256(aw)), ",")); + row = string(abi.encodePacked(row, vm.toString(dd), ",", vm.toString(bb), ",", vm.toString(maxPnl), ",")); + row = string(abi.encodePacked(row, vm.toString(minPnl), ",", anyLoss ? "true" : "false", ",", vm.toString(lmDelta))); + vm.writeLine(summaryFile, row); + + // Log progress + if (anyLoss) { + console2.log("UNSAFE combo", comboIdx); + console2.log(" maxPnl:", maxPnl); + console2.log(" lmDelta:", lmDelta); + } else if (comboIdx % 5 == 0) { + console2.log("Progress:", comboIdx); + } + } + + /// @notice Approximate total ETH attributable to the LM (direct balance + pool WETH) + /// @dev Pool WETH includes both LM position ETH and trader buy ETH in transit. + /// This is a rough proxy — trader PnL is the reliable safety metric. + function _getLmSystemEth() internal view returns (uint256) { + return address(lm).balance + weth.balanceOf(address(lm)) + weth.balanceOf(address(pool)); + } + + function _executeRun(uint256 runIndex) internal returns (RunResult memory result) { + // DON'T re-setup environment — reuse existing so VWAP accumulates + result.lmEthInit = _getLmSystemEth(); + + // Fund trader fresh each run + vm.deal(trader, 400 ether); + vm.prank(trader); + weth.deposit{ value: 200 ether }(); + result.traderInitEth = weth.balanceOf(trader); + + for (uint256 i = 0; i < cfgTradesPerRun; i++) { + if (uint256(keccak256(abi.encodePacked(runIndex, i, "recenter"))) % 3 == 0) { + if (_tryRecenter()) result.numRecenters++; + } + + uint256 rand = uint256(keccak256(abi.encodePacked(runIndex, i))) % 100; + if (rand < cfgBuyBias) { + if (!_executeBuy(runIndex, i)) result.numBuysFailed++; + } else { + _executeSell(runIndex, i); + } + } + + _tryRecenter(); + _liquidateTraderHoldings(); + + result.lmEthFinal = _getLmSystemEth(); + result.traderFinalEth = weth.balanceOf(trader); + } + + function _setupEnvironment(bool wethIsToken0) internal { + (factory, pool, weth, kraiken, stake, lm,, token0isWeth) = testEnv.setupEnvironmentWithExistingFactory(factory, wethIsToken0, fees, address(optimizer)); + + swapExecutor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm, cfgUncapped); + + vm.deal(address(lm), 200 ether); + vm.prank(address(lm)); + weth.deposit{ value: 100 ether }(); + + vm.prank(fees); + try lm.recenter() { } catch { } + } + + function _executeBuy(uint256 runIndex, uint256 tradeIndex) internal returns (bool) { + uint256 range = cfgMaxBuy - cfgMinBuy; + uint256 amount = (cfgMinBuy * 1 ether) + (uint256(keccak256(abi.encodePacked(runIndex, tradeIndex, "buy"))) % (range * 1 ether)); + if (weth.balanceOf(trader) < amount) return false; + + vm.startPrank(trader); + weth.transfer(address(swapExecutor), amount); + try swapExecutor.executeBuy(amount, trader) returns (uint256 actualAmount) { + vm.stopPrank(); + return actualAmount > 0; + } catch { + vm.stopPrank(); + return false; + } + } + + function _executeSell(uint256 runIndex, uint256 tradeIndex) internal returns (bool) { + uint256 kraikenBal = kraiken.balanceOf(trader); + if (kraikenBal == 0) return false; + + // Sell 20-80% of holdings + uint256 pct = 20 + (uint256(keccak256(abi.encodePacked(runIndex, tradeIndex, "sell"))) % 60); + uint256 amount = kraikenBal * pct / 100; + if (amount == 0) return false; + + vm.startPrank(trader); + kraiken.transfer(address(swapExecutor), amount); + try swapExecutor.executeSell(amount, trader) returns (uint256 actualAmount) { + vm.stopPrank(); + return actualAmount > 0; + } catch { + vm.stopPrank(); + return false; + } + } + + function _tryRecenter() internal returns (bool) { + vm.warp(block.timestamp + 1 hours); + vm.roll(block.number + 1); + vm.prank(fees); + try lm.recenter{ gas: 50_000_000 }() { + return true; + } catch { + return false; + } + } + + function _liquidateTraderHoldings() internal { + uint256 remaining = kraiken.balanceOf(trader); + uint256 attempts; + while (remaining > 0 && attempts < 20) { + uint256 prev = remaining; + vm.startPrank(trader); + kraiken.transfer(address(swapExecutor), remaining); + try swapExecutor.executeSell(remaining, trader) returns (uint256 a) { + vm.stopPrank(); + if (a == 0) break; + } catch { + vm.stopPrank(); + break; + } + + // Recenter between attempts to unlock more sell liquidity + if (attempts % 3 == 2) { + _tryRecenter(); + } + + remaining = kraiken.balanceOf(trader); + if (remaining >= prev) break; + unchecked { + attempts++; + } + } + } + + /// @notice Parse comma-separated uint string into array + function _parseUintArray(string memory csv) internal pure returns (uint256[] memory) { + bytes memory b = bytes(csv); + uint256 count = 1; + for (uint256 i = 0; i < b.length; i++) { + if (b[i] == ",") count++; + } + + uint256[] memory result = new uint256[](count); + uint256 idx = 0; + uint256 start = 0; + + for (uint256 i = 0; i <= b.length; i++) { + if (i == b.length || b[i] == ",") { + uint256 num = 0; + for (uint256 j = start; j < i; j++) { + require(b[j] >= "0" && b[j] <= "9", "Invalid number"); + num = num * 10 + (uint256(uint8(b[j])) - 48); + } + result[idx] = num; + idx++; + start = i + 1; + } + } + + return result; + } +} diff --git a/onchain/analysis/StreamlinedFuzzing.s.sol b/onchain/analysis/StreamlinedFuzzing.s.sol index 86b4c6b..2688814 100644 --- a/onchain/analysis/StreamlinedFuzzing.s.sol +++ b/onchain/analysis/StreamlinedFuzzing.s.sol @@ -1,25 +1,29 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +import { Kraiken } from "../src/Kraiken.sol"; + +import { LiquidityManager } from "../src/LiquidityManager.sol"; + +import { Optimizer } from "../src/Optimizer.sol"; +import { Stake } from "../src/Stake.sol"; +import { ThreePositionStrategy } from "../src/abstracts/ThreePositionStrategy.sol"; +import { UniswapHelpers } from "../src/helpers/UniswapHelpers.sol"; +import { IWETH9 } from "../src/interfaces/IWETH9.sol"; +import { TestEnvironment } from "../test/helpers/TestBase.sol"; + +import { BearMarketOptimizer } from "../test/mocks/BearMarketOptimizer.sol"; +import { BullMarketOptimizer } from "../test/mocks/BullMarketOptimizer.sol"; + +import { ExtremeOptimizer } from "../test/mocks/ExtremeOptimizer.sol"; +import { MaliciousOptimizer } from "../test/mocks/MaliciousOptimizer.sol"; +import { NeutralMarketOptimizer } from "../test/mocks/NeutralMarketOptimizer.sol"; +import { WhaleOptimizer } from "../test/mocks/WhaleOptimizer.sol"; +import { SwapExecutor } from "./helpers/SwapExecutor.sol"; +import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; +import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; import "forge-std/Script.sol"; import "forge-std/console2.sol"; -import {TestEnvironment} from "../test/helpers/TestBase.sol"; -import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; -import {IUniswapV3Factory} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; -import {UniswapHelpers} from "../src/helpers/UniswapHelpers.sol"; -import {IWETH9} from "../src/interfaces/IWETH9.sol"; -import {Kraiken} from "../src/Kraiken.sol"; -import {Stake} from "../src/Stake.sol"; -import {LiquidityManager} from "../src/LiquidityManager.sol"; -import {ThreePositionStrategy} from "../src/abstracts/ThreePositionStrategy.sol"; -import {SwapExecutor} from "./helpers/SwapExecutor.sol"; -import {Optimizer} from "../src/Optimizer.sol"; -import {BullMarketOptimizer} from "../test/mocks/BullMarketOptimizer.sol"; -import {BearMarketOptimizer} from "../test/mocks/BearMarketOptimizer.sol"; -import {NeutralMarketOptimizer} from "../test/mocks/NeutralMarketOptimizer.sol"; -import {WhaleOptimizer} from "../test/mocks/WhaleOptimizer.sol"; -import {ExtremeOptimizer} from "../test/mocks/ExtremeOptimizer.sol"; -import {MaliciousOptimizer} from "../test/mocks/MaliciousOptimizer.sol"; contract StreamlinedFuzzing is Script { // Test environment @@ -32,77 +36,101 @@ contract StreamlinedFuzzing is Script { LiquidityManager lm; SwapExecutor swapExecutor; bool token0isWeth; - + // Actors address trader = makeAddr("trader"); + address staker = makeAddr("staker"); address fees = makeAddr("fees"); - + // Staking tracking - mapping(address => uint256[]) public activePositions; - uint256[] public allPositionIds; + uint256[] public activePositionIds; uint256 totalStakesAttempted; uint256 totalStakesSucceeded; - uint256 totalSnatchesAttempted; - uint256 totalSnatchesSucceeded; - + // CSV filename for current run string csvFilename; - + // Track cumulative fees uint256 totalFees0; uint256 totalFees1; - + // Track recentering uint256 lastRecenterBlock; - + + // Config + uint256 cfgBuyBias; + uint256 cfgMinBuy; + uint256 cfgMaxBuy; + bool cfgUncapped; + function run() public { // Get configuration from environment uint256 numRuns = vm.envOr("FUZZING_RUNS", uint256(20)); uint256 tradesPerRun = vm.envOr("TRADES_PER_RUN", uint256(15)); bool enableStaking = vm.envOr("ENABLE_STAKING", true); - uint256 buyBias = vm.envOr("BUY_BIAS", uint256(50)); + cfgBuyBias = vm.envOr("BUY_BIAS", uint256(50)); uint256 stakingBias = vm.envOr("STAKING_BIAS", uint256(80)); string memory optimizerClass = vm.envOr("OPTIMIZER_CLASS", string("BullMarketOptimizer")); - + cfgUncapped = vm.envOr("UNCAPPED_SWAPS", true); + cfgMinBuy = vm.envOr("MIN_BUY_ETH", uint256(20)); + cfgMaxBuy = vm.envOr("MAX_BUY_ETH", uint256(80)); + console2.log("=== Streamlined Fuzzing Analysis ==="); console2.log("Optimizer:", optimizerClass); - - // Deploy factory once for all runs (gas optimization) + console2.log("Uncapped swaps:", cfgUncapped); + console2.log("Trade range (ETH):", cfgMinBuy, cfgMaxBuy); + + // Deploy factory once testEnv = new TestEnvironment(fees); factory = UniswapHelpers.deployUniswapFactory(); - + // Generate unique 4-character scenario ID string memory scenarioCode = _generateScenarioId(); - - // Run fuzzing scenarios + + // Setup environment ONCE — all runs share it so VWAP accumulates + _setupEnvironment(optimizerClass, true); + + // Track LM ETH across the entire session + uint256 lmEthStart = address(lm).balance + weth.balanceOf(address(lm)) + weth.balanceOf(address(pool)); + console2.log("LM starting ETH:", lmEthStart / 1e18, "ETH"); + + // Run fuzzing scenarios — same environment, VWAP carries over for (uint256 runIndex = 0; runIndex < numRuns; runIndex++) { string memory runId = string(abi.encodePacked(scenarioCode, "-", _padNumber(runIndex, 3))); console2.log("\nRun:", runId); - + // Initialize CSV file for this run - // Always write to analysis directory relative to project root csvFilename = string(abi.encodePacked("analysis/fuzz-", runId, ".csv")); - string memory header = "action,amount,tick,floor_lower,floor_upper,floor_liq,anchor_lower,anchor_upper,anchor_liq,discovery_lower,discovery_upper,discovery_liq,eth_balance,kraiken_balance,vwap,fees_eth,fees_kraiken,recenter\n"; + string memory header = + "action,amount,tick,floor_lower,floor_upper,floor_liq,anchor_lower,anchor_upper,anchor_liq,discovery_lower,discovery_upper,discovery_liq,eth_balance,kraiken_balance,vwap,fees_eth,fees_kraiken,recenter\n"; vm.writeFile(csvFilename, header); - - // Setup fresh environment for each run - _setupEnvironment(optimizerClass, runIndex % 2 == 0); - - // Reset tracking variables + + // Reset tracking for CSV totalFees0 = 0; totalFees1 = 0; lastRecenterBlock = block.number; - - // Fund trader based on run seed - increased for longer campaigns - uint256 traderFund = 500 ether + (uint256(keccak256(abi.encodePacked(runIndex, "trader"))) % 500 ether); - vm.deal(trader, traderFund * 2); + // Reset trader — burn any leftover WETH/ETH from previous run + uint256 leftoverWeth = weth.balanceOf(trader); + if (leftoverWeth > 0) { + vm.prank(trader); + weth.transfer(address(0xdead), leftoverWeth); + } + uint256 leftoverKraiken = kraiken.balanceOf(trader); + if (leftoverKraiken > 0) { + vm.prank(trader); + kraiken.transfer(address(0xdead), leftoverKraiken); + } + + // Fund trader fresh for this run + uint256 traderFund = 200 ether; + vm.deal(trader, traderFund); vm.prank(trader); - weth.deposit{value: traderFund}(); - + weth.deposit{ value: traderFund }(); + // Initial state _recordState("INIT", 0); - + // Execute trades for (uint256 i = 0; i < tradesPerRun; i++) { // Check for recenter opportunity on average every 3 trades @@ -110,51 +138,67 @@ contract StreamlinedFuzzing is Script { if (recenterRand == 0) { _tryRecenter(); } - + // Determine trade based on bias uint256 rand = uint256(keccak256(abi.encodePacked(runIndex, i))) % 100; - - if (rand < buyBias) { + + if (rand < cfgBuyBias) { _executeBuy(runIndex, i); } else { _executeSell(runIndex, i); } - + // Staking operations if enabled - if (enableStaking && i % 3 == 0) { + if (enableStaking && i % 5 == 0) { _executeStakingOperation(runIndex, i, stakingBias); } } - // Final state + // Final recenter + liquidate + _tryRecenter(); _liquidateTraderHoldings(); _recordState("FINAL", 0); + + // Report per-run PnL + uint256 traderEthNow = weth.balanceOf(trader); + uint256 lmEthNow = address(lm).balance + weth.balanceOf(address(lm)) + weth.balanceOf(address(pool)); + if (traderEthNow > traderFund) { + console2.log(" TRADER PROFIT:", (traderEthNow - traderFund) / 1e15, "finney"); + } + console2.log(" LM ETH (wei):", lmEthNow, _signedDelta(lmEthNow, lmEthStart)); } + uint256 lmEthEnd = address(lm).balance + weth.balanceOf(address(lm)) + weth.balanceOf(address(pool)); console2.log("\n=== Analysis Complete ==="); + console2.log("LM ETH start:", lmEthStart / 1e18, "final (ETH):", lmEthEnd / 1e18); + if (lmEthEnd < lmEthStart) { + console2.log("LM LOST ETH:", (lmEthStart - lmEthEnd) / 1e15, "finney"); + } console2.log("Generated", numRuns, "CSV files with prefix:", scenarioCode); } - + + function _signedDelta(uint256 current, uint256 start) internal pure returns (string memory) { + if (current >= start) { + return string(abi.encodePacked("+", vm.toString((current - start) / 1e15), " finney")); + } else { + return string(abi.encodePacked("-", vm.toString((start - current) / 1e15), " finney")); + } + } + function _setupEnvironment(string memory optimizerClass, bool wethIsToken0) internal { - // Get optimizer address address optimizer = _deployOptimizer(optimizerClass); - - // Setup new environment - (factory, pool, weth, kraiken, stake, lm,, token0isWeth) = - testEnv.setupEnvironmentWithExistingFactory(factory, wethIsToken0, fees, optimizer); - - // Deploy swap executor with liquidity boundary checks - swapExecutor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm); - + + (factory, pool, weth, kraiken, stake, lm,, token0isWeth) = testEnv.setupEnvironmentWithExistingFactory(factory, wethIsToken0, fees, optimizer); + + // Deploy swap executor — uncapped by default for exploit testing + swapExecutor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm, cfgUncapped); + // Fund liquidity manager vm.deal(address(lm), 200 ether); - - // Initialize liquidity positions - // First need to give LM some WETH vm.prank(address(lm)); - weth.deposit{value: 100 ether}(); - - // Now try recenter from fee destination + weth.deposit{ value: 100 ether }(); + + // Initial recenter to set positions vm.prank(fees); try lm.recenter() returns (bool isUp) { console2.log("Initial recenter successful, isUp:", isUp); @@ -163,15 +207,8 @@ contract StreamlinedFuzzing is Script { } catch { console2.log("Initial recenter failed with unknown error"); } - - // Clear staking state - delete allPositionIds; - totalStakesAttempted = 0; - totalStakesSucceeded = 0; - totalSnatchesAttempted = 0; - totalSnatchesSucceeded = 0; } - + function _deployOptimizer(string memory optimizerClass) internal returns (address) { if (keccak256(bytes(optimizerClass)) == keccak256(bytes("BullMarketOptimizer"))) { return address(new BullMarketOptimizer()); @@ -186,33 +223,37 @@ contract StreamlinedFuzzing is Script { } else if (keccak256(bytes(optimizerClass)) == keccak256(bytes("MaliciousOptimizer"))) { return address(new MaliciousOptimizer()); } else { - // Default to bull market return address(new BullMarketOptimizer()); } } - + function _executeBuy(uint256 runIndex, uint256 tradeIndex) internal { - uint256 amount = _getTradeAmount(runIndex, tradeIndex, true); + uint256 range = cfgMaxBuy - cfgMinBuy; + uint256 amount = (cfgMinBuy * 1 ether) + (uint256(keccak256(abi.encodePacked(runIndex, tradeIndex, "buy"))) % (range * 1 ether)); if (amount == 0 || weth.balanceOf(trader) < amount) return; - + vm.startPrank(trader); weth.transfer(address(swapExecutor), amount); try swapExecutor.executeBuy(amount, trader) returns (uint256 actualAmount) { if (actualAmount == 0) { - console2.log("Buy returned 0, requested:", amount); + console2.log("Buy returned 0, requested:", amount / 1e18); } _recordState("BUY", actualAmount); - } catch Error(string memory reason) { - console2.log("Buy failed:", reason); + } catch { _recordState("BUY_FAIL", amount); } vm.stopPrank(); } - + function _executeSell(uint256 runIndex, uint256 tradeIndex) internal { - uint256 amount = _getTradeAmount(runIndex, tradeIndex, false); - if (amount == 0 || kraiken.balanceOf(trader) < amount) return; - + uint256 kraikenBal = kraiken.balanceOf(trader); + if (kraikenBal == 0) return; + + // Sell 20-80% of holdings + uint256 pct = 20 + (uint256(keccak256(abi.encodePacked(runIndex, tradeIndex, "sell"))) % 60); + uint256 amount = kraikenBal * pct / 100; + if (amount == 0) return; + vm.startPrank(trader); kraiken.transfer(address(swapExecutor), amount); try swapExecutor.executeSell(amount, trader) returns (uint256 actualAmount) { @@ -222,33 +263,79 @@ contract StreamlinedFuzzing is Script { } vm.stopPrank(); } - - function _executeStakingOperation(uint256, uint256, uint256) internal { - // Staking operations disabled for now - interface needs updating - // TODO: Update to use correct Stake contract interface + + function _executeStakingOperation(uint256 runIndex, uint256 tradeIndex, uint256 stakingBias) internal { + // Need KRAIKEN to stake — use the staker address, not the trader + uint256 stakerKraiken = kraiken.balanceOf(staker); + + // If staker has no KRAIKEN and trader has some, transfer a small amount + if (stakerKraiken == 0) { + uint256 traderBal = kraiken.balanceOf(trader); + if (traderBal == 0) return; + uint256 toTransfer = traderBal / 10; // 10% of trader holdings + if (toTransfer == 0) return; + vm.prank(trader); + kraiken.transfer(staker, toTransfer); + stakerKraiken = toTransfer; + } + + uint256 rand = uint256(keccak256(abi.encodePacked(runIndex, tradeIndex, "staking"))) % 100; + + if (rand < stakingBias && activePositionIds.length == 0) { + // Stake: pick a tax rate (0-15 range, modest) + uint32 taxRate = uint32(uint256(keccak256(abi.encodePacked(runIndex, tradeIndex, "taxrate"))) % 16); + uint256 stakeAmount = stakerKraiken / 2; + + // Check minStake + try kraiken.minStake() returns (uint256 minStake) { + if (stakeAmount < minStake) { + if (stakerKraiken >= minStake) { + stakeAmount = minStake; + } else { + return; + } + } + } catch { + return; + } + + totalStakesAttempted++; + vm.startPrank(staker); + kraiken.approve(address(stake), stakeAmount); + uint256[] memory empty = new uint256[](0); + try stake.snatch(stakeAmount, staker, taxRate, empty) returns (uint256 positionId) { + activePositionIds.push(positionId); + totalStakesSucceeded++; + } catch { } + vm.stopPrank(); + } else if (activePositionIds.length > 0) { + // Exit a random position + uint256 idx = uint256(keccak256(abi.encodePacked(runIndex, tradeIndex, "exit"))) % activePositionIds.length; + uint256 posId = activePositionIds[idx]; + vm.prank(staker); + try stake.exitPosition(posId) { + // Remove from tracking + activePositionIds[idx] = activePositionIds[activePositionIds.length - 1]; + activePositionIds.pop(); + } catch { } + } } - + function _tryRecenter() internal { vm.warp(block.timestamp + 1 hours); - vm.roll(block.number + 1); // Advance block + vm.roll(block.number + 1); vm.prank(fees); - try lm.recenter{gas: 50_000_000}() { + try lm.recenter{ gas: 50_000_000 }() { lastRecenterBlock = block.number; _recordState("RECENTER", 0); - } catch {} - } - - function _getTradeAmount(uint256 runIndex, uint256 tradeIndex, bool isBuy) internal pure returns (uint256) { - uint256 baseAmount = 10 ether + (uint256(keccak256(abi.encodePacked(runIndex, tradeIndex))) % 90 ether); - return isBuy ? baseAmount : baseAmount * 1000; + } catch { } } function _liquidateTraderHoldings() internal { uint256 remaining = kraiken.balanceOf(trader); uint256 attempts; - // Repeatedly sell down inventory, respecting liquidity limits in SwapExecutor - while (remaining > 0 && attempts < 10) { + while (remaining > 0 && attempts < 20) { uint256 prevRemaining = remaining; vm.startPrank(trader); @@ -256,127 +343,128 @@ contract StreamlinedFuzzing is Script { try swapExecutor.executeSell(remaining, trader) returns (uint256 actualAmount) { if (actualAmount == 0) { vm.stopPrank(); - console2.log("Liquidity liquidation halted: sell returned 0"); break; } - } catch Error(string memory reason) { - vm.stopPrank(); - console2.log("Liquidity liquidation failed:", reason); - break; } catch { vm.stopPrank(); - console2.log("Liquidity liquidation failed with unknown error"); break; } vm.stopPrank(); - remaining = kraiken.balanceOf(trader); - if (remaining >= prevRemaining) { - console2.log("Liquidity liquidation made no progress; remaining KRAIKEN:", remaining); - break; + // Recenter between liquidation attempts to unlock more liquidity + if (attempts % 3 == 2) { + _tryRecenter(); } + remaining = kraiken.balanceOf(trader); + if (remaining >= prevRemaining) break; + unchecked { attempts++; } } - - if (kraiken.balanceOf(trader) > 0) { - console2.log("Warning: trader still holds KRAIKEN after liquidation:", kraiken.balanceOf(trader)); - } } function _recordState(string memory action, uint256 amount) internal { - // Build CSV row in parts to avoid stack too deep string memory row = _buildRowPart1(action, amount); row = string(abi.encodePacked(row, _buildRowPart2())); row = string(abi.encodePacked(row, _buildRowPart3())); - + vm.writeLine(csvFilename, row); } - + function _buildRowPart1(string memory action, uint256 amount) internal view returns (string memory) { (, int24 tick,,,,,) = pool.slot0(); - - // Get floor position + (uint128 floorLiq, int24 floorLower, int24 floorUpper) = lm.positions(ThreePositionStrategy.Stage.FLOOR); - - return string(abi.encodePacked( - action, ",", - vm.toString(amount), ",", - vm.toString(tick), ",", - vm.toString(floorLower), ",", - vm.toString(floorUpper), ",", - vm.toString(uint256(floorLiq)), "," - )); + + return string( + abi.encodePacked( + action, + ",", + vm.toString(amount), + ",", + vm.toString(tick), + ",", + vm.toString(floorLower), + ",", + vm.toString(floorUpper), + ",", + vm.toString(uint256(floorLiq)), + "," + ) + ); } - + function _buildRowPart2() internal view returns (string memory) { - // Get anchor and discovery positions (uint128 anchorLiq, int24 anchorLower, int24 anchorUpper) = lm.positions(ThreePositionStrategy.Stage.ANCHOR); (uint128 discoveryLiq, int24 discoveryLower, int24 discoveryUpper) = lm.positions(ThreePositionStrategy.Stage.DISCOVERY); - - return string(abi.encodePacked( - vm.toString(anchorLower), ",", - vm.toString(anchorUpper), ",", - vm.toString(uint256(anchorLiq)), ",", - vm.toString(discoveryLower), ",", - vm.toString(discoveryUpper), ",", - vm.toString(uint256(discoveryLiq)), "," - )); + + return string( + abi.encodePacked( + vm.toString(anchorLower), + ",", + vm.toString(anchorUpper), + ",", + vm.toString(uint256(anchorLiq)), + ",", + vm.toString(discoveryLower), + ",", + vm.toString(discoveryUpper), + ",", + vm.toString(uint256(discoveryLiq)), + "," + ) + ); } - + function _buildRowPart3() internal view returns (string memory) { - // Get balances and fees uint256 ethBalance = weth.balanceOf(trader); uint256 kraikenBalance = kraiken.balanceOf(trader); - + (uint128 fees0, uint128 fees1) = pool.protocolFees(); uint256 deltaFees0 = fees0 > totalFees0 ? fees0 - totalFees0 : 0; uint256 deltaFees1 = fees1 > totalFees1 ? fees1 - totalFees1 : 0; - - return string(abi.encodePacked( - vm.toString(ethBalance), ",", - vm.toString(kraikenBalance), ",", - "0,", // vwap placeholder - vm.toString(deltaFees0), ",", - vm.toString(deltaFees1), ",", - "0" // recenter flag placeholder - no newline here - )); + + return string( + abi.encodePacked( + vm.toString(ethBalance), ",", vm.toString(kraikenBalance), ",", "0,", vm.toString(deltaFees0), ",", vm.toString(deltaFees1), ",", "0" + ) + ); } - + function _generateScenarioId() internal view returns (string memory) { uint256 rand = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao))); bytes memory chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; bytes memory result = new bytes(4); - + for (uint256 i = 0; i < 4; i++) { result[i] = chars[rand % chars.length]; rand = rand / chars.length; } - + return string(result); } - + function _padNumber(uint256 num, uint256 digits) internal pure returns (string memory) { string memory numStr = vm.toString(num); bytes memory numBytes = bytes(numStr); - + if (numBytes.length >= digits) { return numStr; } - + bytes memory result = new bytes(digits); uint256 padding = digits - numBytes.length; - + for (uint256 i = 0; i < padding; i++) { - result[i] = '0'; + result[i] = "0"; } - + for (uint256 i = 0; i < numBytes.length; i++) { result[padding + i] = numBytes[i]; } - + return string(result); } } diff --git a/onchain/analysis/helpers/SwapExecutor.sol b/onchain/analysis/helpers/SwapExecutor.sol index 1a8dd51..f3f87ea 100644 --- a/onchain/analysis/helpers/SwapExecutor.sol +++ b/onchain/analysis/helpers/SwapExecutor.sol @@ -1,17 +1,21 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.19; -import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; -import {IWETH9} from "../../src/interfaces/IWETH9.sol"; -import {Kraiken} from "../../src/Kraiken.sol"; -import {TickMath} from "@aperture/uni-v3-lib/TickMath.sol"; -import {LiquidityBoundaryHelper} from "../../test/helpers/LiquidityBoundaryHelper.sol"; -import {ThreePositionStrategy} from "../../src/abstracts/ThreePositionStrategy.sol"; +import { Kraiken } from "../../src/Kraiken.sol"; + +import { ThreePositionStrategy } from "../../src/abstracts/ThreePositionStrategy.sol"; +import { IWETH9 } from "../../src/interfaces/IWETH9.sol"; +import { LiquidityBoundaryHelper } from "../../test/helpers/LiquidityBoundaryHelper.sol"; +import { TickMath } from "@aperture/uni-v3-lib/TickMath.sol"; +import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol"; /** * @title SwapExecutor - * @notice Helper contract to execute swaps on Uniswap V3 pools for analysis scripts - * @dev Extracted from analysis scripts to avoid code duplication + * @notice Helper contract to execute swaps on Uniswap V3 pools for analysis scripts. + * @dev Supports two modes: + * - Capped (default): trade sizes limited by LiquidityBoundaryHelper + * - Uncapped: trades go directly to pool with no size caps, matching production behavior + * Set uncapped=true via constructor to allow trades through position boundaries. */ contract SwapExecutor { IUniswapV3Pool public pool; @@ -19,93 +23,55 @@ contract SwapExecutor { Kraiken public harberg; bool public token0isWeth; ThreePositionStrategy public liquidityManager; - - constructor(IUniswapV3Pool _pool, IWETH9 _weth, Kraiken _harberg, bool _token0isWeth, ThreePositionStrategy _liquidityManager) { + bool public uncapped; + + constructor(IUniswapV3Pool _pool, IWETH9 _weth, Kraiken _harberg, bool _token0isWeth, ThreePositionStrategy _liquidityManager, bool _uncapped) { pool = _pool; weth = _weth; harberg = _harberg; token0isWeth = _token0isWeth; liquidityManager = _liquidityManager; + uncapped = _uncapped; } - + function executeBuy(uint256 amount, address recipient) external returns (uint256) { - // Calculate maximum safe buy amount based on liquidity - uint256 maxBuyAmount = LiquidityBoundaryHelper.calculateBuyLimit(pool, liquidityManager, token0isWeth); - - // Cap the amount to the safe limit - uint256 safeAmount = amount > maxBuyAmount ? maxBuyAmount : amount; - - // Skip if amount is zero + uint256 safeAmount = amount; + + if (!uncapped) { + uint256 maxBuyAmount = LiquidityBoundaryHelper.calculateBuyLimit(pool, liquidityManager, token0isWeth); + safeAmount = amount > maxBuyAmount ? maxBuyAmount : amount; + } + if (safeAmount == 0) return 0; - - // For buying HARB with WETH, we're swapping in the direction that increases HARB price - // zeroForOne = true if WETH is token0, false if WETH is token1 + bool zeroForOne = token0isWeth; - - // Set appropriate price limit based on swap direction - uint160 sqrtPriceLimitX96; - if (zeroForOne) { - // Price goes down (in terms of token0/token1 ratio) - sqrtPriceLimitX96 = TickMath.MIN_SQRT_RATIO + 1; - } else { - // Price goes up - sqrtPriceLimitX96 = TickMath.MAX_SQRT_RATIO - 1; - } - - pool.swap( - recipient, - zeroForOne, - int256(safeAmount), - sqrtPriceLimitX96, - "" - ); - + uint160 sqrtPriceLimitX96 = zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1; + + pool.swap(recipient, zeroForOne, int256(safeAmount), sqrtPriceLimitX96, ""); return safeAmount; } - + function executeSell(uint256 amount, address recipient) external returns (uint256) { - // Calculate maximum safe sell amount based on liquidity - uint256 maxSellAmount = LiquidityBoundaryHelper.calculateSellLimit(pool, liquidityManager, token0isWeth); - - // Cap the amount to the safe limit - uint256 safeAmount = amount > maxSellAmount ? maxSellAmount : amount; - - // Skip if amount is zero - if (safeAmount == 0) return 0; - - // For selling HARB for WETH, we're swapping in the direction that decreases HARB price - // zeroForOne = false if WETH is token0, true if WETH is token1 - bool zeroForOne = !token0isWeth; - - // Set appropriate price limit based on swap direction - uint160 sqrtPriceLimitX96; - if (zeroForOne) { - // Price goes down (in terms of token0/token1 ratio) - sqrtPriceLimitX96 = TickMath.MIN_SQRT_RATIO + 1; - } else { - // Price goes up - sqrtPriceLimitX96 = TickMath.MAX_SQRT_RATIO - 1; + uint256 safeAmount = amount; + + if (!uncapped) { + uint256 maxSellAmount = LiquidityBoundaryHelper.calculateSellLimit(pool, liquidityManager, token0isWeth); + safeAmount = amount > maxSellAmount ? maxSellAmount : amount; } - - pool.swap( - recipient, - zeroForOne, - int256(safeAmount), - sqrtPriceLimitX96, - "" - ); - + + if (safeAmount == 0) return 0; + + bool zeroForOne = !token0isWeth; + uint160 sqrtPriceLimitX96 = zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1; + + pool.swap(recipient, zeroForOne, int256(safeAmount), sqrtPriceLimitX96, ""); return safeAmount; } - + // Callback required for Uniswap V3 swaps - function uniswapV3SwapCallback( - int256 amount0Delta, - int256 amount1Delta, - bytes calldata - ) external { + function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata) external { require(msg.sender == address(pool), "Unauthorized callback"); - + if (amount0Delta > 0) { IWETH9(pool.token0()).transfer(address(pool), uint256(amount0Delta)); } @@ -113,4 +79,4 @@ contract SwapExecutor { IWETH9(pool.token1()).transfer(address(pool), uint256(amount1Delta)); } } -} \ No newline at end of file +} diff --git a/onchain/analysis/run-fuzzing.sh b/onchain/analysis/run-fuzzing.sh index f13ece8..825cf5d 100755 --- a/onchain/analysis/run-fuzzing.sh +++ b/onchain/analysis/run-fuzzing.sh @@ -1,10 +1,11 @@ #!/bin/bash -# Usage: ./run-fuzzing.sh [optimizer] [runs=N] [staking=on|off] [buybias=N] [trades=N] [stakingbias=N] [debugCSV] +# Usage: ./run-fuzzing.sh [optimizer] [key=value ...] [debugCSV] # Examples: # ./run-fuzzing.sh BullMarketOptimizer runs=50 # ./run-fuzzing.sh WhaleOptimizer runs=20 staking=off -# ./run-fuzzing.sh BullMarketOptimizer runs=200 staking=on buybias=100 trades=30 stakingbias=95 +# ./run-fuzzing.sh BullMarketOptimizer runs=10 trades=30 buybias=80 uncapped=true +# ./run-fuzzing.sh BullMarketOptimizer runs=10 trades=30 minbuy=30 maxbuy=70 # ./run-fuzzing.sh BullMarketOptimizer debugCSV # Opens HTML visualizer after generating CSVs # Colors for output @@ -15,61 +16,38 @@ BLUE='\033[0;34m' NC='\033[0m' # No Color BOLD='\033[1m' -# Configuration +# Configuration defaults OPTIMIZER=${1:-BullMarketOptimizer} - -# Check if second parameter is debugCSV -DEBUG_CSV=false -if [[ "$2" == "debugCSV" ]]; then - DEBUG_CSV=true - RUNS="runs=3" - STAKING="staking=on" - BUYBIAS="buybias=50" - TRADES="trades=5" - STAKINGBIAS="stakingbias=80" -else - RUNS=${2:-runs=20} - STAKING=${3:-staking=on} - BUYBIAS=${4:-buybias=50} - TRADES=${5:-trades=15} - STAKINGBIAS=${6:-stakingbias=80} -fi - -# Parse runs parameter -if [[ $RUNS == runs=* ]]; then - RUNS_VALUE=${RUNS#runs=} -else - RUNS_VALUE=$RUNS -fi - -# Parse staking parameter +RUNS_VALUE=20 STAKING_ENABLED="true" -if [[ $STAKING == staking=* ]]; then - STAKING_VALUE=${STAKING#staking=} - if [[ $STAKING_VALUE == "off" ]] || [[ $STAKING_VALUE == "false" ]] || [[ $STAKING_VALUE == "0" ]]; then - STAKING_ENABLED="false" - fi -fi - -# Parse buy bias parameter BUYBIAS_VALUE="50" -if [[ $BUYBIAS == buybias=* ]]; then - BUYBIAS_VALUE=${BUYBIAS#buybias=} -fi - -# Parse trades parameter TRADES_VALUE="15" -if [[ $TRADES == trades=* ]]; then - TRADES_VALUE=${TRADES#trades=} -fi - -# Parse staking bias parameter STAKINGBIAS_VALUE="80" -if [[ $STAKINGBIAS == stakingbias=* ]]; then - STAKINGBIAS_VALUE=${STAKINGBIAS#stakingbias=} -fi +UNCAPPED_VALUE="true" +MINBUY_VALUE="20" +MAXBUY_VALUE="80" +DEBUG_CSV=false -# Ensure we're in the onchain directory (script should be run from there) +# Parse arguments (skip first which is optimizer) +shift +for arg in "$@"; do + case "$arg" in + debugCSV) + DEBUG_CSV=true + RUNS_VALUE=3 + TRADES_VALUE=5 + ;; + runs=*) RUNS_VALUE=${arg#runs=} ;; + staking=off|staking=false|staking=0) STAKING_ENABLED="false" ;; + staking=*) STAKING_ENABLED="true" ;; + buybias=*) BUYBIAS_VALUE=${arg#buybias=} ;; + trades=*) TRADES_VALUE=${arg#trades=} ;; + stakingbias=*) STAKINGBIAS_VALUE=${arg#stakingbias=} ;; + uncapped=*) UNCAPPED_VALUE=${arg#uncapped=} ;; + minbuy=*) MINBUY_VALUE=${arg#minbuy=} ;; + maxbuy=*) MAXBUY_VALUE=${arg#maxbuy=} ;; + esac +done echo -e "${GREEN}=== Fuzzing Analysis ===${NC}" echo "Optimizer: $OPTIMIZER" @@ -80,6 +58,8 @@ if [ "$STAKING_ENABLED" = "true" ]; then echo "Staking bias: $STAKINGBIAS_VALUE%" fi echo "Buy bias: $BUYBIAS_VALUE%" +echo "Uncapped swaps: $UNCAPPED_VALUE" +echo "Buy range: ${MINBUY_VALUE}-${MAXBUY_VALUE} ETH" echo "" # Validate optimizer @@ -107,6 +87,9 @@ ENABLE_STAKING=$STAKING_ENABLED \ BUY_BIAS=$BUYBIAS_VALUE \ TRADES_PER_RUN=$TRADES_VALUE \ STAKING_BIAS=$STAKINGBIAS_VALUE \ +UNCAPPED_SWAPS=$UNCAPPED_VALUE \ +MIN_BUY_ETH=$MINBUY_VALUE \ +MAX_BUY_ETH=$MAXBUY_VALUE \ forge script analysis/StreamlinedFuzzing.s.sol:StreamlinedFuzzing --skip-simulation --gas-estimate-multiplier 300 -vv 2>&1 # Analysis complete @@ -117,7 +100,7 @@ echo -e "${GREEN}=== ANALYSIS COMPLETE ===${NC}" NEW_CSVS=$(ls -1 analysis/fuzz-????-???.csv 2>/dev/null | sort) # Get the scenario code from the first new file if [ "$EXISTING_CSVS" != "$NEW_CSVS" ]; then - SCENARIO_CODE=$(echo "$NEW_CSVS" | grep -v -F "$EXISTING_CSVS" | head -1 | sed 's/fuzz-\(....\).*/\1/') + SCENARIO_CODE=$(echo "$NEW_CSVS" | grep -v -F "$EXISTING_CSVS" | head -1 | sed 's/.*fuzz-\(....\).*/\1/') else SCENARIO_CODE="UNKN" fi @@ -135,9 +118,9 @@ for csv in analysis/fuzz-${SCENARIO_CODE}-*.csv; do # Get INIT and FINAL rows INIT_ETH=$(grep "^INIT," "$csv" | cut -d',' -f13) FINAL_ETH=$(grep "^FINAL," "$csv" | cut -d',' -f13) - + if [ ! -z "$INIT_ETH" ] && [ ! -z "$FINAL_ETH" ]; then - if [ "$FINAL_ETH" -gt "$INIT_ETH" ]; then + if [ "$FINAL_ETH" -gt "$INIT_ETH" ] 2>/dev/null; then PROFIT_PCT=$(echo "scale=1; ($FINAL_ETH - $INIT_ETH) * 100 / $INIT_ETH" | bc -l 2>/dev/null || echo "0") echo -e "${GREEN} $csv: PROFITABLE (+${PROFIT_PCT}%)${NC}" ((PROFITABLE_COUNT++)) @@ -159,7 +142,7 @@ echo -e "${GREEN}CSV files generated with scenario ID: ${SCENARIO_CODE}${NC}" if [ "$DEBUG_CSV" = true ]; then echo "" echo -e "${YELLOW}Launching HTML visualizer...${NC}" - + # Check if Python3 is available if command -v python3 &> /dev/null; then echo "Starting local server from analysis folder at http://localhost:8000" @@ -168,20 +151,17 @@ if [ "$DEBUG_CSV" = true ]; then cd analysis python3 -m http.server 8000 --bind 127.0.0.1 & SERVER_PID=$! - + # Try to open browser (cross-platform) sleep 1 if command -v open &> /dev/null; then - # macOS open "http://localhost:8000/run-visualizer.html" elif command -v xdg-open &> /dev/null; then - # Linux xdg-open "http://localhost:8000/run-visualizer.html" elif command -v wslview &> /dev/null; then - # WSL wslview "http://localhost:8000/run-visualizer.html" fi - + echo "" echo "Press Ctrl+C to stop the server" wait $SERVER_PID @@ -190,4 +170,4 @@ if [ "$DEBUG_CSV" = true ]; then echo " cd analysis && python3 -m http.server 8000" echo " Then open http://localhost:8000/run-visualizer.html in your browser" fi -fi \ No newline at end of file +fi diff --git a/onchain/analysis/run-parameter-sweep.sh b/onchain/analysis/run-parameter-sweep.sh new file mode 100755 index 0000000..2461841 --- /dev/null +++ b/onchain/analysis/run-parameter-sweep.sh @@ -0,0 +1,264 @@ +#!/bin/bash +# Parameter sweep for finding safe/unsafe optimizer parameter boundaries +# +# Usage: ./analysis/run-parameter-sweep.sh [mode] +# Modes: +# quick - Small focused grid (~5 min) +# standard - Medium grid (~30 min) +# boundary - Focused on exploitation boundary (~20 min) +# +# Run from onchain/ directory + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +MODE=${1:-standard} + +# Common: uncapped swaps for realistic exploitation testing +UNCAPPED="true" +MINBUY="20" +MAXBUY="80" + +case $MODE in + quick) + CI="0,500000000000000000,1000000000000000000" + AS="100000000000000000,500000000000000000,1000000000000000000" + AW="30,50,80" + DD="200000000000000000,1000000000000000000" + BB="80,100" + RUNS=3 + TRADES=20 + ;; + standard) + CI="0,200000000000000000,500000000000000000,800000000000000000,1000000000000000000" + AS="50000000000000000,100000000000000000,200000000000000000,500000000000000000,700000000000000000,1000000000000000000" + AW="20,50,80" + DD="200000000000000000,500000000000000000,1000000000000000000" + BB="80,100" + RUNS=5 + TRADES=30 + ;; + boundary) + # Focused sweep around known exploitation boundary: + # Bull (exploitable): CI=0, AS=1e18, AW=50, DD=1e18 + # Bear (mostly safe): CI=0.8e18, AS=0.2e18, AW=80+, DD=0.2e18 + # Key: anchorShare transition between 0.2 and 0.7 + CI="0,300000000000000000,500000000000000000,800000000000000000" + AS="100000000000000000,200000000000000000,300000000000000000,400000000000000000,500000000000000000,700000000000000000,1000000000000000000" + AW="30,50,80" + DD="200000000000000000,500000000000000000,1000000000000000000" + BB="80" + RUNS=5 + TRADES=30 + ;; + *) + echo "Unknown mode: $MODE (use: quick, standard, boundary)" + exit 1 + ;; +esac + +echo -e "${GREEN}=== KRAIKEN Parameter Sweep ($MODE mode) ===${NC}" +echo "Runs per combo: $RUNS | Trades per run: $TRADES" +echo "Uncapped: $UNCAPPED | Trade range: ${MINBUY}-${MAXBUY} ETH" + +# Master results +SUMMARY_FILE="analysis/sweep-FULL-summary.csv" +echo "ci,anchor_share,anchor_width,discovery_depth,buy_bias,max_trader_pnl,min_trader_pnl,any_lm_loss,lm_eth_delta" > "$SUMMARY_FILE" + +# Split into small batches: one forge call per (CI, AS, AW) triple +# Each call handles DD*BB combos to stay within gas limits +batch=0 +total_combos=0 +start_time=$(date +%s) + +for ci_val in $(echo "$CI" | tr ',' ' '); do + for as_val in $(echo "$AS" | tr ',' ' '); do + for aw_val in $(echo "$AW" | tr ',' ' '); do + batch=$((batch + 1)) + ci_pct=$(python3 -c "print(f'{int(\"$ci_val\")/1e18:.0%}')" 2>/dev/null || echo "$ci_val") + as_pct=$(python3 -c "print(f'{int(\"$as_val\")/1e18:.1%}')" 2>/dev/null || echo "$as_val") + + echo -ne "\r${BLUE}[$batch] CI=$ci_pct AS=$as_pct AW=$aw_val${NC} " + + CI_VALUES="$ci_val" \ + AS_VALUES="$as_val" \ + AW_VALUES="$aw_val" \ + DD_VALUES="$DD" \ + BB_VALUES="$BB" \ + RUNS_PER_COMBO="$RUNS" \ + TRADES_PER_RUN="$TRADES" \ + UNCAPPED_SWAPS="$UNCAPPED" \ + MIN_BUY_ETH="$MINBUY" \ + MAX_BUY_ETH="$MAXBUY" \ + SWEEP_TAG="B${batch}" \ + forge script analysis/ParameterSweepFuzzing.s.sol:ParameterSweepFuzzing \ + --skip-simulation --gas-estimate-multiplier 300 -vv 2>&1 | grep "UNSAFE" || true + + batch_csv="analysis/sweep-B${batch}-summary.csv" + if [ -f "$batch_csv" ]; then + new_lines=$(tail -n +2 "$batch_csv" | wc -l) + total_combos=$((total_combos + new_lines)) + tail -n +2 "$batch_csv" >> "$SUMMARY_FILE" + rm "$batch_csv" + fi + done + done +done + +elapsed=$(($(date +%s) - start_time)) +echo "" +echo "" +echo -e "${GREEN}=== ANALYSIS ===${NC}" +echo "Elapsed: ${elapsed}s" + +# Analyze results +total_tested=$(tail -n +2 "$SUMMARY_FILE" | wc -l | tr -d ' ') +unsafe_combos=$(tail -n +2 "$SUMMARY_FILE" | grep -c ',true,' || true) +unsafe_combos=${unsafe_combos:-0} +safe_combos=$((total_tested - unsafe_combos)) + +echo "Total tested: $total_tested" +echo -e "Safe: ${GREEN}$safe_combos${NC}" +echo -e "Unsafe: ${RED}$unsafe_combos${NC}" + +# Generate report +RESULTS_FILE="analysis/PARAMETER_SEARCH_RESULTS.md" + +cat > "$RESULTS_FILE" << EOF +# KRAIKEN Parameter Search Results + +## Objective +Find optimizer parameters where the LiquidityManager NEVER loses ETH regardless of trade sequence. + +## Methodology +- **Mode**: $MODE +- **Runs per parameter combination**: $RUNS +- **Trades per run**: $TRADES +- **Uncapped swaps**: $UNCAPPED (trades push through position boundaries) +- **Trade size range**: ${MINBUY}-${MAXBUY} ETH (vs 100 ETH LM balance) +- **VWAP accumulation**: Environment reused across runs per combo +- **Buy biases tested**: $(echo $BB | tr ',' ', ')% +- **Total combinations tested**: $total_tested +- **Elapsed time**: ${elapsed}s + +### Parameters Searched +| Parameter | Values | Description | +|-----------|--------|-------------| +| capitalInefficiency | $(python3 -c "print(', '.join(f'{int(x)/1e18:.0%}' for x in '$CI'.split(',')))" 2>/dev/null || echo "$CI") | Capital buffer (0=aggressive, 1e18=conservative) | +| anchorShare | $(python3 -c "print(', '.join(f'{int(x)/1e18:.1%}' for x in '$AS'.split(',')))" 2>/dev/null || echo "$AS") | ETH allocation to anchor vs floor | +| anchorWidth | $(echo $AW | tr ',' ', ') | Anchor position width (1-100) | +| discoveryDepth | $(python3 -c "print(', '.join(f'{int(x)/1e18:.0%}' for x in '$DD'.split(',')))" 2>/dev/null || echo "$DD") | Discovery liquidity density | +| buyBias | $(echo $BB | tr ',' ', ')% | Adversarial trade mix (80=buy-heavy, 100=buy-only) | + +### Key Metrics +- **Trader PnL < 0**: LM gained ETH (SAFE). Trader lost money. +- **Trader PnL > 0**: LM lost ETH (UNSAFE). Trader extracted ETH. + +## Results Summary +- **Safe combinations**: $safe_combos / $total_tested +- **Unsafe combinations**: $unsafe_combos / $total_tested +EOF + +if [ "$unsafe_combos" -gt 0 ]; then + { + echo "" + echo "## UNSAFE Parameters (Trader Profited = LM Lost ETH)" + echo "" + echo "| CI | AnchorShare | AnchorWidth | DiscoveryDepth | BuyBias | Max Trader PnL (ETH) |" + echo "|-----|-------------|-------------|----------------|---------|----------------------|" + + while IFS=, read -r ci as aw dd bb maxpnl minpnl loss lmdelta; do + if [[ "$loss" == "true" ]]; then + ci_pct=$(python3 -c "print(f'{int(\"$ci\")/1e18:.0%}')" 2>/dev/null || echo "$ci") + as_pct=$(python3 -c "print(f'{int(\"$as\")/1e18:.1%}')" 2>/dev/null || echo "$as") + dd_pct=$(python3 -c "print(f'{int(\"$dd\")/1e18:.0%}')" 2>/dev/null || echo "$dd") + maxpnl_eth=$(python3 -c "print(f'{int(\"$maxpnl\")/1e18:+.4f}')" 2>/dev/null || echo "$maxpnl") + echo "| $ci_pct | $as_pct | $aw | $dd_pct | $bb | $maxpnl_eth |" + fi + done < <(tail -n +2 "$SUMMARY_FILE") + } >> "$RESULTS_FILE" +fi + +{ + echo "" + echo "## SAFE Parameters" + echo "" +} >> "$RESULTS_FILE" + +if [ "$safe_combos" -gt 0 ]; then + { + echo "All tested parameter combinations where the LM never lost ETH:" + echo "" + echo "### Safe Ranges by Buy Bias" + echo "" + } >> "$RESULTS_FILE" + + for bb_val in $(echo "$BB" | tr ',' ' '); do + safe_at_bb=$(tail -n +2 "$SUMMARY_FILE" | grep ",$bb_val," | grep -c ',false,' || true) + safe_at_bb=${safe_at_bb:-0} + total_at_bb=$(tail -n +2 "$SUMMARY_FILE" | grep -c ",$bb_val," || true) + total_at_bb=${total_at_bb:-0} + echo "- **BuyBias=${bb_val}%**: ${safe_at_bb}/${total_at_bb} safe" >> "$RESULTS_FILE" + done + + { + echo "" + echo "### Top 20 Safest Combinations (most negative max trader PnL = hardest to exploit)" + echo "" + echo "| CI | AnchorShare | AnchorWidth | DiscoveryDepth | BuyBias | Max Trader PnL (ETH) |" + echo "|-----|-------------|-------------|----------------|---------|----------------------|" + } >> "$RESULTS_FILE" + + tail -n +2 "$SUMMARY_FILE" | grep ',false,' | sort -t, -k6 -n | head -20 | while IFS=, read -r ci as aw dd bb maxpnl minpnl loss lmdelta; do + ci_pct=$(python3 -c "print(f'{int(\"$ci\")/1e18:.0%}')" 2>/dev/null || echo "$ci") + as_pct=$(python3 -c "print(f'{int(\"$as\")/1e18:.1%}')" 2>/dev/null || echo "$as") + dd_pct=$(python3 -c "print(f'{int(\"$dd\")/1e18:.0%}')" 2>/dev/null || echo "$dd") + maxpnl_eth=$(python3 -c "print(f'{int(\"$maxpnl\")/1e18:+.4f}')" 2>/dev/null || echo "$maxpnl") + echo "| $ci_pct | $as_pct | $aw | $dd_pct | $bb | $maxpnl_eth |" + done >> "$RESULTS_FILE" +else + echo "No safe parameter combinations found." >> "$RESULTS_FILE" +fi + +{ + echo "" + echo "## Recommendations" + echo "" +} >> "$RESULTS_FILE" + +if [ "$unsafe_combos" -eq 0 ]; then + cat >> "$RESULTS_FILE" << 'RECO' +**All tested parameter combinations are SAFE.** The three-position strategy's asymmetric +slippage profile effectively prevents profitable arbitrage across all tested parameters. +RECO +else + cat >> "$RESULTS_FILE" << 'RECO' +**Some parameter combinations are UNSAFE.** The exploitation boundary depends primarily on: +1. **anchorShare**: Higher values allocate more ETH to the thin anchor position, reducing floor protection +2. **capitalInefficiency**: Lower values (0%) make the floor position more aggressive, increasing exploit risk +3. **anchorWidth**: Narrower widths concentrate anchor liquidity, making it cheaper to push through + +### Safe Zone +Parameters where the trader never profited across all tested scenarios. + +### Unsafe Zone +Parameters where at least one test run showed trader profit (= LM ETH loss). +The exploitation pattern is: buy→recenter→sell→recenter repeated over many cycles, +where VWAP accumulates at inflated prices and the floor gets positioned too high. +RECO +fi + +{ + echo "" + echo "---" + echo "*Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)*" +} >> "$RESULTS_FILE" + +echo "" +echo -e "${GREEN}Report: $RESULTS_FILE${NC}" +echo -e "${GREEN}Data: $SUMMARY_FILE${NC}" diff --git a/onchain/analysis/sweep-FULL-summary.csv b/onchain/analysis/sweep-FULL-summary.csv new file mode 100644 index 0000000..47f90a3 --- /dev/null +++ b/onchain/analysis/sweep-FULL-summary.csv @@ -0,0 +1,253 @@ +ci,anchor_share,anchor_width,discovery_depth,buy_bias,max_trader_pnl,min_trader_pnl,any_lm_loss,lm_eth_delta +0,100000000000000000,30,200000000000000000,80,44486458737952248326,-344363430578411493891,true,-169638741014798823362 +0,100000000000000000,30,500000000000000000,80,167054352470885235368,-77259177653417840988,true,-22435102158072601482 +0,100000000000000000,30,1000000000000000000,80,173816793379889528476,-59514356933507649733,true,-69940881107079455084 +0,100000000000000000,50,200000000000000000,80,195483692737516408951,-369152693533808532380,true,31759825248527646357 +0,100000000000000000,50,500000000000000000,80,151064658407102988593,-369225664334627951118,true,-127567825383992877106 +0,100000000000000000,50,1000000000000000000,80,32342469884268611741,-372912977224560262040,true,-101057520197727960231 +0,100000000000000000,80,200000000000000000,80,-114482208360718517702,-239725025920578087572,false,-183123902400890564257 +0,100000000000000000,80,500000000000000000,80,194910121464237681794,-404955705321896438825,true,574579522132729829104 +0,100000000000000000,80,1000000000000000000,80,142897475752009126708,-389487655450378923124,true,-193405299106155672834 +0,200000000000000000,30,200000000000000000,80,155187449165434975506,-81414371712601104428,true,-14471643115117013711 +0,200000000000000000,30,500000000000000000,80,163571941929097860168,-61891821190559980695,true,-59111909308481265028 +0,200000000000000000,30,1000000000000000000,80,165191274499760358170,-49196271006628913671,true,-91906039817996179609 +0,200000000000000000,50,200000000000000000,80,160782048458739948809,-389121911399725473143,true,-199716004946540170887 +0,200000000000000000,50,500000000000000000,80,45794784565606216985,-389791630630333203294,true,-164290408576398456600 +0,200000000000000000,50,1000000000000000000,80,158757786572777390466,-134404140735943866788,true,97343275988125971109 +0,200000000000000000,80,200000000000000000,80,194806372543088703936,-395126339452027380227,true,525330585266873287014 +0,200000000000000000,80,500000000000000000,80,195283444565288124583,-378934838255636561585,true,94461047632773348276 +0,200000000000000000,80,1000000000000000000,80,111927359456056780874,-415064060019209901141,true,-194297437373278233331 +0,300000000000000000,30,200000000000000000,80,151713710405220026922,-66803139846659683830,true,-48702870779363797114 +0,300000000000000000,30,500000000000000000,80,159120661507972218931,-53673652365812154279,true,-80419875713673270341 +0,300000000000000000,30,1000000000000000000,80,171085310457536831070,-44119439613512959138,true,-102500602604622687026 +0,300000000000000000,50,200000000000000000,80,129283876294671712502,-387556460041908335385,true,-103029945090892239733 +0,300000000000000000,50,500000000000000000,80,141092240386577048974,-137908468166928563712,true,116600148700035296369 +0,300000000000000000,50,1000000000000000000,80,153996883423908400751,-112548700738468588136,true,53350614009652912619 +0,300000000000000000,80,200000000000000000,80,194910411595087512498,-350679574532673709254,true,460479088240710843683 +0,300000000000000000,80,500000000000000000,80,167461944576477377247,-395861382229558374542,true,-193162572569588408235 +0,300000000000000000,80,1000000000000000000,80,98060499930109189113,-250716505116596324840,true,312486482933744007968 +0,400000000000000000,30,200000000000000000,80,150574660485639722638,-57216306116552183682,true,-69349041862805040791 +0,400000000000000000,30,500000000000000000,80,156124699110441137264,-47095173940362397718,true,-99832902457775308917 +0,400000000000000000,30,1000000000000000000,80,164088294742656937514,-42798144626710557224,true,-113123706754740409853 +0,400000000000000000,50,200000000000000000,80,101984891357896228984,-403703973237306529259,true,-159810963167993342184 +0,400000000000000000,50,500000000000000000,80,93050802795113211297,-112942190738660851389,true,76782355343247627703 +0,400000000000000000,50,1000000000000000000,80,146211168921899826859,-96739021177271216259,true,21069548957298981478 +0,400000000000000000,80,200000000000000000,80,195284991249546441631,-378161183910984441657,true,58207646330519942139 +0,400000000000000000,80,500000000000000000,80,126822292889133327718,-415459501485737323687,true,-193595896070916250641 +0,400000000000000000,80,1000000000000000000,80,103277645489965962501,-210716203660920866401,true,237814266045280259416 +0,500000000000000000,30,200000000000000000,80,160165089135763787724,-52457372387737530852,true,-85746495037472917363 +0,500000000000000000,30,500000000000000000,80,165616942741438695662,-43168229997509836871,true,-103769464850816973019 +0,500000000000000000,30,1000000000000000000,80,167636674824743958880,-41857951969119624936,true,-115715987428184139359 +0,500000000000000000,50,200000000000000000,80,137257398268547095084,-121785800325298887611,true,87291598212266833144 +0,500000000000000000,50,500000000000000000,80,150777245701970530819,-90149733560086496311,true,32715087184711766092 +0,500000000000000000,50,1000000000000000000,80,154578529909996436716,-82389992004970148312,true,-5468794540756498607 +0,500000000000000000,80,200000000000000000,80,195483692737516408959,-372371512988250740855,true,21463017195070812053 +0,500000000000000000,80,500000000000000000,80,181708320653268054170,-717258802272851657891,true,28819472468983323132 +0,500000000000000000,80,1000000000000000000,80,147809334942209271564,-182174948624133030510,true,183590533004646736168 +0,700000000000000000,30,200000000000000000,80,172090616605752812013,-42821103304505047624,true,-106791750982074587073 +0,700000000000000000,30,500000000000000000,80,170834047266560500604,-41111027131374479843,true,-118558136533368404361 +0,700000000000000000,30,1000000000000000000,80,167395132031342642656,-41529125330675915290,true,-124902404215783434523 +0,700000000000000000,50,200000000000000000,80,156136231279694516144,-94139527570993234727,true,14837205782972891731 +0,700000000000000000,50,500000000000000000,80,163364167838044828205,-75773855747551436700,true,-12941746241299673390 +0,700000000000000000,50,1000000000000000000,80,169048448202911124516,-67754967724795492707,true,-46320675467968598949 +0,700000000000000000,80,200000000000000000,80,120148399059968917304,-732077293217835406710,true,-4213840679965837406 +0,700000000000000000,80,500000000000000000,80,127521084589355446262,-154565617021369556225,true,140881801676534546751 +0,700000000000000000,80,1000000000000000000,80,142256248043042149315,-137790692837483515921,true,103309717403743261013 +0,1000000000000000000,30,200000000000000000,80,159409348983483522327,-45323865693161526820,true,-119163013809315791363 +0,1000000000000000000,30,500000000000000000,80,162452755509922365965,-43262157332015765635,true,-126046782829450776857 +0,1000000000000000000,30,1000000000000000000,80,159891052289713702690,-45782422542117172808,true,-130155371134741777162 +0,1000000000000000000,50,200000000000000000,80,164049839625152367034,-70616966732105789908,true,-50848406512391379874 +0,1000000000000000000,50,500000000000000000,80,166462148519741767156,-66475894559302170836,true,-62812605866902698219 +0,1000000000000000000,50,1000000000000000000,80,163133148454976439883,-64681106428906906130,true,-75993715568526322626 +0,1000000000000000000,80,200000000000000000,80,139673365082890615487,-712529516196922108915,true,-44516558108995271432 +0,1000000000000000000,80,500000000000000000,80,142321738796538591059,-114176132346686812978,true,41246991178681608486 +0,1000000000000000000,80,1000000000000000000,80,147699828470900538132,-87373605077360303171,true,7391641868694835344 +300000000000000000,100000000000000000,30,200000000000000000,80,90107301307546703312,-161862554278910606841,true,184266683567034936324 +300000000000000000,100000000000000000,30,500000000000000000,80,115842754187213666521,-149312985436998962354,true,139186011849384937074 +300000000000000000,100000000000000000,30,1000000000000000000,80,119930390142787695766,-140456076007307060965,true,106301030816141345132 +300000000000000000,100000000000000000,50,200000000000000000,80,126413797870735931602,-280837339281459536814,true,445765474456618424461 +300000000000000000,100000000000000000,50,500000000000000000,80,121184503562354436732,-249988872641610082927,true,403040445654495678798 +300000000000000000,100000000000000000,50,1000000000000000000,80,103792440020700387729,-224812536568183527173,true,309816967808204831140 +300000000000000000,100000000000000000,80,200000000000000000,80,71689243311549135464,-394188998762162443364,true,40925083278779943808 +300000000000000000,100000000000000000,80,500000000000000000,80,113356502106070524277,-394242638579300655517,true,564381739722628659999 +300000000000000000,100000000000000000,80,1000000000000000000,80,110127229214213262868,-353976416519181661719,true,555047767500413014090 +300000000000000000,200000000000000000,30,200000000000000000,80,99572622424230742776,-141870738242882295339,true,130512168429676381778 +300000000000000000,200000000000000000,30,500000000000000000,80,109559001489116450394,-124425878984435035664,true,99202104576490437214 +300000000000000000,200000000000000000,30,1000000000000000000,80,102526515175603812125,-121866086752138898655,true,82185759309751294162 +300000000000000000,200000000000000000,50,200000000000000000,80,120076418909682034930,-235571922975371769278,true,362989292124758351974 +300000000000000000,200000000000000000,50,500000000000000000,80,88354241944285805761,-214139044643556608425,true,286950076035470995362 +300000000000000000,200000000000000000,50,1000000000000000000,80,105746563945546034609,-197788747189567644373,true,240191470493631701653 +300000000000000000,200000000000000000,80,200000000000000000,80,115876988095706978431,-389158239980328590634,true,527215058577882545669 +300000000000000000,200000000000000000,80,500000000000000000,80,112395407219313592651,-339744268544626947790,true,494625112034363742481 +300000000000000000,200000000000000000,80,1000000000000000000,80,102024486124534527525,-296372731705502286051,true,477702458866675883227 +300000000000000000,300000000000000000,30,200000000000000000,80,88920464627971369227,-124240146059600588139,true,96385413636861671348 +300000000000000000,300000000000000000,30,500000000000000000,80,97158447579894841293,-115730399903882729733,true,72459372210021436981 +300000000000000000,300000000000000000,30,1000000000000000000,80,102976714355000887670,-111412838717716383372,true,63182328411283816191 +300000000000000000,300000000000000000,50,200000000000000000,80,113346591037115864789,-202568530914906266277,true,276188206403829251174 +300000000000000000,300000000000000000,50,500000000000000000,80,81537980997145678606,-187591085289987344189,true,218237262988112240956 +300000000000000000,300000000000000000,50,1000000000000000000,80,91271920387095553391,-181798195335045584065,true,188461519155115622591 +300000000000000000,300000000000000000,80,200000000000000000,80,113744203391555900599,-345764911194062960855,true,451642160967398894394 +300000000000000000,300000000000000000,80,500000000000000000,80,105249183379045062672,-293091879495295121076,true,435780194458694625987 +300000000000000000,300000000000000000,80,1000000000000000000,80,98060499930109189113,-242665341267690387608,true,385997991341989218570 +300000000000000000,400000000000000000,30,200000000000000000,80,80537083487724658096,-113753020645263888591,true,65923425252412341623 +300000000000000000,400000000000000000,30,500000000000000000,80,87186147578647587883,-109868191561636769915,true,61434776167442084227 +300000000000000000,400000000000000000,30,1000000000000000000,80,89265380428089521901,-104790916112609874802,true,49782471972386051176 +300000000000000000,400000000000000000,50,200000000000000000,80,101984891357896228984,-185379597430739908461,true,216322706985157517281 +300000000000000000,400000000000000000,50,500000000000000000,80,93050802795113211297,-167789073576034252735,true,179763854292853435315 +300000000000000000,400000000000000000,50,1000000000000000000,80,82169187485556656368,-163836078579841657095,true,150295393960451251958 +300000000000000000,400000000000000000,80,200000000000000000,80,112146686903194430162,-304946915681640331653,true,394865849110529356288 +300000000000000000,400000000000000000,80,500000000000000000,80,98444350388172076663,-249408519837231369296,true,357192004106156059233 +300000000000000000,400000000000000000,80,1000000000000000000,80,81658451002257837855,-242559466779692443036,true,345969339860429203498 +300000000000000000,500000000000000000,30,200000000000000000,80,81115008534181111585,-96644637042564352855,true,46463799974410115285 +300000000000000000,500000000000000000,30,500000000000000000,80,87929680931988001720,-103320744414857961744,true,39488138421233652604 +300000000000000000,500000000000000000,30,1000000000000000000,80,85422695889459625853,-91078038512982797942,true,36965212240887653554 +300000000000000000,500000000000000000,50,200000000000000000,80,64979024268061866500,-158615144579486207967,true,153373806381727322036 +300000000000000000,500000000000000000,50,500000000000000000,80,82517803505587784667,-151738486423671497287,true,135943843335468931896 +300000000000000000,500000000000000000,50,1000000000000000000,80,85960086184551212940,-144611233126908568330,true,118634187616683820919 +300000000000000000,500000000000000000,80,200000000000000000,80,106554680600409061408,-243445620578820512667,true,312031716836268404438 +300000000000000000,500000000000000000,80,500000000000000000,80,104779946046969534812,-209763396645828492330,true,306403500142105087962 +300000000000000000,500000000000000000,80,1000000000000000000,80,99185560258319231697,-215763870810632508434,true,281674057376863687075 +300000000000000000,700000000000000000,30,200000000000000000,80,81520663380994883518,-74457744225408771320,true,16192019004694037318 +300000000000000000,700000000000000000,30,500000000000000000,80,79013424840824728885,-67845265124252328511,true,16211848496958546465 +300000000000000000,700000000000000000,30,1000000000000000000,80,72538651012079626575,-69236170243112263953,true,10325798050469524832 +300000000000000000,700000000000000000,50,200000000000000000,80,69845516229807783673,-110626211265688035202,true,83262214777326406613 +300000000000000000,700000000000000000,50,500000000000000000,80,80662276907143907513,-100572571892647844815,true,67427855506738184219 +300000000000000000,700000000000000000,50,1000000000000000000,80,86158336079897645338,-101486811066707183694,true,63540333112507136066 +300000000000000000,700000000000000000,80,200000000000000000,80,65683979060278118145,-158807253853505997218,true,205648700914097179782 +300000000000000000,700000000000000000,80,500000000000000000,80,58303902795509903100,-154018369775207643199,true,186341379713225964843 +300000000000000000,700000000000000000,80,1000000000000000000,80,65679235679792616093,-157945643974289292406,true,171262083083818054220 +300000000000000000,1000000000000000000,30,200000000000000000,80,48030848800840149509,-50391471562312213247,true,3028289877824552727 +300000000000000000,1000000000000000000,30,500000000000000000,80,46061948912631857969,-48140270305063945692,true,4171858736076297548 +300000000000000000,1000000000000000000,30,1000000000000000000,80,45025600810425548909,-48068326690607399640,true,3286604958799171949 +300000000000000000,1000000000000000000,50,200000000000000000,80,61267913252416902198,-66372147311822058691,true,31149128611596818801 +300000000000000000,1000000000000000000,50,500000000000000000,80,64089224385289216484,-60892839378304369006,true,31921907704334962519 +300000000000000000,1000000000000000000,50,1000000000000000000,80,54658023720697948800,-57706673267471653921,true,23050124121187638187 +300000000000000000,1000000000000000000,80,200000000000000000,80,59161632604099011989,-110618169425182058975,true,99198511562295091301 +300000000000000000,1000000000000000000,80,500000000000000000,80,54474456330858328703,-91309785061472866387,true,86528498815086544607 +300000000000000000,1000000000000000000,80,1000000000000000000,80,58547662761575342558,-88716563204141709661,true,78316558694418762146 +500000000000000000,100000000000000000,30,200000000000000000,80,44486458737952248326,-175492736798642342792,true,202816972862435784210 +500000000000000000,100000000000000000,30,500000000000000000,80,36206120417758688313,-160956656045798694610,true,178290041315015296957 +500000000000000000,100000000000000000,30,1000000000000000000,80,39736839423928406979,-168771146761320369483,true,170103441273370534383 +500000000000000000,100000000000000000,50,200000000000000000,80,78711578928330705891,-259584351343254675440,true,414422124934757811228 +500000000000000000,100000000000000000,50,500000000000000000,80,74234005460738908552,-241044681680829601987,true,356670338978896219838 +500000000000000000,100000000000000000,50,1000000000000000000,80,32342469884268611741,-228505681280192717122,true,312672159583669111635 +500000000000000000,100000000000000000,80,200000000000000000,80,72263809083903043161,-300362772749047727860,true,569373379582803890410 +500000000000000000,100000000000000000,80,500000000000000000,80,72868698629180002611,-271686200332581005687,true,547108647418804290571 +500000000000000000,100000000000000000,80,1000000000000000000,80,63390941242693996825,-287842228117934993913,true,527708600464211033744 +500000000000000000,200000000000000000,30,200000000000000000,80,31065103199988951255,-154218365153302492981,true,163385555768605587896 +500000000000000000,200000000000000000,30,500000000000000000,80,30547271291789835623,-140754547013391332208,true,146413497211840620960 +500000000000000000,200000000000000000,30,1000000000000000000,80,29651367876392850752,-143068451326219418577,true,144184932744962095883 +500000000000000000,200000000000000000,50,200000000000000000,80,75289194461590512308,-230492256827587722877,true,320452471246771868058 +500000000000000000,200000000000000000,50,500000000000000000,80,45794784565606216985,-222257272790918818370,true,281629100645196876513 +500000000000000000,200000000000000000,50,1000000000000000000,80,27494765792646536294,-209034345882227715893,true,259117689321105443699 +500000000000000000,200000000000000000,80,200000000000000000,80,72861288616759974138,-261526015945077666254,true,482904621712430905217 +500000000000000000,200000000000000000,80,500000000000000000,80,67632969871181564815,-261115486097641517753,true,460572135501616131424 +500000000000000000,200000000000000000,80,1000000000000000000,80,61982651667291683137,-259241216017636384938,true,428547893524583801267 +500000000000000000,300000000000000000,30,200000000000000000,80,34538841960203899843,-127013832338825880826,true,131323188241454412199 +500000000000000000,300000000000000000,30,500000000000000000,80,27103812109069946678,-119357628782690322603,true,123282017789984093166 +500000000000000000,300000000000000000,30,1000000000000000000,80,35031536619837987338,-118715123688777620135,true,118173490035489203174 +500000000000000000,300000000000000000,50,200000000000000000,80,70793761337372631788,-197023506618685753315,true,253188476130364119821 +500000000000000000,300000000000000000,50,500000000000000000,80,45160064447757984142,-190528744609480208120,true,228173221415754471786 +500000000000000000,300000000000000000,50,1000000000000000000,80,32255668941515526011,-178104785832190684348,true,211920874224451287779 +500000000000000000,300000000000000000,80,200000000000000000,80,70976685443335367733,-219156108495643696441,true,407244156287009288505 +500000000000000000,300000000000000000,80,500000000000000000,80,62983860045325135955,-267178171921048145980,true,407833003380206788801 +500000000000000000,300000000000000000,80,1000000000000000000,80,60105995506427441011,-257897460005444137012,true,375421179830147532657 +500000000000000000,400000000000000000,30,200000000000000000,80,35677891879784204126,-103508219776316577574,true,109501690408097556667 +500000000000000000,400000000000000000,30,500000000000000000,80,30103557030084122159,-103638201376493037537,true,104053565890733664848 +500000000000000000,400000000000000000,30,1000000000000000000,80,22164257622766989250,-103032221403716395185,true,102747368693468312473 +500000000000000000,400000000000000000,50,200000000000000000,80,66274769568476490417,-157918847632576875598,true,200003311143820214148 +500000000000000000,400000000000000000,50,500000000000000000,80,62637499419605255260,-148973288226574640181,true,187109673926980077474 +500000000000000000,400000000000000000,50,1000000000000000000,80,40041383443524099903,-140195496050596969238,true,172077671723566878256 +500000000000000000,400000000000000000,80,200000000000000000,80,67583474435264671932,-181761222031898938291,true,329300627735663135156 +500000000000000000,400000000000000000,80,500000000000000000,80,61688497390002116798,-214023968137309065044,true,329938892752459499216 +500000000000000000,400000000000000000,80,1000000000000000000,80,54112381876491304163,-210650036311846002614,true,305446745987071200232 +500000000000000000,500000000000000000,30,200000000000000000,80,26087463229660139043,-90846892907319251173,true,95254486600679119959 +500000000000000000,500000000000000000,30,500000000000000000,80,20609509697044266641,-90794969531731228761,true,94581815348815544245 +500000000000000000,500000000000000000,30,1000000000000000000,80,18615877540679967888,-91434113077011390444,true,94048296144574411478 +500000000000000000,500000000000000000,50,200000000000000000,80,48995154096876831686,-134455190014312922748,true,163840444475274359574 +500000000000000000,500000000000000000,50,500000000000000000,80,35457695737137372910,-126990090068980061222,true,153435455139368155006 +500000000000000000,500000000000000000,50,1000000000000000000,80,31674022455427490050,-122448139594092920763,true,151294376817807220024 +500000000000000000,500000000000000000,80,200000000000000000,80,70351294909313197251,-185539476141624759553,true,293987377910008696590 +500000000000000000,500000000000000000,80,500000000000000000,80,68363250503695078956,-179355559231500348303,true,276188556134828461258 +500000000000000000,500000000000000000,80,1000000000000000000,80,62882051832966425927,-183218740689457814418,true,263097129816514472354 +500000000000000000,700000000000000000,30,200000000000000000,80,14672884841177386083,-73170085613778221244,true,81144908807858117272 +500000000000000000,700000000000000000,30,500000000000000000,80,15883215019214578931,-73432560581904692121,true,82488998712992121142 +500000000000000000,700000000000000000,30,1000000000000000000,80,19749698463053482590,-74339238958136662653,true,81126794756404617511 +500000000000000000,700000000000000000,50,200000000000000000,80,30116321085729410620,-89700297321919080105,true,114818465834302120477 +500000000000000000,700000000000000000,50,500000000000000000,80,22861491564074840295,-86386908167346397241,true,113889491367005838542 +500000000000000000,700000000000000000,50,1000000000000000000,80,17204104162512802251,-86974578065788181960,true,113013711279050566202 +500000000000000000,700000000000000000,80,200000000000000000,80,56911187539240733164,-125008621608389764880,true,192171714851499481658 +500000000000000000,700000000000000000,80,500000000000000000,80,55491605061147183068,-122038465663306578447,true,183882303683333002041 +500000000000000000,700000000000000000,80,1000000000000000000,80,43996304322381777452,-123131747697630669686,true,172629532441593810792 +500000000000000000,1000000000000000000,30,200000000000000000,80,14488833876677040228,-64457071415888606787,true,83099248580858262094 +500000000000000000,1000000000000000000,30,500000000000000000,80,17352784622264510429,-62342972568557503764,true,80882184967214463719 +500000000000000000,1000000000000000000,30,1000000000000000000,80,20062650065406007663,-61356883860937814021,true,82860657952626967279 +500000000000000000,1000000000000000000,50,200000000000000000,80,22202712740271559742,-64801948701401687396,true,91840223767196160627 +500000000000000000,1000000000000000000,50,500000000000000000,80,19768483654867759052,-63141945689631744199,true,67471684819300492248 +500000000000000000,1000000000000000000,50,1000000000000000000,80,23500732957913414039,-63107207879210619169,true,72112923440198258571 +500000000000000000,1000000000000000000,80,200000000000000000,80,44194855451167647673,-84430338015995379687,true,122043065665834779949 +500000000000000000,1000000000000000000,80,500000000000000000,80,41600977174740418222,-81524195953737881264,true,116276324219682089389 +500000000000000000,1000000000000000000,80,1000000000000000000,80,38552723894523388645,-85329661234512118904,true,112213452188844606617 +800000000000000000,100000000000000000,30,200000000000000000,80,36905336156534684556,-171371790526992377987,true,264620293310099405957 +800000000000000000,100000000000000000,30,500000000000000000,80,19195504442807786011,-169089212169222660103,true,262498445161576015305 +800000000000000000,100000000000000000,30,1000000000000000000,80,13338202761233202431,-156803013686707012080,true,248276112491961513602 +800000000000000000,100000000000000000,50,200000000000000000,80,38058042759277885532,-202612059110886545239,true,379693615612309742627 +800000000000000000,100000000000000000,50,500000000000000000,80,36852273288214659043,-187420175578405586341,true,348377406343646818806 +800000000000000000,100000000000000000,50,1000000000000000000,80,32342469884268611741,-177620311156643546014,true,333186182812971509378 +800000000000000000,100000000000000000,80,200000000000000000,80,22145899943216375490,-251032036729030608202,true,554183472283769980025 +800000000000000000,100000000000000000,80,500000000000000000,80,22419284166685867096,-243129172641849855521,true,513958193324268721943 +800000000000000000,100000000000000000,80,1000000000000000000,80,17467688613630627114,-276987311216036584477,true,512913241543772285054 +800000000000000000,200000000000000000,30,200000000000000000,80,31065103199988951255,-143593461993068451320,true,231636502257266223824 +800000000000000000,200000000000000000,30,500000000000000000,80,22675631660871881181,-106893637308331132525,true,215932889426683393309 +800000000000000000,200000000000000000,30,1000000000000000000,80,21061277865663568593,-141071478485198678793,true,230380815735649317208 +800000000000000000,200000000000000000,50,200000000000000000,80,32294340788263924877,-158375965418989578409,true,306898553243877227333 +800000000000000000,200000000000000000,50,500000000000000000,80,31786283323356027980,-154957820727680939814,true,294047098133520390290 +800000000000000000,200000000000000000,50,1000000000000000000,80,27494765792646536294,-154127626169673341154,true,288140029698097227289 +800000000000000000,200000000000000000,80,200000000000000000,80,23756443429234964499,-197956232742756670716,true,431280354909927238977 +800000000000000000,200000000000000000,80,500000000000000000,80,22619980599634655837,-223527231477159307931,true,432099188075514099550 +800000000000000000,200000000000000000,80,1000000000000000000,80,18496350812883736769,-220457937514786812505,true,413701684850142940952 +800000000000000000,300000000000000000,30,200000000000000000,80,27901334692866743353,-136668570069891498135,true,216296271289601050441 +800000000000000000,300000000000000000,30,500000000000000000,80,27103812109069946678,-103810360209599751554,true,198676019109835727335 +800000000000000000,300000000000000000,30,1000000000000000000,80,15167241907887095695,-99231307048051047347,true,195120962383670863949 +800000000000000000,300000000000000000,50,200000000000000000,80,31983229354294288690,-132591452604911881836,true,260892961393253677285 +800000000000000000,300000000000000000,50,500000000000000000,80,28164603926372893533,-129096406965735370237,true,258016718812427868422 +800000000000000000,300000000000000000,50,1000000000000000000,80,25482882866370742539,-129399676492688675736,true,251054432947853040160 +800000000000000000,300000000000000000,80,200000000000000000,80,23035705841219707082,-187421975163448294149,true,384445209125342488932 +800000000000000000,300000000000000000,80,500000000000000000,80,29077955346439466636,-173468098949990015840,true,356865853379189253710 +800000000000000000,300000000000000000,80,1000000000000000000,80,18536073384736111188,-177683503713478055344,true,345711726590259520354 +800000000000000000,400000000000000000,30,200000000000000000,80,26260247285102961706,-87468925181602760369,true,187267932533623614774 +800000000000000000,400000000000000000,30,500000000000000000,80,23890236047907060801,-89193603927679563026,true,186321311345585310608 +800000000000000000,400000000000000000,30,1000000000000000000,80,22164257622766989250,-91363581482957249444,true,190795273652333207670 +800000000000000000,400000000000000000,50,200000000000000000,80,28820386942698714471,-108742209300118109246,true,230683078062454922799 +800000000000000000,400000000000000000,50,500000000000000000,80,24223389724458513022,-110399096174250294967,true,225189808870007163628 +800000000000000000,400000000000000000,50,1000000000000000000,80,23434669951968625250,-107852054006081952742,true,227269093441876833567 +800000000000000000,400000000000000000,80,200000000000000000,80,31429959225488399978,-149704918054505559093,true,319169461064250188164 +800000000000000000,400000000000000000,80,500000000000000000,80,28422479260053691234,-141754395079672359988,true,300610412993899447332 +800000000000000000,400000000000000000,80,1000000000000000000,80,26156986822633581923,-136754850579053842712,true,295366574525089053281 +800000000000000000,500000000000000000,30,200000000000000000,80,20129703895388473151,-85839715094773432645,true,187860925171176099557 +800000000000000000,500000000000000000,30,500000000000000000,80,17719218011545819620,-86812492558089974199,true,188131742239938141930 +800000000000000000,500000000000000000,30,1000000000000000000,80,18615877540679967888,-86275010007780891931,true,186172116415780720864 +800000000000000000,500000000000000000,50,200000000000000000,80,21834556016404270940,-94098823856410024921,true,206800030579192303067 +800000000000000000,500000000000000000,50,500000000000000000,80,20570474444971935923,-90733491609929923824,true,207294078378913221657 +800000000000000000,500000000000000000,50,1000000000000000000,80,19627245786589672051,-90045735009417035197,true,207155106719503616524 +800000000000000000,500000000000000000,80,200000000000000000,80,30261400528168642864,-121436373923420292187,true,271202927101493819090 +800000000000000000,500000000000000000,80,500000000000000000,80,25740210272387031574,-116828814141372694492,true,262822672794623939799 +800000000000000000,500000000000000000,80,1000000000000000000,80,24594637054396371609,-113440095573296020588,true,254275757231432892909 +800000000000000000,700000000000000000,30,200000000000000000,80,9471339372967042621,-93998437958296104276,true,189693865047187048713 +800000000000000000,700000000000000000,30,500000000000000000,80,10657517196797271583,-93835485015950227194,true,189676887206548556143 +800000000000000000,700000000000000000,30,1000000000000000000,80,19749698463053482590,-92640208096258511891,true,191103747383389375792 +800000000000000000,700000000000000000,50,200000000000000000,80,14828334758639986429,-89305316989103735927,true,194177187371619811418 +800000000000000000,700000000000000000,50,500000000000000000,80,12925429302228230051,-91605937753739683882,true,192769638795558167374 +800000000000000000,700000000000000000,50,1000000000000000000,80,12371045867822184539,-93220760704551967972,true,192517045178546294484 +800000000000000000,700000000000000000,80,200000000000000000,80,23054771736948570367,-103982735347410476013,true,216862315623127923073 +800000000000000000,700000000000000000,80,500000000000000000,80,21233565037410265573,-99334603707250807450,true,214918336790619778086 +800000000000000000,700000000000000000,80,1000000000000000000,80,18953945724970167970,-104630188452660080484,true,212868313554923304854 +800000000000000000,1000000000000000000,30,200000000000000000,80,12799419517002840484,-95301900922670605048,true,199753716177349409739 +800000000000000000,1000000000000000000,30,500000000000000000,80,13897524752255559121,-98307695955918542148,true,197049050134197580201 +800000000000000000,1000000000000000000,30,1000000000000000000,80,16573634285638715620,-86765945330479750502,true,191930843763866869468 +800000000000000000,1000000000000000000,50,200000000000000000,80,7809100589691159823,-93800515444879906672,true,173600205281731715780 +800000000000000000,1000000000000000000,50,500000000000000000,80,7616068263028317786,-90770736392856725540,true,172359452971360399663 +800000000000000000,1000000000000000000,50,1000000000000000000,80,18521941207394758653,-86847506107369858404,true,175419686813318797994 +800000000000000000,1000000000000000000,80,200000000000000000,80,18894595240345422887,-84540823289529917771,true,163071117580484715820 +800000000000000000,1000000000000000000,80,500000000000000000,80,18720498797532172895,-86618984325279010053,true,164490522064959025991 +800000000000000000,1000000000000000000,80,1000000000000000000,80,16033994108252459502,-83772847866938879484,true,163629468088936677962 diff --git a/onchain/test/ReplayProfitableScenario.t.sol b/onchain/test/ReplayProfitableScenario.t.sol index ded1f82..1b9c952 100644 --- a/onchain/test/ReplayProfitableScenario.t.sol +++ b/onchain/test/ReplayProfitableScenario.t.sol @@ -205,7 +205,7 @@ contract ReplayProfitableScenario is Test { return; } - SwapExecutor executor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm); + SwapExecutor executor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm, false); vm.prank(buyer); weth.transfer(address(executor), amount); @@ -222,7 +222,7 @@ contract ReplayProfitableScenario is Test { if (amount == 0) return; } - SwapExecutor executor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm); + SwapExecutor executor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm, false); vm.prank(seller); kraiken.transfer(address(executor), amount); diff --git a/onchain/test/mocks/ConfigurableOptimizer.sol b/onchain/test/mocks/ConfigurableOptimizer.sol new file mode 100644 index 0000000..5aa2668 --- /dev/null +++ b/onchain/test/mocks/ConfigurableOptimizer.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.19; + +/// @title ConfigurableOptimizer +/// @notice Optimizer mock with directly settable parameters for parameter sweep fuzzing +/// @dev Parameters are set via constructor or setter, not environment variables (Solidity can't read env) +contract ConfigurableOptimizer { + uint256 private _capitalInefficiency; + uint256 private _anchorShare; + uint24 private _anchorWidth; + uint256 private _discoveryDepth; + + constructor(uint256 capitalInefficiency_, uint256 anchorShare_, uint24 anchorWidth_, uint256 discoveryDepth_) { + _capitalInefficiency = capitalInefficiency_; + _anchorShare = anchorShare_; + _anchorWidth = anchorWidth_; + _discoveryDepth = discoveryDepth_; + } + + function setParams(uint256 capitalInefficiency_, uint256 anchorShare_, uint24 anchorWidth_, uint256 discoveryDepth_) external { + _capitalInefficiency = capitalInefficiency_; + _anchorShare = anchorShare_; + _anchorWidth = anchorWidth_; + _discoveryDepth = discoveryDepth_; + } + + function calculateSentiment(uint256, uint256) public pure returns (uint256) { + return 0; + } + + function getSentiment() external pure returns (uint256) { + return 0; + } + + function getLiquidityParams() external view returns (uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth) { + capitalInefficiency = _capitalInefficiency; + anchorShare = _anchorShare; + anchorWidth = _anchorWidth; + discoveryDepth = _discoveryDepth; + } + + function getDescription() external pure returns (string memory) { + return "Configurable Optimizer (Parameter Sweep)"; + } +} diff --git a/web-app/src/views/CheatsView.vue b/web-app/src/views/CheatsView.vue index 3df6cbb..a70ba98 100644 --- a/web-app/src/views/CheatsView.vue +++ b/web-app/src/views/CheatsView.vue @@ -75,6 +75,25 @@ + +
+ + + +
+
+
@@ -127,6 +146,7 @@ import FButton from '@/components/fcomponents/FButton.vue'; import FCard from '@/components/fcomponents/FCard.vue'; import FInput from '@/components/fcomponents/FInput.vue'; import { config } from '@/wagmi'; +import { useStatCollection } from '@/composables/useStatCollection'; import { useToast } from 'vue-toastification'; import { useAccount } from '@wagmi/vue'; import { getChain, DEFAULT_CHAIN_ID } from '@/config'; @@ -149,6 +169,7 @@ import { } from 'viem'; const toast = useToast(); +const statCollection = useStatCollection(); const { address, chainId } = useAccount(); const wagmiConfig: WagmiConfig = config;