diff --git a/onchain/analysis/CLAUDE.md b/onchain/analysis/CLAUDE.md index 4944885..a49f128 100644 --- a/onchain/analysis/CLAUDE.md +++ b/onchain/analysis/CLAUDE.md @@ -6,6 +6,44 @@ Tools for testing the KRAIKEN LiquidityManager's resilience against various trad ## Quick Start +### Using run-fuzzing.sh (Recommended) + +The `run-fuzzing.sh` script provides an easy way to run fuzzing campaigns with different market optimizers: + +```bash +# Basic usage - run with specific optimizer +./analysis/run-fuzzing.sh BullMarketOptimizer + +# Specify number of runs (default: 50) +./analysis/run-fuzzing.sh WhaleOptimizer runs=100 + +# Specify trades per run (default: 20, actual will be ±5) +./analysis/run-fuzzing.sh BearMarketOptimizer runs=10 trades=50 + +# Debug mode - generates position tracking CSV (forces runs=1) +./analysis/run-fuzzing.sh NeutralMarketOptimizer debugCSV + +# Multiple parameters +./analysis/run-fuzzing.sh BullMarketOptimizer runs=25 trades=30 +``` + +**Available optimizers:** +- `BullMarketOptimizer` - Biased towards buying +- `NeutralMarketOptimizer` - Balanced trading +- `BearMarketOptimizer` - Biased towards selling +- `WhaleOptimizer` - Large position trading +- `MockOptimizer` - Test optimizer +- `RandomScenarioOptimizer` - Random behavior + +**Features:** +- Automatic results aggregation and summary generation +- Progress tracking with colored output +- Cumulative P&L calculation across all runs +- Automatic visualization launch for profitable scenarios +- Organized output in timestamped directories + +### Manual Fuzzing (Advanced) + ```bash # Run fuzzing analysis with default settings (100 runs per market) forge script analysis/FuzzingAnalysis.s.sol --ffi --via-ir @@ -19,8 +57,18 @@ TRACK_POSITIONS=true FUZZING_RUNS=50 forge script analysis/FuzzingAnalysis.s.sol ## Configuration +### run-fuzzing.sh Parameters +- **optimizer_class**: Required. The optimizer class to use (e.g., BullMarketOptimizer) +- **runs=N**: Optional. Number of fuzzing runs (default: 50) +- **trades=N**: Optional. Trades per run (default: 20, actual will be ±5) +- **debugCSV**: Optional. Enable debug mode with position tracking CSV (forces runs=1) + +### Environment Variables (Manual Mode) - **FUZZING_RUNS**: Number of random trading scenarios per market type (default: 100) - **TRACK_POSITIONS**: Enable detailed position tracking CSV output (default: false) +- **OPTIMIZER_CLASS**: The optimizer to use (default: BullMarketOptimizer) +- **TRADES_PER_RUN**: Number of trades per run (default: 20) +- **SEED_OFFSET**: Starting seed for random number generation (default: 0) ## How It Works @@ -32,6 +80,15 @@ TRACK_POSITIONS=true FUZZING_RUNS=50 forge script analysis/FuzzingAnalysis.s.sol ## Output Files +### Using run-fuzzing.sh +Each campaign creates a timestamped directory: `fuzzing_results_[optimizer]_[timestamp]/` +- `config.txt` - Campaign configuration +- `run_*.log` - Individual run logs +- `merged_profitable_scenarios.csv` - All profitable scenarios combined +- `summary.txt` - Campaign summary with statistics +- `debug_positions_*.csv` - Position tracking data (when debugCSV is used) + +### Manual Mode - `profitable_scenarios_[timestamp].csv` - Details of all profitable trading sequences - `positions_[scenario]_[seed].csv` - Liquidity position data (only with TRACK_POSITIONS=true) @@ -54,7 +111,22 @@ python3 -m http.server 8000 ## Components -- `FuzzingAnalysis.s.sol` - Main fuzzing script +- `run-fuzzing.sh` - Main campaign runner with automatic visualization +- `FuzzingAnalysis.s.sol` - Core fuzzing script - `helpers/SwapExecutor.sol` - Shared swap execution logic -- `CSVManager.sol` - CSV generation utilities -- `CSVHelper.sol` - CSV formatting helpers \ No newline at end of file +- `helpers/CSVManager.sol` - CSV generation utilities +- `helpers/CSVHelper.sol` - CSV formatting helpers + +## Example Campaign Comparison + +To run fuzzing campaigns comparing different market optimizers: + +```bash +# Run campaigns for all three market conditions +./analysis/run-fuzzing.sh BullMarketOptimizer runs=100 +./analysis/run-fuzzing.sh NeutralMarketOptimizer runs=100 +./analysis/run-fuzzing.sh BearMarketOptimizer runs=100 + +# Check results +cat fuzzing_results_*/summary.txt | grep -E "(Optimizer:|Success rate:|Average P&L)" +``` \ No newline at end of file diff --git a/onchain/analysis/FuzzingAnalysis.s.sol b/onchain/analysis/FuzzingAnalysis.s.sol index 1631ba5..1a48f03 100644 --- a/onchain/analysis/FuzzingAnalysis.s.sol +++ b/onchain/analysis/FuzzingAnalysis.s.sol @@ -331,7 +331,11 @@ contract FuzzingAnalysis is Test, CSVManager { } function _recordPositionData(string memory label) internal { - (uint160 sqrtPriceX96,int24 currentTick,,,,,) = pool.slot0(); + (,int24 currentTick,,,,,) = pool.slot0(); + + // Cap currentTick to avoid overflow in extreme cases + if (currentTick > 887000) currentTick = 887000; + if (currentTick < -887000) currentTick = -887000; // Get each position (uint128 floorLiq, int24 floorLower, int24 floorUpper) = lm.positions(ThreePositionStrategy.Stage.FLOOR); @@ -375,6 +379,7 @@ contract FuzzingAnalysis is Test, CSVManager { floorHarb = 0; } else { // Current price is within the position + uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(currentTick); floorEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, floorLiq); floorHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, floorLiq); } @@ -389,6 +394,7 @@ contract FuzzingAnalysis is Test, CSVManager { floorEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, floorLiq); } else { // Current price is within the position + uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(currentTick); floorHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, floorLiq); floorEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, floorLiq); } @@ -407,6 +413,7 @@ contract FuzzingAnalysis is Test, CSVManager { anchorEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, anchorLiq); anchorHarb = 0; } else { + uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(currentTick); anchorEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, anchorLiq); anchorHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, anchorLiq); } @@ -418,6 +425,7 @@ contract FuzzingAnalysis is Test, CSVManager { anchorHarb = 0; anchorEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, anchorLiq); } else { + uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(currentTick); anchorHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, anchorLiq); anchorEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, anchorLiq); } @@ -436,6 +444,7 @@ contract FuzzingAnalysis is Test, CSVManager { discoveryEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, discoveryLiq); discoveryHarb = 0; } else { + uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(currentTick); discoveryEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, discoveryLiq); discoveryHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, discoveryLiq); } @@ -447,6 +456,7 @@ contract FuzzingAnalysis is Test, CSVManager { discoveryHarb = 0; discoveryEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, discoveryLiq); } else { + uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(currentTick); discoveryHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, discoveryLiq); discoveryEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, discoveryLiq); } diff --git a/onchain/analysis/run-fuzzing.sh b/onchain/analysis/run-fuzzing.sh index 63e4300..aa21270 100755 --- a/onchain/analysis/run-fuzzing.sh +++ b/onchain/analysis/run-fuzzing.sh @@ -23,16 +23,18 @@ OPTIMIZER_CLASS="" TOTAL_RUNS=50 TRADES_PER_RUN=20 DEBUG_CSV=false +WHALE_MODE=false # Function to show usage show_usage() { - echo "Usage: $0 [runs=N] [trades=N] [debugCSV]" + echo "Usage: $0 [runs=N] [trades=N] [debugCSV] [whaleMode]" echo "" echo "Parameters:" echo " optimizer_class Required. The optimizer class to use" echo " runs=N Optional. Number of fuzzing runs (default: 50)" echo " trades=N Optional. Trades per run (default: 20, actual will be ±5)" echo " debugCSV Optional. Enable debug mode with position tracking CSV (forces runs=1)" + echo " whaleMode Optional. Enable whale-sized trades (20-80% of balance, 50-500 ETH funding)" echo "" echo "Examples:" echo " $0 BullMarketOptimizer" @@ -40,6 +42,7 @@ show_usage() { echo " $0 BearMarketOptimizer runs=10 trades=50" echo " $0 NeutralMarketOptimizer trades=30 runs=25" echo " $0 BullMarketOptimizer debugCSV" + echo " $0 WhaleOptimizer whaleMode runs=20" echo "" echo "Available optimizers:" echo " - BullMarketOptimizer" @@ -82,6 +85,9 @@ for arg in "$@"; do DEBUG_CSV=true TOTAL_RUNS=1 ;; + whaleMode) + WHALE_MODE=true + ;; *) echo "Error: Unknown parameter '$arg'" show_usage @@ -106,6 +112,9 @@ echo "Trades per run: $TRADES_PER_RUN (±5)" if [ "$DEBUG_CSV" = true ]; then echo -e "${YELLOW}Debug mode: ENABLED (position tracking CSV will be generated)${NC}" fi +if [ "$WHALE_MODE" = true ]; then + echo -e "${YELLOW}Whale mode: ENABLED (20-80% trades, 50-500 ETH funding)${NC}" +fi echo "Output directory: $OUTPUT_DIR" echo "" @@ -161,9 +170,9 @@ for i in $(seq 1 $TOTAL_RUNS); do # Use iteration number as seed offset to ensure different scenarios # Enable position tracking if debugCSV is set if [ "$DEBUG_CSV" = true ]; then - TRACK_POSITIONS=true SEED_OFFSET=$((i - 1)) OPTIMIZER_CLASS="$OPTIMIZER_CLASS" TRADES_PER_RUN="$TRADES_PER_RUN" FUZZING_RUNS=1 forge script FuzzingAnalysis.s.sol --ffi --via-ir --gas-limit 200000000 > "$OUTPUT_DIR/run_$i.log" 2>&1 + WHALE_MODE="$WHALE_MODE" TRACK_POSITIONS=true SEED_OFFSET=$((i - 1)) OPTIMIZER_CLASS="$OPTIMIZER_CLASS" TRADES_PER_RUN="$TRADES_PER_RUN" FUZZING_RUNS=1 forge script FuzzingAnalysis.s.sol --ffi --via-ir --gas-limit 200000000 > "$OUTPUT_DIR/run_$i.log" 2>&1 else - SEED_OFFSET=$((i - 1)) OPTIMIZER_CLASS="$OPTIMIZER_CLASS" TRADES_PER_RUN="$TRADES_PER_RUN" FUZZING_RUNS=1 forge script FuzzingAnalysis.s.sol --ffi --via-ir --gas-limit 200000000 > "$OUTPUT_DIR/run_$i.log" 2>&1 + WHALE_MODE="$WHALE_MODE" SEED_OFFSET=$((i - 1)) OPTIMIZER_CLASS="$OPTIMIZER_CLASS" TRADES_PER_RUN="$TRADES_PER_RUN" FUZZING_RUNS=1 forge script FuzzingAnalysis.s.sol --ffi --via-ir --gas-limit 200000000 > "$OUTPUT_DIR/run_$i.log" 2>&1 fi if [ $? -eq 0 ]; then diff --git a/onchain/test/mocks/BearMarketOptimizer.sol b/onchain/test/mocks/BearMarketOptimizer.sol index 2d5b72e..551c73b 100644 --- a/onchain/test/mocks/BearMarketOptimizer.sol +++ b/onchain/test/mocks/BearMarketOptimizer.sol @@ -27,11 +27,11 @@ contract BearMarketOptimizer { { capitalInefficiency = 8 * 10 ** 17; // 80% - conservative anchorShare = 2 * 10 ** 17; // 20% - small anchor - anchorWidth = 80; // wide width + anchorWidth = 1000; // wide width discoveryDepth = 2 * 10 ** 17; // 20% - shallow discovery } function getDescription() external pure returns (string memory) { return "Bear Market (Low Risk)"; } -} \ No newline at end of file +} diff --git a/onchain/test/mocks/BullMarketOptimizer.sol b/onchain/test/mocks/BullMarketOptimizer.sol index e686931..63120db 100644 --- a/onchain/test/mocks/BullMarketOptimizer.sol +++ b/onchain/test/mocks/BullMarketOptimizer.sol @@ -27,7 +27,7 @@ contract BullMarketOptimizer { { capitalInefficiency = 0; // 10% - very aggressive anchorShare = 1e18; // 95% - massive anchor position - anchorWidth = 50; // moderate width (was 10) + anchorWidth = 1000; // moderate width (was 10) discoveryDepth = 1e18; // 5% - minimal discovery } diff --git a/onchain/test/mocks/NeutralMarketOptimizer.sol b/onchain/test/mocks/NeutralMarketOptimizer.sol index ba45d31..75dbaca 100644 --- a/onchain/test/mocks/NeutralMarketOptimizer.sol +++ b/onchain/test/mocks/NeutralMarketOptimizer.sol @@ -27,11 +27,11 @@ contract NeutralMarketOptimizer { { capitalInefficiency = 5 * 10 ** 17; // 50% - balanced anchorShare = 5 * 10 ** 17; // 50% - balanced anchor - anchorWidth = 50; // standard width + anchorWidth = 1000; // standard width discoveryDepth = 5 * 10 ** 17; // 50% - balanced discovery } function getDescription() external pure returns (string memory) { return "Neutral Market (Balanced)"; } -} \ No newline at end of file +}