feat: protocol stats display + parameter sweep fuzzing infrastructure (#106)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
37b1002dae
commit
21857ae8ca
10 changed files with 1592 additions and 308 deletions
361
onchain/analysis/PARAMETER_SEARCH_RESULTS.md
Normal file
361
onchain/analysis/PARAMETER_SEARCH_RESULTS.md
Normal file
|
|
@ -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*
|
||||||
306
onchain/analysis/ParameterSweepFuzzing.s.sol
Normal file
306
onchain/analysis/ParameterSweepFuzzing.s.sol
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,25 +1,29 @@
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.19;
|
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/Script.sol";
|
||||||
import "forge-std/console2.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 {
|
contract StreamlinedFuzzing is Script {
|
||||||
// Test environment
|
// Test environment
|
||||||
|
|
@ -32,77 +36,101 @@ contract StreamlinedFuzzing is Script {
|
||||||
LiquidityManager lm;
|
LiquidityManager lm;
|
||||||
SwapExecutor swapExecutor;
|
SwapExecutor swapExecutor;
|
||||||
bool token0isWeth;
|
bool token0isWeth;
|
||||||
|
|
||||||
// Actors
|
// Actors
|
||||||
address trader = makeAddr("trader");
|
address trader = makeAddr("trader");
|
||||||
|
address staker = makeAddr("staker");
|
||||||
address fees = makeAddr("fees");
|
address fees = makeAddr("fees");
|
||||||
|
|
||||||
// Staking tracking
|
// Staking tracking
|
||||||
mapping(address => uint256[]) public activePositions;
|
uint256[] public activePositionIds;
|
||||||
uint256[] public allPositionIds;
|
|
||||||
uint256 totalStakesAttempted;
|
uint256 totalStakesAttempted;
|
||||||
uint256 totalStakesSucceeded;
|
uint256 totalStakesSucceeded;
|
||||||
uint256 totalSnatchesAttempted;
|
|
||||||
uint256 totalSnatchesSucceeded;
|
|
||||||
|
|
||||||
// CSV filename for current run
|
// CSV filename for current run
|
||||||
string csvFilename;
|
string csvFilename;
|
||||||
|
|
||||||
// Track cumulative fees
|
// Track cumulative fees
|
||||||
uint256 totalFees0;
|
uint256 totalFees0;
|
||||||
uint256 totalFees1;
|
uint256 totalFees1;
|
||||||
|
|
||||||
// Track recentering
|
// Track recentering
|
||||||
uint256 lastRecenterBlock;
|
uint256 lastRecenterBlock;
|
||||||
|
|
||||||
|
// Config
|
||||||
|
uint256 cfgBuyBias;
|
||||||
|
uint256 cfgMinBuy;
|
||||||
|
uint256 cfgMaxBuy;
|
||||||
|
bool cfgUncapped;
|
||||||
|
|
||||||
function run() public {
|
function run() public {
|
||||||
// Get configuration from environment
|
// Get configuration from environment
|
||||||
uint256 numRuns = vm.envOr("FUZZING_RUNS", uint256(20));
|
uint256 numRuns = vm.envOr("FUZZING_RUNS", uint256(20));
|
||||||
uint256 tradesPerRun = vm.envOr("TRADES_PER_RUN", uint256(15));
|
uint256 tradesPerRun = vm.envOr("TRADES_PER_RUN", uint256(15));
|
||||||
bool enableStaking = vm.envOr("ENABLE_STAKING", true);
|
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));
|
uint256 stakingBias = vm.envOr("STAKING_BIAS", uint256(80));
|
||||||
string memory optimizerClass = vm.envOr("OPTIMIZER_CLASS", string("BullMarketOptimizer"));
|
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("=== Streamlined Fuzzing Analysis ===");
|
||||||
console2.log("Optimizer:", optimizerClass);
|
console2.log("Optimizer:", optimizerClass);
|
||||||
|
console2.log("Uncapped swaps:", cfgUncapped);
|
||||||
// Deploy factory once for all runs (gas optimization)
|
console2.log("Trade range (ETH):", cfgMinBuy, cfgMaxBuy);
|
||||||
|
|
||||||
|
// Deploy factory once
|
||||||
testEnv = new TestEnvironment(fees);
|
testEnv = new TestEnvironment(fees);
|
||||||
factory = UniswapHelpers.deployUniswapFactory();
|
factory = UniswapHelpers.deployUniswapFactory();
|
||||||
|
|
||||||
// Generate unique 4-character scenario ID
|
// Generate unique 4-character scenario ID
|
||||||
string memory scenarioCode = _generateScenarioId();
|
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++) {
|
for (uint256 runIndex = 0; runIndex < numRuns; runIndex++) {
|
||||||
string memory runId = string(abi.encodePacked(scenarioCode, "-", _padNumber(runIndex, 3)));
|
string memory runId = string(abi.encodePacked(scenarioCode, "-", _padNumber(runIndex, 3)));
|
||||||
console2.log("\nRun:", runId);
|
console2.log("\nRun:", runId);
|
||||||
|
|
||||||
// Initialize CSV file for this run
|
// Initialize CSV file for this run
|
||||||
// Always write to analysis directory relative to project root
|
|
||||||
csvFilename = string(abi.encodePacked("analysis/fuzz-", runId, ".csv"));
|
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);
|
vm.writeFile(csvFilename, header);
|
||||||
|
|
||||||
// Setup fresh environment for each run
|
// Reset tracking for CSV
|
||||||
_setupEnvironment(optimizerClass, runIndex % 2 == 0);
|
|
||||||
|
|
||||||
// Reset tracking variables
|
|
||||||
totalFees0 = 0;
|
totalFees0 = 0;
|
||||||
totalFees1 = 0;
|
totalFees1 = 0;
|
||||||
lastRecenterBlock = block.number;
|
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);
|
vm.prank(trader);
|
||||||
weth.deposit{value: traderFund}();
|
weth.deposit{ value: traderFund }();
|
||||||
|
|
||||||
// Initial state
|
// Initial state
|
||||||
_recordState("INIT", 0);
|
_recordState("INIT", 0);
|
||||||
|
|
||||||
// Execute trades
|
// Execute trades
|
||||||
for (uint256 i = 0; i < tradesPerRun; i++) {
|
for (uint256 i = 0; i < tradesPerRun; i++) {
|
||||||
// Check for recenter opportunity on average every 3 trades
|
// Check for recenter opportunity on average every 3 trades
|
||||||
|
|
@ -110,51 +138,67 @@ contract StreamlinedFuzzing is Script {
|
||||||
if (recenterRand == 0) {
|
if (recenterRand == 0) {
|
||||||
_tryRecenter();
|
_tryRecenter();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine trade based on bias
|
// Determine trade based on bias
|
||||||
uint256 rand = uint256(keccak256(abi.encodePacked(runIndex, i))) % 100;
|
uint256 rand = uint256(keccak256(abi.encodePacked(runIndex, i))) % 100;
|
||||||
|
|
||||||
if (rand < buyBias) {
|
if (rand < cfgBuyBias) {
|
||||||
_executeBuy(runIndex, i);
|
_executeBuy(runIndex, i);
|
||||||
} else {
|
} else {
|
||||||
_executeSell(runIndex, i);
|
_executeSell(runIndex, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Staking operations if enabled
|
// Staking operations if enabled
|
||||||
if (enableStaking && i % 3 == 0) {
|
if (enableStaking && i % 5 == 0) {
|
||||||
_executeStakingOperation(runIndex, i, stakingBias);
|
_executeStakingOperation(runIndex, i, stakingBias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final state
|
// Final recenter + liquidate
|
||||||
|
_tryRecenter();
|
||||||
_liquidateTraderHoldings();
|
_liquidateTraderHoldings();
|
||||||
_recordState("FINAL", 0);
|
_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("\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);
|
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 {
|
function _setupEnvironment(string memory optimizerClass, bool wethIsToken0) internal {
|
||||||
// Get optimizer address
|
|
||||||
address optimizer = _deployOptimizer(optimizerClass);
|
address optimizer = _deployOptimizer(optimizerClass);
|
||||||
|
|
||||||
// Setup new environment
|
(factory, pool, weth, kraiken, stake, lm,, token0isWeth) = testEnv.setupEnvironmentWithExistingFactory(factory, wethIsToken0, fees, optimizer);
|
||||||
(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);
|
||||||
// Deploy swap executor with liquidity boundary checks
|
|
||||||
swapExecutor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm);
|
|
||||||
|
|
||||||
// Fund liquidity manager
|
// Fund liquidity manager
|
||||||
vm.deal(address(lm), 200 ether);
|
vm.deal(address(lm), 200 ether);
|
||||||
|
|
||||||
// Initialize liquidity positions
|
|
||||||
// First need to give LM some WETH
|
|
||||||
vm.prank(address(lm));
|
vm.prank(address(lm));
|
||||||
weth.deposit{value: 100 ether}();
|
weth.deposit{ value: 100 ether }();
|
||||||
|
|
||||||
// Now try recenter from fee destination
|
// Initial recenter to set positions
|
||||||
vm.prank(fees);
|
vm.prank(fees);
|
||||||
try lm.recenter() returns (bool isUp) {
|
try lm.recenter() returns (bool isUp) {
|
||||||
console2.log("Initial recenter successful, isUp:", isUp);
|
console2.log("Initial recenter successful, isUp:", isUp);
|
||||||
|
|
@ -163,15 +207,8 @@ contract StreamlinedFuzzing is Script {
|
||||||
} catch {
|
} catch {
|
||||||
console2.log("Initial recenter failed with unknown error");
|
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) {
|
function _deployOptimizer(string memory optimizerClass) internal returns (address) {
|
||||||
if (keccak256(bytes(optimizerClass)) == keccak256(bytes("BullMarketOptimizer"))) {
|
if (keccak256(bytes(optimizerClass)) == keccak256(bytes("BullMarketOptimizer"))) {
|
||||||
return address(new BullMarketOptimizer());
|
return address(new BullMarketOptimizer());
|
||||||
|
|
@ -186,33 +223,37 @@ contract StreamlinedFuzzing is Script {
|
||||||
} else if (keccak256(bytes(optimizerClass)) == keccak256(bytes("MaliciousOptimizer"))) {
|
} else if (keccak256(bytes(optimizerClass)) == keccak256(bytes("MaliciousOptimizer"))) {
|
||||||
return address(new MaliciousOptimizer());
|
return address(new MaliciousOptimizer());
|
||||||
} else {
|
} else {
|
||||||
// Default to bull market
|
|
||||||
return address(new BullMarketOptimizer());
|
return address(new BullMarketOptimizer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _executeBuy(uint256 runIndex, uint256 tradeIndex) internal {
|
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;
|
if (amount == 0 || weth.balanceOf(trader) < amount) return;
|
||||||
|
|
||||||
vm.startPrank(trader);
|
vm.startPrank(trader);
|
||||||
weth.transfer(address(swapExecutor), amount);
|
weth.transfer(address(swapExecutor), amount);
|
||||||
try swapExecutor.executeBuy(amount, trader) returns (uint256 actualAmount) {
|
try swapExecutor.executeBuy(amount, trader) returns (uint256 actualAmount) {
|
||||||
if (actualAmount == 0) {
|
if (actualAmount == 0) {
|
||||||
console2.log("Buy returned 0, requested:", amount);
|
console2.log("Buy returned 0, requested:", amount / 1e18);
|
||||||
}
|
}
|
||||||
_recordState("BUY", actualAmount);
|
_recordState("BUY", actualAmount);
|
||||||
} catch Error(string memory reason) {
|
} catch {
|
||||||
console2.log("Buy failed:", reason);
|
|
||||||
_recordState("BUY_FAIL", amount);
|
_recordState("BUY_FAIL", amount);
|
||||||
}
|
}
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _executeSell(uint256 runIndex, uint256 tradeIndex) internal {
|
function _executeSell(uint256 runIndex, uint256 tradeIndex) internal {
|
||||||
uint256 amount = _getTradeAmount(runIndex, tradeIndex, false);
|
uint256 kraikenBal = kraiken.balanceOf(trader);
|
||||||
if (amount == 0 || kraiken.balanceOf(trader) < amount) return;
|
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);
|
vm.startPrank(trader);
|
||||||
kraiken.transfer(address(swapExecutor), amount);
|
kraiken.transfer(address(swapExecutor), amount);
|
||||||
try swapExecutor.executeSell(amount, trader) returns (uint256 actualAmount) {
|
try swapExecutor.executeSell(amount, trader) returns (uint256 actualAmount) {
|
||||||
|
|
@ -222,33 +263,79 @@ contract StreamlinedFuzzing is Script {
|
||||||
}
|
}
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _executeStakingOperation(uint256, uint256, uint256) internal {
|
function _executeStakingOperation(uint256 runIndex, uint256 tradeIndex, uint256 stakingBias) internal {
|
||||||
// Staking operations disabled for now - interface needs updating
|
// Need KRAIKEN to stake — use the staker address, not the trader
|
||||||
// TODO: Update to use correct Stake contract interface
|
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 {
|
function _tryRecenter() internal {
|
||||||
vm.warp(block.timestamp + 1 hours);
|
vm.warp(block.timestamp + 1 hours);
|
||||||
vm.roll(block.number + 1); // Advance block
|
vm.roll(block.number + 1);
|
||||||
vm.prank(fees);
|
vm.prank(fees);
|
||||||
try lm.recenter{gas: 50_000_000}() {
|
try lm.recenter{ gas: 50_000_000 }() {
|
||||||
lastRecenterBlock = block.number;
|
lastRecenterBlock = block.number;
|
||||||
_recordState("RECENTER", 0);
|
_recordState("RECENTER", 0);
|
||||||
} catch {}
|
} 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _liquidateTraderHoldings() internal {
|
function _liquidateTraderHoldings() internal {
|
||||||
uint256 remaining = kraiken.balanceOf(trader);
|
uint256 remaining = kraiken.balanceOf(trader);
|
||||||
uint256 attempts;
|
uint256 attempts;
|
||||||
|
|
||||||
// Repeatedly sell down inventory, respecting liquidity limits in SwapExecutor
|
while (remaining > 0 && attempts < 20) {
|
||||||
while (remaining > 0 && attempts < 10) {
|
|
||||||
uint256 prevRemaining = remaining;
|
uint256 prevRemaining = remaining;
|
||||||
|
|
||||||
vm.startPrank(trader);
|
vm.startPrank(trader);
|
||||||
|
|
@ -256,127 +343,128 @@ contract StreamlinedFuzzing is Script {
|
||||||
try swapExecutor.executeSell(remaining, trader) returns (uint256 actualAmount) {
|
try swapExecutor.executeSell(remaining, trader) returns (uint256 actualAmount) {
|
||||||
if (actualAmount == 0) {
|
if (actualAmount == 0) {
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
console2.log("Liquidity liquidation halted: sell returned 0");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch Error(string memory reason) {
|
|
||||||
vm.stopPrank();
|
|
||||||
console2.log("Liquidity liquidation failed:", reason);
|
|
||||||
break;
|
|
||||||
} catch {
|
} catch {
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
console2.log("Liquidity liquidation failed with unknown error");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
remaining = kraiken.balanceOf(trader);
|
// Recenter between liquidation attempts to unlock more liquidity
|
||||||
if (remaining >= prevRemaining) {
|
if (attempts % 3 == 2) {
|
||||||
console2.log("Liquidity liquidation made no progress; remaining KRAIKEN:", remaining);
|
_tryRecenter();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remaining = kraiken.balanceOf(trader);
|
||||||
|
if (remaining >= prevRemaining) break;
|
||||||
|
|
||||||
unchecked {
|
unchecked {
|
||||||
attempts++;
|
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 {
|
function _recordState(string memory action, uint256 amount) internal {
|
||||||
// Build CSV row in parts to avoid stack too deep
|
|
||||||
string memory row = _buildRowPart1(action, amount);
|
string memory row = _buildRowPart1(action, amount);
|
||||||
row = string(abi.encodePacked(row, _buildRowPart2()));
|
row = string(abi.encodePacked(row, _buildRowPart2()));
|
||||||
row = string(abi.encodePacked(row, _buildRowPart3()));
|
row = string(abi.encodePacked(row, _buildRowPart3()));
|
||||||
|
|
||||||
vm.writeLine(csvFilename, row);
|
vm.writeLine(csvFilename, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _buildRowPart1(string memory action, uint256 amount) internal view returns (string memory) {
|
function _buildRowPart1(string memory action, uint256 amount) internal view returns (string memory) {
|
||||||
(, int24 tick,,,,,) = pool.slot0();
|
(, int24 tick,,,,,) = pool.slot0();
|
||||||
|
|
||||||
// Get floor position
|
|
||||||
(uint128 floorLiq, int24 floorLower, int24 floorUpper) = lm.positions(ThreePositionStrategy.Stage.FLOOR);
|
(uint128 floorLiq, int24 floorLower, int24 floorUpper) = lm.positions(ThreePositionStrategy.Stage.FLOOR);
|
||||||
|
|
||||||
return string(abi.encodePacked(
|
return string(
|
||||||
action, ",",
|
abi.encodePacked(
|
||||||
vm.toString(amount), ",",
|
action,
|
||||||
vm.toString(tick), ",",
|
",",
|
||||||
vm.toString(floorLower), ",",
|
vm.toString(amount),
|
||||||
vm.toString(floorUpper), ",",
|
",",
|
||||||
vm.toString(uint256(floorLiq)), ","
|
vm.toString(tick),
|
||||||
));
|
",",
|
||||||
|
vm.toString(floorLower),
|
||||||
|
",",
|
||||||
|
vm.toString(floorUpper),
|
||||||
|
",",
|
||||||
|
vm.toString(uint256(floorLiq)),
|
||||||
|
","
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _buildRowPart2() internal view returns (string memory) {
|
function _buildRowPart2() internal view returns (string memory) {
|
||||||
// Get anchor and discovery positions
|
|
||||||
(uint128 anchorLiq, int24 anchorLower, int24 anchorUpper) = lm.positions(ThreePositionStrategy.Stage.ANCHOR);
|
(uint128 anchorLiq, int24 anchorLower, int24 anchorUpper) = lm.positions(ThreePositionStrategy.Stage.ANCHOR);
|
||||||
(uint128 discoveryLiq, int24 discoveryLower, int24 discoveryUpper) = lm.positions(ThreePositionStrategy.Stage.DISCOVERY);
|
(uint128 discoveryLiq, int24 discoveryLower, int24 discoveryUpper) = lm.positions(ThreePositionStrategy.Stage.DISCOVERY);
|
||||||
|
|
||||||
return string(abi.encodePacked(
|
return string(
|
||||||
vm.toString(anchorLower), ",",
|
abi.encodePacked(
|
||||||
vm.toString(anchorUpper), ",",
|
vm.toString(anchorLower),
|
||||||
vm.toString(uint256(anchorLiq)), ",",
|
",",
|
||||||
vm.toString(discoveryLower), ",",
|
vm.toString(anchorUpper),
|
||||||
vm.toString(discoveryUpper), ",",
|
",",
|
||||||
vm.toString(uint256(discoveryLiq)), ","
|
vm.toString(uint256(anchorLiq)),
|
||||||
));
|
",",
|
||||||
|
vm.toString(discoveryLower),
|
||||||
|
",",
|
||||||
|
vm.toString(discoveryUpper),
|
||||||
|
",",
|
||||||
|
vm.toString(uint256(discoveryLiq)),
|
||||||
|
","
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _buildRowPart3() internal view returns (string memory) {
|
function _buildRowPart3() internal view returns (string memory) {
|
||||||
// Get balances and fees
|
|
||||||
uint256 ethBalance = weth.balanceOf(trader);
|
uint256 ethBalance = weth.balanceOf(trader);
|
||||||
uint256 kraikenBalance = kraiken.balanceOf(trader);
|
uint256 kraikenBalance = kraiken.balanceOf(trader);
|
||||||
|
|
||||||
(uint128 fees0, uint128 fees1) = pool.protocolFees();
|
(uint128 fees0, uint128 fees1) = pool.protocolFees();
|
||||||
uint256 deltaFees0 = fees0 > totalFees0 ? fees0 - totalFees0 : 0;
|
uint256 deltaFees0 = fees0 > totalFees0 ? fees0 - totalFees0 : 0;
|
||||||
uint256 deltaFees1 = fees1 > totalFees1 ? fees1 - totalFees1 : 0;
|
uint256 deltaFees1 = fees1 > totalFees1 ? fees1 - totalFees1 : 0;
|
||||||
|
|
||||||
return string(abi.encodePacked(
|
return string(
|
||||||
vm.toString(ethBalance), ",",
|
abi.encodePacked(
|
||||||
vm.toString(kraikenBalance), ",",
|
vm.toString(ethBalance), ",", vm.toString(kraikenBalance), ",", "0,", vm.toString(deltaFees0), ",", vm.toString(deltaFees1), ",", "0"
|
||||||
"0,", // vwap placeholder
|
)
|
||||||
vm.toString(deltaFees0), ",",
|
);
|
||||||
vm.toString(deltaFees1), ",",
|
|
||||||
"0" // recenter flag placeholder - no newline here
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _generateScenarioId() internal view returns (string memory) {
|
function _generateScenarioId() internal view returns (string memory) {
|
||||||
uint256 rand = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao)));
|
uint256 rand = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao)));
|
||||||
bytes memory chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
bytes memory chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
bytes memory result = new bytes(4);
|
bytes memory result = new bytes(4);
|
||||||
|
|
||||||
for (uint256 i = 0; i < 4; i++) {
|
for (uint256 i = 0; i < 4; i++) {
|
||||||
result[i] = chars[rand % chars.length];
|
result[i] = chars[rand % chars.length];
|
||||||
rand = rand / chars.length;
|
rand = rand / chars.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(result);
|
return string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _padNumber(uint256 num, uint256 digits) internal pure returns (string memory) {
|
function _padNumber(uint256 num, uint256 digits) internal pure returns (string memory) {
|
||||||
string memory numStr = vm.toString(num);
|
string memory numStr = vm.toString(num);
|
||||||
bytes memory numBytes = bytes(numStr);
|
bytes memory numBytes = bytes(numStr);
|
||||||
|
|
||||||
if (numBytes.length >= digits) {
|
if (numBytes.length >= digits) {
|
||||||
return numStr;
|
return numStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes memory result = new bytes(digits);
|
bytes memory result = new bytes(digits);
|
||||||
uint256 padding = digits - numBytes.length;
|
uint256 padding = digits - numBytes.length;
|
||||||
|
|
||||||
for (uint256 i = 0; i < padding; i++) {
|
for (uint256 i = 0; i < padding; i++) {
|
||||||
result[i] = '0';
|
result[i] = "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint256 i = 0; i < numBytes.length; i++) {
|
for (uint256 i = 0; i < numBytes.length; i++) {
|
||||||
result[padding + i] = numBytes[i];
|
result[padding + i] = numBytes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(result);
|
return string(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
pragma solidity ^0.8.19;
|
pragma solidity ^0.8.19;
|
||||||
|
|
||||||
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
|
import { Kraiken } from "../../src/Kraiken.sol";
|
||||||
import {IWETH9} from "../../src/interfaces/IWETH9.sol";
|
|
||||||
import {Kraiken} from "../../src/Kraiken.sol";
|
import { ThreePositionStrategy } from "../../src/abstracts/ThreePositionStrategy.sol";
|
||||||
import {TickMath} from "@aperture/uni-v3-lib/TickMath.sol";
|
import { IWETH9 } from "../../src/interfaces/IWETH9.sol";
|
||||||
import {LiquidityBoundaryHelper} from "../../test/helpers/LiquidityBoundaryHelper.sol";
|
import { LiquidityBoundaryHelper } from "../../test/helpers/LiquidityBoundaryHelper.sol";
|
||||||
import {ThreePositionStrategy} from "../../src/abstracts/ThreePositionStrategy.sol";
|
import { TickMath } from "@aperture/uni-v3-lib/TickMath.sol";
|
||||||
|
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title SwapExecutor
|
* @title SwapExecutor
|
||||||
* @notice Helper contract to execute swaps on Uniswap V3 pools for analysis scripts
|
* @notice Helper contract to execute swaps on Uniswap V3 pools for analysis scripts.
|
||||||
* @dev Extracted from analysis scripts to avoid code duplication
|
* @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 {
|
contract SwapExecutor {
|
||||||
IUniswapV3Pool public pool;
|
IUniswapV3Pool public pool;
|
||||||
|
|
@ -19,93 +23,55 @@ contract SwapExecutor {
|
||||||
Kraiken public harberg;
|
Kraiken public harberg;
|
||||||
bool public token0isWeth;
|
bool public token0isWeth;
|
||||||
ThreePositionStrategy public liquidityManager;
|
ThreePositionStrategy public liquidityManager;
|
||||||
|
bool public uncapped;
|
||||||
constructor(IUniswapV3Pool _pool, IWETH9 _weth, Kraiken _harberg, bool _token0isWeth, ThreePositionStrategy _liquidityManager) {
|
|
||||||
|
constructor(IUniswapV3Pool _pool, IWETH9 _weth, Kraiken _harberg, bool _token0isWeth, ThreePositionStrategy _liquidityManager, bool _uncapped) {
|
||||||
pool = _pool;
|
pool = _pool;
|
||||||
weth = _weth;
|
weth = _weth;
|
||||||
harberg = _harberg;
|
harberg = _harberg;
|
||||||
token0isWeth = _token0isWeth;
|
token0isWeth = _token0isWeth;
|
||||||
liquidityManager = _liquidityManager;
|
liquidityManager = _liquidityManager;
|
||||||
|
uncapped = _uncapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeBuy(uint256 amount, address recipient) external returns (uint256) {
|
function executeBuy(uint256 amount, address recipient) external returns (uint256) {
|
||||||
// Calculate maximum safe buy amount based on liquidity
|
uint256 safeAmount = amount;
|
||||||
uint256 maxBuyAmount = LiquidityBoundaryHelper.calculateBuyLimit(pool, liquidityManager, token0isWeth);
|
|
||||||
|
if (!uncapped) {
|
||||||
// Cap the amount to the safe limit
|
uint256 maxBuyAmount = LiquidityBoundaryHelper.calculateBuyLimit(pool, liquidityManager, token0isWeth);
|
||||||
uint256 safeAmount = amount > maxBuyAmount ? maxBuyAmount : amount;
|
safeAmount = amount > maxBuyAmount ? maxBuyAmount : amount;
|
||||||
|
}
|
||||||
// Skip if amount is zero
|
|
||||||
if (safeAmount == 0) return 0;
|
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;
|
bool zeroForOne = token0isWeth;
|
||||||
|
uint160 sqrtPriceLimitX96 = zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1;
|
||||||
// Set appropriate price limit based on swap direction
|
|
||||||
uint160 sqrtPriceLimitX96;
|
pool.swap(recipient, zeroForOne, int256(safeAmount), 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,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
return safeAmount;
|
return safeAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeSell(uint256 amount, address recipient) external returns (uint256) {
|
function executeSell(uint256 amount, address recipient) external returns (uint256) {
|
||||||
// Calculate maximum safe sell amount based on liquidity
|
uint256 safeAmount = amount;
|
||||||
uint256 maxSellAmount = LiquidityBoundaryHelper.calculateSellLimit(pool, liquidityManager, token0isWeth);
|
|
||||||
|
if (!uncapped) {
|
||||||
// Cap the amount to the safe limit
|
uint256 maxSellAmount = LiquidityBoundaryHelper.calculateSellLimit(pool, liquidityManager, token0isWeth);
|
||||||
uint256 safeAmount = amount > maxSellAmount ? maxSellAmount : amount;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.swap(
|
if (safeAmount == 0) return 0;
|
||||||
recipient,
|
|
||||||
zeroForOne,
|
bool zeroForOne = !token0isWeth;
|
||||||
int256(safeAmount),
|
uint160 sqrtPriceLimitX96 = zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1;
|
||||||
sqrtPriceLimitX96,
|
|
||||||
""
|
pool.swap(recipient, zeroForOne, int256(safeAmount), sqrtPriceLimitX96, "");
|
||||||
);
|
|
||||||
|
|
||||||
return safeAmount;
|
return safeAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback required for Uniswap V3 swaps
|
// Callback required for Uniswap V3 swaps
|
||||||
function uniswapV3SwapCallback(
|
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata) external {
|
||||||
int256 amount0Delta,
|
|
||||||
int256 amount1Delta,
|
|
||||||
bytes calldata
|
|
||||||
) external {
|
|
||||||
require(msg.sender == address(pool), "Unauthorized callback");
|
require(msg.sender == address(pool), "Unauthorized callback");
|
||||||
|
|
||||||
if (amount0Delta > 0) {
|
if (amount0Delta > 0) {
|
||||||
IWETH9(pool.token0()).transfer(address(pool), uint256(amount0Delta));
|
IWETH9(pool.token0()).transfer(address(pool), uint256(amount0Delta));
|
||||||
}
|
}
|
||||||
|
|
@ -113,4 +79,4 @@ contract SwapExecutor {
|
||||||
IWETH9(pool.token1()).transfer(address(pool), uint256(amount1Delta));
|
IWETH9(pool.token1()).transfer(address(pool), uint256(amount1Delta));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
#!/bin/bash
|
#!/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:
|
# Examples:
|
||||||
# ./run-fuzzing.sh BullMarketOptimizer runs=50
|
# ./run-fuzzing.sh BullMarketOptimizer runs=50
|
||||||
# ./run-fuzzing.sh WhaleOptimizer runs=20 staking=off
|
# ./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
|
# ./run-fuzzing.sh BullMarketOptimizer debugCSV # Opens HTML visualizer after generating CSVs
|
||||||
|
|
||||||
# Colors for output
|
# Colors for output
|
||||||
|
|
@ -15,61 +16,38 @@ BLUE='\033[0;34m'
|
||||||
NC='\033[0m' # No Color
|
NC='\033[0m' # No Color
|
||||||
BOLD='\033[1m'
|
BOLD='\033[1m'
|
||||||
|
|
||||||
# Configuration
|
# Configuration defaults
|
||||||
OPTIMIZER=${1:-BullMarketOptimizer}
|
OPTIMIZER=${1:-BullMarketOptimizer}
|
||||||
|
RUNS_VALUE=20
|
||||||
# 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
|
|
||||||
STAKING_ENABLED="true"
|
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"
|
BUYBIAS_VALUE="50"
|
||||||
if [[ $BUYBIAS == buybias=* ]]; then
|
|
||||||
BUYBIAS_VALUE=${BUYBIAS#buybias=}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse trades parameter
|
|
||||||
TRADES_VALUE="15"
|
TRADES_VALUE="15"
|
||||||
if [[ $TRADES == trades=* ]]; then
|
|
||||||
TRADES_VALUE=${TRADES#trades=}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse staking bias parameter
|
|
||||||
STAKINGBIAS_VALUE="80"
|
STAKINGBIAS_VALUE="80"
|
||||||
if [[ $STAKINGBIAS == stakingbias=* ]]; then
|
UNCAPPED_VALUE="true"
|
||||||
STAKINGBIAS_VALUE=${STAKINGBIAS#stakingbias=}
|
MINBUY_VALUE="20"
|
||||||
fi
|
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 -e "${GREEN}=== Fuzzing Analysis ===${NC}"
|
||||||
echo "Optimizer: $OPTIMIZER"
|
echo "Optimizer: $OPTIMIZER"
|
||||||
|
|
@ -80,6 +58,8 @@ if [ "$STAKING_ENABLED" = "true" ]; then
|
||||||
echo "Staking bias: $STAKINGBIAS_VALUE%"
|
echo "Staking bias: $STAKINGBIAS_VALUE%"
|
||||||
fi
|
fi
|
||||||
echo "Buy bias: $BUYBIAS_VALUE%"
|
echo "Buy bias: $BUYBIAS_VALUE%"
|
||||||
|
echo "Uncapped swaps: $UNCAPPED_VALUE"
|
||||||
|
echo "Buy range: ${MINBUY_VALUE}-${MAXBUY_VALUE} ETH"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Validate optimizer
|
# Validate optimizer
|
||||||
|
|
@ -107,6 +87,9 @@ ENABLE_STAKING=$STAKING_ENABLED \
|
||||||
BUY_BIAS=$BUYBIAS_VALUE \
|
BUY_BIAS=$BUYBIAS_VALUE \
|
||||||
TRADES_PER_RUN=$TRADES_VALUE \
|
TRADES_PER_RUN=$TRADES_VALUE \
|
||||||
STAKING_BIAS=$STAKINGBIAS_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
|
forge script analysis/StreamlinedFuzzing.s.sol:StreamlinedFuzzing --skip-simulation --gas-estimate-multiplier 300 -vv 2>&1
|
||||||
|
|
||||||
# Analysis complete
|
# Analysis complete
|
||||||
|
|
@ -117,7 +100,7 @@ echo -e "${GREEN}=== ANALYSIS COMPLETE ===${NC}"
|
||||||
NEW_CSVS=$(ls -1 analysis/fuzz-????-???.csv 2>/dev/null | sort)
|
NEW_CSVS=$(ls -1 analysis/fuzz-????-???.csv 2>/dev/null | sort)
|
||||||
# Get the scenario code from the first new file
|
# Get the scenario code from the first new file
|
||||||
if [ "$EXISTING_CSVS" != "$NEW_CSVS" ]; then
|
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
|
else
|
||||||
SCENARIO_CODE="UNKN"
|
SCENARIO_CODE="UNKN"
|
||||||
fi
|
fi
|
||||||
|
|
@ -135,9 +118,9 @@ for csv in analysis/fuzz-${SCENARIO_CODE}-*.csv; do
|
||||||
# Get INIT and FINAL rows
|
# Get INIT and FINAL rows
|
||||||
INIT_ETH=$(grep "^INIT," "$csv" | cut -d',' -f13)
|
INIT_ETH=$(grep "^INIT," "$csv" | cut -d',' -f13)
|
||||||
FINAL_ETH=$(grep "^FINAL," "$csv" | cut -d',' -f13)
|
FINAL_ETH=$(grep "^FINAL," "$csv" | cut -d',' -f13)
|
||||||
|
|
||||||
if [ ! -z "$INIT_ETH" ] && [ ! -z "$FINAL_ETH" ]; then
|
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")
|
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}"
|
echo -e "${GREEN} $csv: PROFITABLE (+${PROFIT_PCT}%)${NC}"
|
||||||
((PROFITABLE_COUNT++))
|
((PROFITABLE_COUNT++))
|
||||||
|
|
@ -159,7 +142,7 @@ echo -e "${GREEN}CSV files generated with scenario ID: ${SCENARIO_CODE}${NC}"
|
||||||
if [ "$DEBUG_CSV" = true ]; then
|
if [ "$DEBUG_CSV" = true ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${YELLOW}Launching HTML visualizer...${NC}"
|
echo -e "${YELLOW}Launching HTML visualizer...${NC}"
|
||||||
|
|
||||||
# Check if Python3 is available
|
# Check if Python3 is available
|
||||||
if command -v python3 &> /dev/null; then
|
if command -v python3 &> /dev/null; then
|
||||||
echo "Starting local server from analysis folder at http://localhost:8000"
|
echo "Starting local server from analysis folder at http://localhost:8000"
|
||||||
|
|
@ -168,20 +151,17 @@ if [ "$DEBUG_CSV" = true ]; then
|
||||||
cd analysis
|
cd analysis
|
||||||
python3 -m http.server 8000 --bind 127.0.0.1 &
|
python3 -m http.server 8000 --bind 127.0.0.1 &
|
||||||
SERVER_PID=$!
|
SERVER_PID=$!
|
||||||
|
|
||||||
# Try to open browser (cross-platform)
|
# Try to open browser (cross-platform)
|
||||||
sleep 1
|
sleep 1
|
||||||
if command -v open &> /dev/null; then
|
if command -v open &> /dev/null; then
|
||||||
# macOS
|
|
||||||
open "http://localhost:8000/run-visualizer.html"
|
open "http://localhost:8000/run-visualizer.html"
|
||||||
elif command -v xdg-open &> /dev/null; then
|
elif command -v xdg-open &> /dev/null; then
|
||||||
# Linux
|
|
||||||
xdg-open "http://localhost:8000/run-visualizer.html"
|
xdg-open "http://localhost:8000/run-visualizer.html"
|
||||||
elif command -v wslview &> /dev/null; then
|
elif command -v wslview &> /dev/null; then
|
||||||
# WSL
|
|
||||||
wslview "http://localhost:8000/run-visualizer.html"
|
wslview "http://localhost:8000/run-visualizer.html"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Press Ctrl+C to stop the server"
|
echo "Press Ctrl+C to stop the server"
|
||||||
wait $SERVER_PID
|
wait $SERVER_PID
|
||||||
|
|
@ -190,4 +170,4 @@ if [ "$DEBUG_CSV" = true ]; then
|
||||||
echo " cd analysis && python3 -m http.server 8000"
|
echo " cd analysis && python3 -m http.server 8000"
|
||||||
echo " Then open http://localhost:8000/run-visualizer.html in your browser"
|
echo " Then open http://localhost:8000/run-visualizer.html in your browser"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
264
onchain/analysis/run-parameter-sweep.sh
Executable file
264
onchain/analysis/run-parameter-sweep.sh
Executable file
|
|
@ -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}"
|
||||||
253
onchain/analysis/sweep-FULL-summary.csv
Normal file
253
onchain/analysis/sweep-FULL-summary.csv
Normal file
|
|
@ -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
|
||||||
|
|
|
@ -205,7 +205,7 @@ contract ReplayProfitableScenario is Test {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SwapExecutor executor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm);
|
SwapExecutor executor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm, false);
|
||||||
vm.prank(buyer);
|
vm.prank(buyer);
|
||||||
weth.transfer(address(executor), amount);
|
weth.transfer(address(executor), amount);
|
||||||
|
|
||||||
|
|
@ -222,7 +222,7 @@ contract ReplayProfitableScenario is Test {
|
||||||
if (amount == 0) return;
|
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);
|
vm.prank(seller);
|
||||||
kraiken.transfer(address(executor), amount);
|
kraiken.transfer(address(executor), amount);
|
||||||
|
|
||||||
|
|
|
||||||
45
onchain/test/mocks/ConfigurableOptimizer.sol
Normal file
45
onchain/test/mocks/ConfigurableOptimizer.sol
Normal file
|
|
@ -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)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -75,6 +75,25 @@
|
||||||
</div>
|
</div>
|
||||||
</FCard>
|
</FCard>
|
||||||
|
|
||||||
|
<FCard title="Protocol Stats">
|
||||||
|
<div class="cheats-form">
|
||||||
|
<template v-if="!statCollection.initialized">
|
||||||
|
<p class="cheats-hint">Loading protocol stats…</p>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="statCollection.statsError">
|
||||||
|
<p class="cheats-warning">{{ statCollection.statsError }}</p>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="liquidity-meta">
|
||||||
|
<span>Total Supply: {{ formatEth(statCollection.kraikenTotalSupply) }} KRK</span>
|
||||||
|
</div>
|
||||||
|
<div class="liquidity-meta">
|
||||||
|
<span>Total Staked: {{ formatEth(statCollection.outstandingStake) }} KRK</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</FCard>
|
||||||
|
|
||||||
<div class="cheats-grid">
|
<div class="cheats-grid">
|
||||||
<FCard title="Mint ETH">
|
<FCard title="Mint ETH">
|
||||||
<div class="cheats-form">
|
<div class="cheats-form">
|
||||||
|
|
@ -127,6 +146,7 @@ import FButton from '@/components/fcomponents/FButton.vue';
|
||||||
import FCard from '@/components/fcomponents/FCard.vue';
|
import FCard from '@/components/fcomponents/FCard.vue';
|
||||||
import FInput from '@/components/fcomponents/FInput.vue';
|
import FInput from '@/components/fcomponents/FInput.vue';
|
||||||
import { config } from '@/wagmi';
|
import { config } from '@/wagmi';
|
||||||
|
import { useStatCollection } from '@/composables/useStatCollection';
|
||||||
import { useToast } from 'vue-toastification';
|
import { useToast } from 'vue-toastification';
|
||||||
import { useAccount } from '@wagmi/vue';
|
import { useAccount } from '@wagmi/vue';
|
||||||
import { getChain, DEFAULT_CHAIN_ID } from '@/config';
|
import { getChain, DEFAULT_CHAIN_ID } from '@/config';
|
||||||
|
|
@ -149,6 +169,7 @@ import {
|
||||||
} from 'viem';
|
} from 'viem';
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const statCollection = useStatCollection();
|
||||||
const { address, chainId } = useAccount();
|
const { address, chainId } = useAccount();
|
||||||
const wagmiConfig: WagmiConfig = config;
|
const wagmiConfig: WagmiConfig = config;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue