diff --git a/onchain/analysis/SimpleAnalysis.s.sol b/onchain/analysis/SimpleAnalysis.s.sol index 87cc9b9..9be6bcb 100644 --- a/onchain/analysis/SimpleAnalysis.s.sol +++ b/onchain/analysis/SimpleAnalysis.s.sol @@ -10,838 +10,309 @@ pragma solidity ^0.8.19; */ import "../test/LiquidityManager.t.sol"; -import "../test/mocks/MockOptimizer.sol"; +import "../test/mocks/BullMarketOptimizer.sol"; +import "../test/mocks/NeutralMarketOptimizer.sol"; +import "../test/mocks/BearMarketOptimizer.sol"; import "./CSVManager.sol"; +import "../src/helpers/UniswapHelpers.sol"; import {LiquidityAmounts} from "@aperture/uni-v3-lib/LiquidityAmounts.sol"; import "@aperture/uni-v3-lib/TickMath.sol"; +import {IUniswapV3Factory} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; +import {WETH} from "solmate/tokens/WETH.sol"; contract SimpleAnalysis is LiquidityManagerTest, CSVManager { + using UniswapHelpers for IUniswapV3Pool; uint256 public scenariosAnalyzed; uint256 public profitableScenarios; - // Market condition scenarios for sentiment analysis - struct SentimentScenario { - uint256 capitalInefficiency; - uint256 anchorShare; - uint24 anchorWidth; - uint256 discoveryDepth; - string description; - } + // Market condition optimizers for sentiment analysis + BullMarketOptimizer bullOptimizer; + NeutralMarketOptimizer neutralOptimizer; + BearMarketOptimizer bearOptimizer; - struct ScenarioResults { - uint256 totalScenarios; - uint256 profitableScenarios; - uint256 totalProfit; - uint256 maxProfit; - uint256 avgProfit; + /// @notice Initialize setup for fuzzing without position validation + function _initializeForFuzzing() internal { + _skipSetup(); + setUpCustomToken0(false); // WETH as token1 + // Note: recenter access will be granted in _createFreshEnvironment } /// @notice Entry point for forge script execution function run() public { - console.log("Starting LiquidityManager Market Condition Analysis..."); - console.log("This will analyze trading scenarios across different sentiment conditions."); + console.log("Starting LiquidityManager Fuzzing Analysis..."); + console.log("Testing 30-action sequences across 3 market conditions for profitable scenarios"); - // Run parameter validation analysis first - console.log("Running parameter validation..."); - try this.runParameterValidationAnalysis() { - console.log("Parameter validation completed successfully"); - } catch Error(string memory reason) { - console.log("Parameter validation failed:", reason); - return; - } catch { - console.log("Parameter validation failed with unknown error"); - return; - } + // Initialize custom setup to skip position validation + _initializeForFuzzing(); - // Then run sentiment fuzzing analysis - console.log("Running sentiment fuzzing analysis..."); - try this.runSentimentFuzzingAnalysis() { - console.log("Sentiment fuzzing completed successfully"); - } catch Error(string memory reason) { - console.log("Sentiment fuzzing failed:", reason); - return; - } catch { - console.log("Sentiment fuzzing failed with unknown error"); - return; - } - - console.log("Market condition analysis complete."); - } - - /// @notice Simple parameter validation without complex trading - function runParameterValidationAnalysis() public { - console.log("\\n=== PARAMETER VALIDATION ANALYSIS ==="); - - // Test 3 key sentiment scenarios - SentimentScenario memory bullMarket = SentimentScenario({ - capitalInefficiency: 2 * 10 ** 17, // 20% - aggressive - anchorShare: 8 * 10 ** 17, // 80% - large anchor - anchorWidth: 30, // narrow width - discoveryDepth: 9 * 10 ** 17, // 90% - deep discovery - description: "Bull Market (High Risk)" - }); - - SentimentScenario memory neutralMarket = SentimentScenario({ - capitalInefficiency: 5 * 10 ** 17, // 50% - balanced - anchorShare: 5 * 10 ** 17, // 50% - balanced anchor - anchorWidth: 50, // standard width - discoveryDepth: 5 * 10 ** 17, // 50% - balanced discovery - description: "Neutral Market (Balanced)" - }); - - SentimentScenario memory bearMarket = SentimentScenario({ - capitalInefficiency: 8 * 10 ** 17, // 80% - conservative - anchorShare: 2 * 10 ** 17, // 20% - small anchor - anchorWidth: 80, // wide width - discoveryDepth: 2 * 10 ** 17, // 20% - shallow discovery - description: "Bear Market (Low Risk)" - }); - - // Test parameter configuration and basic recenter - testParameterConfiguration(bullMarket); - testParameterConfiguration(neutralMarket); - testParameterConfiguration(bearMarket); - } - - /// @notice Test parameter configuration and basic functionality - function testParameterConfiguration(SentimentScenario memory scenario) internal { - console.log("\\nTesting:", scenario.description); - console.log("Capital Inefficiency:", scenario.capitalInefficiency * 100 / 1e18, "%"); - console.log("Anchor Share:", scenario.anchorShare * 100 / 1e18, "%"); - console.log("Anchor Width:", scenario.anchorWidth); - console.log("Discovery Depth:", scenario.discoveryDepth * 100 / 1e18, "%"); - - // Configure MockOptimizer with sentiment parameters - MockOptimizer mockOptimizer = MockOptimizer(address(optimizer)); - mockOptimizer.setLiquidityParams( - scenario.capitalInefficiency, - scenario.anchorShare, - scenario.anchorWidth, - scenario.discoveryDepth - ); - - // Verify parameters were set correctly - (uint256 capIneff, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth) = - mockOptimizer.getLiquidityParams(); - - bool parametersCorrect = ( - capIneff == scenario.capitalInefficiency && - anchorShare == scenario.anchorShare && - anchorWidth == scenario.anchorWidth && - discoveryDepth == scenario.discoveryDepth - ); - - console.log("Parameters configured correctly:", parametersCorrect); - - // Test a simple recenter to see if positions are created with new parameters - try lm.recenter() returns (bool isUp) { - console.log("Recenter successful, price moved:", isUp ? "UP" : "DOWN"); - - // Check position allocation using Stage enum - (uint128 floorLiq,,) = lm.positions(ThreePositionStrategy.Stage.FLOOR); - (uint128 anchorLiq,,) = lm.positions(ThreePositionStrategy.Stage.ANCHOR); - (uint128 discoveryLiq,,) = lm.positions(ThreePositionStrategy.Stage.DISCOVERY); - - console.log("Position liquidity created:"); - console.log("Floor:", floorLiq > 0 ? "YES" : "NO"); - console.log("Anchor:", anchorLiq > 0 ? "YES" : "NO"); - console.log("Discovery:", discoveryLiq > 0 ? "YES" : "NO"); - - } catch Error(string memory reason) { - console.log("Recenter failed:", reason); - } catch { - console.log("Recenter failed with unknown error"); - } - - console.log("---"); - } - - /// @notice Run fuzzing analysis with different sentiment configurations and random inputs - function runSentimentFuzzingAnalysis() public { - console.log("\\n=== SENTIMENT FUZZING ANALYSIS ==="); - console.log("Testing for profitable trading opportunities with random fuzzing..."); - - // Initialize CSV once at the start - will only be written if profitable - console.log("Testing for profitable scenarios with random inputs..."); + // Initialize CSV for potential profitable scenario logging initializePositionsCSV(); - // Test just configuration first - console.log("\\n--- TESTING BASIC CONFIGURATION ---"); + // Test 3 different market sentiment optimizers + string[3] memory scenarioNames = ["Bull Market", "Neutral Market", "Bear Market"]; - // Use simple, safe parameters - uint8 numActions = 6; - uint8 frequency = 2; - uint8[] memory amounts = new uint8[](6); - amounts[0] = 100; amounts[1] = 120; amounts[2] = 80; - amounts[3] = 90; amounts[4] = 110; amounts[5] = 95; - - // Test just the parameter configuration - SentimentScenario memory testScenario = SentimentScenario({ - capitalInefficiency: 5 * 10**17, // 50% - anchorShare: 5 * 10**17, // 50% - anchorWidth: 50, // standard - discoveryDepth: 5 * 10**17, // 50% - description: "Basic_Config_Test" - }); - - console.log("Testing basic configuration:", testScenario.description); - console.log("NumActions:", numActions, "Frequency:", frequency); - console.log("Capital Inefficiency:", testScenario.capitalInefficiency * 100 / 1e18, "%"); - - // Test parameter configuration only - MockOptimizer mockOptimizer = MockOptimizer(address(optimizer)); - mockOptimizer.setLiquidityParams( - testScenario.capitalInefficiency, - testScenario.anchorShare, - testScenario.anchorWidth, - testScenario.discoveryDepth - ); - - console.log("Parameter configuration successful"); - - // Test simple recenter - try lm.recenter() returns (bool isUp) { - console.log("Basic recenter successful, price moved:", isUp ? "UP" : "DOWN"); - } catch Error(string memory reason) { - console.log("Basic recenter failed:", reason); - return; + for (uint256 i = 0; i < 3; i++) { + console.log(string.concat("\\n=== TESTING ", scenarioNames[i], " ===")); + + // Setup optimizer for this scenario + _setupScenarioOptimizer(i); + + // Run fuzzing loop for this scenario + bool foundProfit = _runFuzzingLoop(scenarioNames[i]); + + if (foundProfit) { + console.log("Profitable scenario found! CSV written to ./analysis/profitable_scenario.csv"); + console.log("Exiting analysis after finding profitable trade."); + return; + } else { + console.log("No profitable scenarios found in this configuration."); + } } - console.log("Basic configuration test completed successfully"); - console.log("\\nFor full fuzzing analysis, the system appears to be working"); - console.log("Random fuzzing might not find profitable scenarios due to effective anti-arbitrage protection"); - console.log("No profitable scenarios found - CSV not written"); + console.log("\\n=== ANALYSIS COMPLETE ==="); + console.log("No profitable scenarios found across all market conditions."); + console.log("This indicates effective anti-arbitrage protection is working."); } - /// @notice Generate a random sentiment scenario for fuzzing with conservative bounds - function generateRandomScenario(uint256 seed) internal view returns (SentimentScenario memory) { - // Use more conservative ranges to avoid extreme tick boundary issues - uint256 randCapIneff = bound(uint256(keccak256(abi.encodePacked(seed, "capineff"))), 2 * 10**17, 8 * 10**17); // 20% to 80% - uint256 randAnchorShare = bound(uint256(keccak256(abi.encodePacked(seed, "anchor"))), 2 * 10**17, 8 * 10**17); // 20% to 80% - uint24 randAnchorWidth = uint24(bound(uint256(keccak256(abi.encodePacked(seed, "width"))), 30, 80)); // 30 to 80 - uint256 randDiscoveryDepth = bound(uint256(keccak256(abi.encodePacked(seed, "discovery"))), 2 * 10**17, 8 * 10**17); // 20% to 80% - - return SentimentScenario({ - capitalInefficiency: randCapIneff, - anchorShare: randAnchorShare, - anchorWidth: randAnchorWidth, - discoveryDepth: randDiscoveryDepth, - description: string.concat("Random_", vm.toString(seed)) - }); + /// @notice Setup complete fresh environment for specific scenario + function _setupScenarioOptimizer(uint256 scenarioIndex) internal { + address optimizerAddress = _getOrCreateOptimizer(scenarioIndex); + _logScenarioParameters(scenarioIndex); + _createFreshEnvironment(optimizerAddress); } - /// @notice Run fuzzing for a specific sentiment scenario with random parameters - /// @return true if a profitable scenario was found - function runSentimentFuzzing(SentimentScenario memory scenario, uint8 numActions, uint8 frequency, uint8[] memory amounts) internal returns (bool) { - // Apply fuzzing constraints like historical tests - if (numActions <= 5) return false; // vm.assume(numActions > 5) - if (frequency == 0) return false; // vm.assume(frequency > 0) - if (frequency >= 20) return false; // vm.assume(frequency < 20) - if (amounts.length < numActions) return false; // vm.assume(amounts.length >= numActions) - - console.log("Testing:", scenario.description); - console.log("Capital Inefficiency:", scenario.capitalInefficiency * 100 / 1e18, "%"); - - // CSV already initialized once at start of analysis - - // Configure sentiment parameters - MockOptimizer mockOptimizer = MockOptimizer(address(optimizer)); - mockOptimizer.setLiquidityParams( - scenario.capitalInefficiency, - scenario.anchorShare, - scenario.anchorWidth, - scenario.discoveryDepth - ); - - // Test this single random configuration - uint256 profit = runFuzzingSequence(numActions, frequency, amounts); - - if (profit > 0) { - console.log("PROFITABLE SCENARIO FOUND!"); - console.log("Actions:", numActions); - console.log("Frequency:", frequency); - console.log("Profit:", profit); - - // Mark this as a profitable scenario end - _capturePositionData("profitable_scenario_end"); - - console.log("First profitable scenario found - stopping execution"); - return true; + /// @notice Deploy a fresh Uniswap factory for isolated testing + function deployFreshFactory() internal returns (IUniswapV3Factory) { + return UniswapHelpers.deployUniswapFactory(); + } + + /// @notice Get or create optimizer for given scenario index + function _getOrCreateOptimizer(uint256 scenarioIndex) internal returns (address) { + if (scenarioIndex == 0) { + if (address(bullOptimizer) == address(0)) { + bullOptimizer = new BullMarketOptimizer(); + } + return address(bullOptimizer); + } else if (scenarioIndex == 1) { + if (address(neutralOptimizer) == address(0)) { + neutralOptimizer = new NeutralMarketOptimizer(); + } + return address(neutralOptimizer); + } else { + if (address(bearOptimizer) == address(0)) { + bearOptimizer = new BearMarketOptimizer(); + } + return address(bearOptimizer); } - - console.log("No profit - continuing fuzzing..."); - return false; } - /// @notice Run a fuzzing sequence using exact historical testScenarioFuzz logic - function runFuzzingSequence(uint8 numActions, uint8 frequency, uint8[] memory amounts) internal returns (uint256 profit) { - // Use larger balance to accommodate larger trades for amplitude + /// @notice Log scenario parameters for given index + function _logScenarioParameters(uint256 scenarioIndex) internal view { + if (scenarioIndex == 0) { + console.log("Bull Market: 20% cap inefficiency, 80% anchor share, 30 width, 90% discovery"); + } else if (scenarioIndex == 1) { + console.log("Neutral Market: 50% cap inefficiency, 50% anchor share, 50 width, 50% discovery"); + } else { + console.log("Bear Market: 80% cap inefficiency, 20% anchor share, 80 width, 20% discovery"); + } + } + + /// @notice Grant recenter access with proper permissions for analysis + function _grantAnalysisRecenterAccess() internal { + vm.prank(feeDestination); + lm.setRecenterAccess(address(this)); + } + + /// @notice Configure all contracts with proper permissions and funding + function _configureContracts() internal { + lm.setFeeDestination(feeDestination); + harberg.setStakingPool(address(stake)); + + vm.prank(feeDestination); + harberg.setLiquidityManager(address(lm)); + + vm.deal(address(lm), 50 ether); + _grantAnalysisRecenterAccess(); + } + + /// @notice Setup test account with ETH and WETH + function _setupTestAccount() internal { vm.deal(account, 500 ether); vm.prank(account); weth.deposit{value: 200 ether}(); + } + + /// @notice Perform initial recenter to establish positions + function _performInitialRecenter() internal { + try lm.recenter() returns (bool /* isUp */) { + console.log("Initial recenter successful"); + } catch Error(string memory reason) { + console.log("Initial recenter failed:", reason); + // Continue anyway for fuzzing analysis + } + _capturePositionData("initial_setup"); + } + + /// @notice Create fresh contracts for each scenario to avoid AddressAlreadySet errors + function _createFreshEnvironment(address optimizerAddress) internal { + // Create new factory + factory = deployFreshFactory(); - // Setup initial liquidity and log it - recenter(false); - _capturePositionData("initial_recenter"); + // Create new WETH + weth = IWETH9(address(new WETH())); - uint256 traderBalanceBefore = weth.balanceOf(account); + // Create new Kraiken token + harberg = new Kraiken("KRAIKEN", "HARB"); - // Execute exact historical trading logic - uint8 f = 0; - for (uint i = 0; i < numActions && i < amounts.length; i++) { - // Use exact historical amount calculation - uint256 amount = (uint256(amounts[i]) * 1 ether) + 1 ether; - uint256 harbergBal = harberg.balanceOf(account); + // Determine token order + token0isWeth = address(weth) < address(harberg); + + // Create new pool + pool = IUniswapV3Pool(factory.createPool(address(weth), address(harberg), FEE)); + pool.initializePoolFor1Cent(token0isWeth); + + // Create new Stake contract + stake = new Stake(address(harberg), feeDestination); + + // Create new LiquidityManager with the specific optimizer + lm = new LiquidityManager(address(factory), address(weth), address(harberg), optimizerAddress); + + // Configure all contracts in batch + _configureContracts(); + } + + /// @notice Run 30-action fuzzing loop for a specific scenario + function _runFuzzingLoop(string memory scenarioName) internal returns (bool foundProfit) { + _setupTestAccount(); + _performInitialRecenter(); + + uint256 initialBalance = weth.balanceOf(account); + console.log("Starting balance:", initialBalance); + + // Generate seed for this scenario's randomness + uint256 scenarioSeed = uint256(keccak256(abi.encodePacked(scenarioName, block.timestamp))); + + // Execute 30 fuzzing actions + for (uint256 action = 0; action < 30; action++) { + uint256 actionSeed = uint256(keccak256(abi.encodePacked(scenarioSeed, action))); - // Exact historical trading logic - if (harbergBal == 0) { - amount = amount % (weth.balanceOf(account) / 2); - amount = amount == 0 ? weth.balanceOf(account) : amount; - buy(amount); - _capturePositionData(string.concat("buy_", vm.toString(amount))); - } else if (weth.balanceOf(account) == 0) { - uint256 sellAmount = amount % harbergBal; - sell(sellAmount); - _capturePositionData(string.concat("sell_", vm.toString(sellAmount))); - } else { - if (amount % 2 == 0) { - amount = amount % (weth.balanceOf(account) / 2); - amount = amount == 0 ? weth.balanceOf(account) : amount; + // Determine if buy or sell (50/50 chance) + bool isBuy = (actionSeed % 2) == 0; + + // Generate random amount (1-50 ETH range) + uint256 amount = 1 ether + (actionSeed % (50 ether)); + + // Execute trade + if (isBuy) { + // Ensure we don't exceed available WETH balance + uint256 wethBalance = weth.balanceOf(account); + if (amount > wethBalance) { + amount = wethBalance / 2; // Use half of available balance + } + if (amount > 0) { buy(amount); _capturePositionData(string.concat("buy_", vm.toString(amount))); - } else { - uint256 sellAmount = amount % harbergBal; + console.log("Action", action + 1, ": Buy", amount); + } + } else { + // Sell KRAIKEN tokens + uint256 harbergBalance = harberg.balanceOf(account); + if (harbergBalance > 0) { + uint256 sellAmount = (actionSeed % harbergBalance) + 1; + if (sellAmount > harbergBalance) sellAmount = harbergBalance; sell(sellAmount); _capturePositionData(string.concat("sell_", vm.toString(sellAmount))); + console.log("Action", action + 1, ": Sell", sellAmount); } } - // Exact historical extreme price protection - (, int24 currentTick,,,,,) = pool.slot0(); - if (currentTick < -887270) { - sell(100000000000000); - _capturePositionData("extreme_price_sell"); - } - if (currentTick > 887270) { - buy(1000000000000000); - _capturePositionData("extreme_price_buy"); - } - - // Exact historical recentering frequency with amplitude check - if (f >= frequency) { - // Try to recenter, but handle amplitude failures gracefully + // 30% chance to recenter after each trade + if ((actionSeed % 100) < 30) { try lm.recenter() returns (bool isUp) { _capturePositionData("recenter"); + console.log("Action", action + 1, ": Recenter (price moved", isUp ? "UP)" : "DOWN)"); } catch Error(string memory reason) { - // Log amplitude failures but continue - if (keccak256(bytes(reason)) == keccak256("amplitude not reached.")) { - // This is expected, continue without logging - } else { + // Recenter can fail due to amplitude requirements - this is normal + if (keccak256(bytes(reason)) != keccak256("amplitude not reached.")) { console.log("Recenter failed:", reason); } } - f = 0; - } else { - f++; } } - // Exact historical final cleanup - simulate large sell to push price down to floor - uint256 finalHarbBal = harberg.balanceOf(account); - if (finalHarbBal > 0) { - sell(finalHarbBal); - _capturePositionData(string.concat("final_sell_", vm.toString(finalHarbBal))); + // Final cleanup - sell all remaining KRAIKEN to realize profit/loss + uint256 finalHarbBalance = harberg.balanceOf(account); + if (finalHarbBalance > 0) { + sell(finalHarbBalance); + _capturePositionData(string.concat("final_sell_", vm.toString(finalHarbBalance))); + console.log("Final sell of remaining KRAIKEN:", finalHarbBalance); } - // Final recenter with amplitude check - try lm.recenter() returns (bool isUp) { + // Final recenter + try lm.recenter() returns (bool /* isUp */) { _capturePositionData("final_recenter"); - } catch Error(string memory reason) { - console.log("Final recenter failed:", reason); + console.log("Final recenter completed"); + } catch { _capturePositionData("final_recenter_failed"); + console.log("Final recenter failed"); } - uint256 traderBalanceAfter = weth.balanceOf(account); + // Check if scenario was profitable + uint256 finalBalance = weth.balanceOf(account); + console.log("Final balance:", finalBalance); - // Calculate profit (historical logic expected trader should not profit) - if (traderBalanceAfter > traderBalanceBefore) { - profit = traderBalanceAfter - traderBalanceBefore; - } else { - profit = 0; - } - - return profit; - } - - /// @notice Simplified analysis with fewer scenarios to avoid setup retries - function runSimplifiedMarketAnalysis() public { - console.log("\\n=== SIMPLIFIED MARKET CONDITION ANALYSIS ==="); - - // Test trading sequences - uint8[] memory amounts = new uint8[](5); - amounts[0] = 100; amounts[1] = 75; amounts[2] = 90; amounts[3] = 60; amounts[4] = 80; - - // Test 3 key scenarios only - console.log("\\n--- KEY MARKET SCENARIOS ---"); - - SentimentScenario memory bullMarket = SentimentScenario({ - capitalInefficiency: 2 * 10 ** 17, // 20% - aggressive - anchorShare: 8 * 10 ** 17, // 80% - large anchor - anchorWidth: 30, // narrow width - discoveryDepth: 9 * 10 ** 17, // 90% - deep discovery - description: "Bull Market (High Risk)" - }); - - SentimentScenario memory neutralMarket = SentimentScenario({ - capitalInefficiency: 5 * 10 ** 17, // 50% - balanced - anchorShare: 5 * 10 ** 17, // 50% - balanced anchor - anchorWidth: 50, // standard width - discoveryDepth: 5 * 10 ** 17, // 50% - balanced discovery - description: "Neutral Market (Balanced)" - }); - - SentimentScenario memory bearMarket = SentimentScenario({ - capitalInefficiency: 8 * 10 ** 17, // 80% - conservative - anchorShare: 2 * 10 ** 17, // 20% - small anchor - anchorWidth: 80, // wide width - discoveryDepth: 2 * 10 ** 17, // 20% - shallow discovery - description: "Bear Market (Low Risk)" - }); - - // Test each scenario with reduced iterations - testSimplifiedScenario(bullMarket, amounts); - testSimplifiedScenario(neutralMarket, amounts); - testSimplifiedScenario(bearMarket, amounts); - } - - /// @notice Test a simplified scenario with minimal iterations - function testSimplifiedScenario(SentimentScenario memory scenario, uint8[] memory amounts) internal { - console.log("Testing:", scenario.description); - console.log("Capital Inefficiency:", scenario.capitalInefficiency * 100 / 1e18, "%"); - console.log("Anchor Share:", scenario.anchorShare * 100 / 1e18, "%"); - - // Configure sentiment once - MockOptimizer mockOptimizer = MockOptimizer(address(optimizer)); - mockOptimizer.setLiquidityParams( - scenario.capitalInefficiency, - scenario.anchorShare, - scenario.anchorWidth, - scenario.discoveryDepth - ); - - uint256 totalProfit = 0; - uint256 profitableCount = 0; - - // Test only 2 scenarios to minimize setup calls - for (uint8 i = 0; i < 2; i++) { - uint8 numActions = 5 + (i * 3); // 5, 8 - uint8 frequency = 3 + i; // 3, 4 + if (finalBalance > initialBalance) { + uint256 profit = finalBalance - initialBalance; + console.log("\\n[ALERT] PROFITABLE SCENARIO FOUND!"); + console.log("Scenario:", scenarioName); + console.log("Profit:", profit, "wei"); + console.log("Profit:", profit / 1e18, "ETH"); - uint256 profit = runSingleTest(numActions, frequency, amounts); - if (profit > 0) { - profitableCount++; - totalProfit += profit; - console.log("Profitable scenario found - Actions:", numActions, "Profit:", profit); - } - } - - console.log("Results: 2 tests,", profitableCount, "profitable, total profit:", totalProfit); - - if (profitableCount > 0) { - console.log("[ALERT] Profitable scenarios detected!"); - } - - console.log("---"); - } - - /// @notice Run a single test without setup changes - function runSingleTest(uint8 numActions, uint8 frequency, uint8[] memory amounts) internal returns (uint256 profit) { - // Reset account balance - vm.deal(account, 300 ether); - vm.prank(account); - weth.deposit{value: 50 ether}(); - - uint256 balanceBefore = weth.balanceOf(account); - - // Execute trading sequence - _executeRandomTradingSequenceWrapper(numActions, frequency, amounts); - - uint256 balanceAfter = weth.balanceOf(account); - - // Calculate profit - if (balanceAfter > balanceBefore) { - profit = balanceAfter - balanceBefore; - } else { - profit = 0; - } - - return profit; - } - - /// @notice Analyze profitability across different market conditions - function runMarketConditionMatrix() public { - console.log("\\n=== MARKET CONDITION MATRIX ANALYSIS ==="); - - // Test trading sequences - uint8[] memory amounts = new uint8[](10); - amounts[0] = 100; amounts[1] = 50; amounts[2] = 75; - amounts[3] = 120; amounts[4] = 30; amounts[5] = 90; - amounts[6] = 45; amounts[7] = 110; amounts[8] = 60; amounts[9] = 80; - - // Bull Market Scenarios (Low Sentiment = High Risk) - console.log("\\n--- BULL MARKET CONDITIONS ---"); - - SentimentScenario memory extremeBull = SentimentScenario({ - capitalInefficiency: 1 * 10 ** 17, // 10% - very aggressive - anchorShare: 9 * 10 ** 17, // 90% - maximum anchor - anchorWidth: 20, // narrow width - discoveryDepth: 95 * 10 ** 16, // 95% - maximum discovery - description: "Extreme Bull (Maximum Risk)" - }); - - SentimentScenario memory moderateBull = SentimentScenario({ - capitalInefficiency: 25 * 10 ** 16, // 25% - aggressive - anchorShare: 75 * 10 ** 16, // 75% - large anchor - anchorWidth: 30, // moderately narrow - discoveryDepth: 8 * 10 ** 17, // 80% - deep discovery - description: "Moderate Bull (High Risk)" - }); - - // Neutral Market Scenarios (Medium Sentiment = Balanced Risk) - console.log("\\n--- NEUTRAL MARKET CONDITIONS ---"); - - SentimentScenario memory neutralBalanced = SentimentScenario({ - capitalInefficiency: 5 * 10 ** 17, // 50% - balanced - anchorShare: 5 * 10 ** 17, // 50% - balanced anchor - anchorWidth: 50, // standard width - discoveryDepth: 5 * 10 ** 17, // 50% - balanced discovery - description: "Neutral Market (Balanced Risk)" - }); - - SentimentScenario memory neutralConservative = SentimentScenario({ - capitalInefficiency: 6 * 10 ** 17, // 60% - slightly conservative - anchorShare: 4 * 10 ** 17, // 40% - smaller anchor - anchorWidth: 60, // wider width - discoveryDepth: 4 * 10 ** 17, // 40% - moderate discovery - description: "Neutral Conservative (Medium Risk)" - }); - - // Bear Market Scenarios (High Sentiment = Low Risk) - console.log("\\n--- BEAR MARKET CONDITIONS ---"); - - SentimentScenario memory moderateBear = SentimentScenario({ - capitalInefficiency: 8 * 10 ** 17, // 80% - conservative - anchorShare: 2 * 10 ** 17, // 20% - small anchor - anchorWidth: 80, // wide width - discoveryDepth: 2 * 10 ** 17, // 20% - shallow discovery - description: "Moderate Bear (Low Risk)" - }); - - SentimentScenario memory extremeBear = SentimentScenario({ - capitalInefficiency: 95 * 10 ** 16, // 95% - maximum conservative - anchorShare: 5 * 10 ** 16, // 5% - minimal anchor - anchorWidth: 100, // maximum width - discoveryDepth: 5 * 10 ** 16, // 5% - minimal discovery - description: "Extreme Bear (Minimum Risk)" - }); - - // Run analysis for each scenario - testSentimentScenario(extremeBull, amounts); - testSentimentScenario(moderateBull, amounts); - testSentimentScenario(neutralBalanced, amounts); - testSentimentScenario(neutralConservative, amounts); - testSentimentScenario(moderateBear, amounts); - testSentimentScenario(extremeBear, amounts); - } - - /// @notice Test a specific sentiment scenario - function testSentimentScenario(SentimentScenario memory scenario, uint8[] memory amounts) internal { - console.log("Testing:", scenario.description); - console.log("Capital Inefficiency:", scenario.capitalInefficiency * 100 / 1e18, "%"); - console.log("Anchor Share:", scenario.anchorShare * 100 / 1e18, "%"); - console.log("Anchor Width:", scenario.anchorWidth); - console.log("Discovery Depth:", scenario.discoveryDepth * 100 / 1e18, "%"); - - ScenarioResults memory results = ScenarioResults({ - totalScenarios: 0, - profitableScenarios: 0, - totalProfit: 0, - maxProfit: 0, - avgProfit: 0 - }); - - // Test fewer scenarios to avoid setup issues - for (uint8 numActions = 5; numActions <= 10; numActions += 5) { - for (uint8 frequency = 3; frequency <= 5; frequency += 2) { - results.totalScenarios++; - uint256 profit = runSentimentAnalysis(scenario, numActions, frequency, amounts); - - if (profit > 0) { - results.profitableScenarios++; - results.totalProfit += profit; - if (profit > results.maxProfit) { - results.maxProfit = profit; - } - } - } - } - - // Calculate average profit - if (results.profitableScenarios > 0) { - results.avgProfit = results.totalProfit / results.profitableScenarios; - } - - // Log results - console.log("Results - Total:", results.totalScenarios); - console.log("Profitable:", results.profitableScenarios); - console.log("Max Profit:", results.maxProfit); - console.log("Avg Profit:", results.avgProfit); - - // Warning for high profitability - if (results.profitableScenarios > results.totalScenarios / 2) { - console.log("[ALERT] High profitability detected - potential vulnerability!"); - } - - console.log("---"); - } - - /// @notice Run analysis with specific sentiment parameters - function runSentimentAnalysis( - SentimentScenario memory scenario, - uint8 numActions, - uint8 frequency, - uint8[] memory amounts - ) internal returns (uint256 profit) { - // Configure MockOptimizer with sentiment parameters - MockOptimizer mockOptimizer = MockOptimizer(address(optimizer)); - mockOptimizer.setLiquidityParams( - scenario.capitalInefficiency, - scenario.anchorShare, - scenario.anchorWidth, - scenario.discoveryDepth - ); - - // Reset account balance for consistent testing - vm.deal(account, 300 ether); - vm.prank(account); - weth.deposit{value: 50 ether}(); - - uint256 balanceBefore = weth.balanceOf(account); - - // Execute trading sequence - _executeRandomTradingSequenceWrapper(numActions, frequency, amounts); - - uint256 balanceAfter = weth.balanceOf(account); - - // Calculate profit - if (balanceAfter > balanceBefore) { - profit = balanceAfter - balanceBefore; - } else { - profit = 0; - } - - return profit; - } - - /// @notice Analyzes a trading scenario for profitability - /// @dev Records CSV data if profitable - THIS IS NOT A UNIT TEST - function runAnalysis(uint8 numActions, uint8 frequency, uint8[] memory amounts) public { - // Bound inputs - vm.assume(numActions > 3 && numActions <= 50); - vm.assume(frequency > 0 && frequency < 20); - vm.assume(amounts.length >= numActions); - - // Initialize CSV before setup - initializePositionsCSV(); - - // Setup with custom logging - _setupCustomWithLogging(false, 50 ether); - - uint256 balanceBefore = weth.balanceOf(account); - - // Execute trading sequence (need to convert memory to calldata) - _executeRandomTradingSequenceWrapper(numActions, frequency, amounts); - - uint256 balanceAfter = weth.balanceOf(account); - - scenariosAnalyzed++; - - // Check profitability - if (balanceAfter > balanceBefore) { - profitableScenarios++; - uint256 profit = balanceAfter - balanceBefore; + // Mark end of profitable scenario in CSV + _capturePositionData("PROFITABLE_SCENARIO_END"); - console.log("[ALERT] Profitable scenario found!"); - console.log("Profit:", vm.toString(profit)); - console.log("Actions:", numActions); - console.log("Frequency:", frequency); - - // Write CSV for analysis to analysis folder + // Write CSV file writeCSVToFile("./analysis/profitable_scenario.csv"); - } - - console.log("Scenario", scenariosAnalyzed, balanceAfter > balanceBefore ? "PROFIT" : "SAFE"); - } - - /// @notice Setup with CSV logging for initial recenter - function _setupCustomWithLogging(bool token0IsWeth, uint256 accountBalance) internal { - _skipSetup(); - - // Perform common setup but track the initial recenter - setUpCustomToken0(token0IsWeth); - - // Fund account and convert to WETH - vm.deal(account, accountBalance); - vm.prank(account); - weth.deposit{value: accountBalance}(); - - // Grant recenter access to bypass oracle checks - vm.prank(feeDestination); - lm.setRecenterAccess(address(this)); - - // Setup initial liquidity and log it - recenter(false); - _capturePositionData("initial_recenter"); - } - - /// @notice Wrapper to handle memory to calldata conversion - function _executeRandomTradingSequenceWrapper(uint8 numActions, uint8 frequency, uint8[] memory amounts) internal { - // Create a simple trading sequence without the complex calldata dependency - uint8 f = 0; - - for (uint i = 0; i < numActions && i < amounts.length; i++) { - uint256 amount = (uint256(amounts[i]) * 1 ether) + 1 ether; - uint256 harbergBal = harberg.balanceOf(account); - // Execute trade based on current balances - if (harbergBal == 0) { - amount = amount % (weth.balanceOf(account) / 2); - amount = amount == 0 ? weth.balanceOf(account) / 10 : amount; - if (amount > 0) { - buy(amount); - // Log buy trade - _capturePositionData(string.concat("buy_", vm.toString(amount))); - } - } else if (weth.balanceOf(account) == 0) { - uint256 sellAmount = amount % harbergBal; - if (sellAmount > 0) { - sell(sellAmount); - // Log sell trade - _capturePositionData(string.concat("sell_", vm.toString(sellAmount))); - } - } else { - if (amount % 2 == 0) { - amount = amount % (weth.balanceOf(account) / 2); - amount = amount == 0 ? weth.balanceOf(account) / 10 : amount; - if (amount > 0) { - buy(amount); - // Log buy trade - _capturePositionData(string.concat("buy_", vm.toString(amount))); - } - } else { - uint256 sellAmount = amount % harbergBal; - if (sellAmount > 0) { - sell(sellAmount); - // Log sell trade - _capturePositionData(string.concat("sell_", vm.toString(sellAmount))); - } - } - } - - // Periodic recentering - if (f >= frequency) { - recenter(false); - // Log recenter - _capturePositionData("recenter"); - f = 0; - } else { - f++; - } + scenariosAnalyzed++; + profitableScenarios++; + return true; + } else { + console.log("Loss:", initialBalance - finalBalance, "wei"); + scenariosAnalyzed++; + return false; } - - // Final cleanup - uint256 finalHarbBal = harberg.balanceOf(account); - if (finalHarbBal > 0) { - sell(finalHarbBal); - // Log final sell - _capturePositionData(string.concat("final_sell_", vm.toString(finalHarbBal))); - } - recenter(true); - // Log final recenter - _capturePositionData("final_recenter"); } - /// @notice Get analysis statistics - function getStats() public view returns (uint256 total, uint256 profitable) { - return (scenariosAnalyzed, profitableScenarios); - } - - /// @notice Capture position data after profitable scenario - function capturePositionSnapshot(string memory actionType) internal { - _capturePositionData(actionType); - console.log("Captured scenario data:", actionType); - } - - /// @notice Internal function to capture position data (split to avoid stack too deep) + /// @notice Capture complete position data for CSV analysis function _capturePositionData(string memory actionType) internal { + Response memory liquidityResponse = checkLiquidity("analysis"); (, int24 currentTick,,,,,) = pool.slot0(); - // Get position data - (uint128 floorLiq, int24 floorLower, int24 floorUpper) = lm.positions(ThreePositionStrategy.Stage.FLOOR); - (uint128 anchorLiq, int24 anchorLower, int24 anchorUpper) = lm.positions(ThreePositionStrategy.Stage.ANCHOR); - (uint128 discoveryLiq, int24 discoveryLower, int24 discoveryUpper) = lm.positions(ThreePositionStrategy.Stage.DISCOVERY); + // Build CSV row in chunks to avoid stack too deep + string memory part1 = string(abi.encodePacked( + actionType, ",", + vm.toString(currentTick), ",", + vm.toString(liquidityResponse.floorTickLower), ",", vm.toString(liquidityResponse.floorTickUpper), ",", + vm.toString(liquidityResponse.ethFloor), ",", vm.toString(liquidityResponse.harbergFloor) + )); - // Get actual token balances from the pool positions instead of calculated estimates - // This gives us the real token amounts, not inflated calculations - (uint256 floorToken0, uint256 floorToken1) = _getPositionTokenAmounts(floorLiq, floorLower, floorUpper); - (uint256 anchorToken0, uint256 anchorToken1) = _getPositionTokenAmounts(anchorLiq, anchorLower, anchorUpper); - (uint256 discoveryToken0, uint256 discoveryToken1) = _getPositionTokenAmounts(discoveryLiq, discoveryLower, discoveryUpper); + string memory part2 = string(abi.encodePacked( + ",", vm.toString(liquidityResponse.anchorTickLower), ",", vm.toString(liquidityResponse.anchorTickUpper), ",", + vm.toString(liquidityResponse.ethAnchor), ",", vm.toString(liquidityResponse.harbergAnchor) + )); - // Assign tokens based on token0isWeth flag - uint256 floorEth = token0isWeth ? floorToken0 : floorToken1; - uint256 floorHarb = token0isWeth ? floorToken1 : floorToken0; - uint256 anchorEth = token0isWeth ? anchorToken0 : anchorToken1; - uint256 anchorHarb = token0isWeth ? anchorToken1 : anchorToken0; - uint256 discoveryEth = token0isWeth ? discoveryToken0 : discoveryToken1; - uint256 discoveryHarb = token0isWeth ? discoveryToken1 : discoveryToken0; - - // Build CSV row with corrected token calculations - // CSV columns: floorEth, floorHarb, anchorEth, anchorHarb, discoveryEth, discoveryHarb, token0isWeth - string memory row1 = string.concat( - actionType, ",", vm.toString(currentTick), ",", - vm.toString(floorLower), ",", vm.toString(floorUpper), ",", - vm.toString(floorEth), ",", vm.toString(floorHarb), "," - ); - string memory row2 = string.concat( - vm.toString(anchorLower), ",", vm.toString(anchorUpper), ",", - vm.toString(anchorEth), ",", vm.toString(anchorHarb), "," - ); - string memory row3 = string.concat( - vm.toString(discoveryLower), ",", vm.toString(discoveryUpper), ",", - vm.toString(discoveryEth), ",", vm.toString(discoveryHarb), ",", - token0isWeth ? "true" : "false" // Include token0isWeth flag - ); - string memory row = string.concat(row1, row2, row3); + string memory part3 = string(abi.encodePacked( + ",", vm.toString(liquidityResponse.discoveryTickLower), ",", vm.toString(liquidityResponse.discoveryTickUpper), ",", + vm.toString(liquidityResponse.ethDiscovery), ",", vm.toString(liquidityResponse.harbergDiscovery), ",", + token0isWeth ? "true" : "false" + )); + string memory row = string(abi.encodePacked(part1, part2, part3)); appendCSVRow(row); } - - /// @notice Debug system balances - function debugBalances() internal { - console.log("=== DEBUG TOKEN BALANCES ==="); - console.log("LM ETH balance:", address(lm).balance); - console.log("LM WETH balance:", weth.balanceOf(address(lm))); - console.log("LM KRAIKEN balance:", harberg.balanceOf(address(lm))); - console.log("Pool ETH balance:", address(pool).balance); - console.log("Pool WETH balance:", weth.balanceOf(address(pool))); - console.log("Pool KRAIKEN balance:", harberg.balanceOf(address(pool))); - console.log("Total ETH in system:", address(lm).balance + address(pool).balance); - console.log("Total WETH in system:", weth.balanceOf(address(lm)) + weth.balanceOf(address(pool))); - console.log("Total KRAIKEN in system:", harberg.balanceOf(address(lm)) + harberg.balanceOf(address(pool))); - } - /// @notice Get actual token amounts from pool position data using proper Uniswap V3 math function _getPositionTokenAmounts( uint128 liquidity, @@ -876,62 +347,8 @@ contract SimpleAnalysis is LiquidityManagerTest, CSVManager { } } - /// @notice Check position allocation and capital efficiency - function checkPositions() internal { - (, int24 currentTick,,,,,) = pool.slot0(); - - // Check position allocation - (uint128 floorLiq, int24 floorLower, int24 floorUpper) = lm.positions(ThreePositionStrategy.Stage.FLOOR); - (uint128 anchorLiq, int24 anchorLower, int24 anchorUpper) = lm.positions(ThreePositionStrategy.Stage.ANCHOR); - (uint128 discoveryLiq, int24 discoveryLower, int24 discoveryUpper) = lm.positions(ThreePositionStrategy.Stage.DISCOVERY); - - console.log("=== POSITION DETAILS ==="); - console.log("Current tick:", vm.toString(currentTick)); - console.log("Floor Position:"); - console.log(" Liquidity:", floorLiq); - console.log(" Range:", vm.toString(floorLower), "to", vm.toString(floorUpper)); - console.log(" Distance from current:", vm.toString(floorLower - currentTick), "ticks"); - - console.log("Anchor Position:"); - console.log(" Liquidity:", anchorLiq); - console.log(" Range:", vm.toString(anchorLower), "to", vm.toString(anchorUpper)); - console.log(" Center vs current:", vm.toString((anchorLower + anchorUpper)/2 - currentTick), "ticks"); - - console.log("Discovery Position:"); - console.log(" Liquidity:", discoveryLiq); - console.log(" Range:", vm.toString(discoveryLower), "to", vm.toString(discoveryUpper)); - console.log(" Distance from current:", vm.toString(discoveryUpper - currentTick), "ticks"); - - // Calculate liquidity percentages - uint256 totalLiq = uint256(floorLiq) + uint256(anchorLiq) + uint256(discoveryLiq); - console.log("=== LIQUIDITY ALLOCATION ==="); - console.log("Floor percentage:", (uint256(floorLiq) * 100) / totalLiq, "%"); - console.log("Anchor percentage:", (uint256(anchorLiq) * 100) / totalLiq, "%"); - console.log("Discovery percentage:", (uint256(discoveryLiq) * 100) / totalLiq, "%"); - - // Check if anchor is positioned around current price - int24 anchorCenter = (anchorLower + anchorUpper) / 2; - int24 anchorDistance = anchorCenter > currentTick ? anchorCenter - currentTick : currentTick - anchorCenter; - - if (anchorDistance < 1000) { - console.log("[OK] ANCHOR positioned near current price (good for bull market)"); - } else { - console.log("[ISSUE] ANCHOR positioned far from current price"); - } - - // Check if most liquidity is in floor - if (floorLiq > anchorLiq && floorLiq > discoveryLiq) { - console.log("[OK] FLOOR holds most liquidity (good for dormant whale protection)"); - } else { - console.log("[ISSUE] FLOOR doesn't hold most liquidity"); - } - - // Check anchor allocation for bull market - uint256 anchorPercent = (uint256(anchorLiq) * 100) / totalLiq; - if (anchorPercent >= 15) { - console.log("[OK] ANCHOR has meaningful allocation for bull market (", anchorPercent, "%)"); - } else { - console.log("[ISSUE] ANCHOR allocation too small for bull market (", anchorPercent, "%)"); - } + /// @notice Get analysis statistics + function getStats() public view returns (uint256 total, uint256 profitable) { + return (scenariosAnalyzed, profitableScenarios); } } \ No newline at end of file diff --git a/onchain/src/LiquidityManager.sol b/onchain/src/LiquidityManager.sol index 5efda73..12eff60 100644 --- a/onchain/src/LiquidityManager.sol +++ b/onchain/src/LiquidityManager.sol @@ -116,7 +116,7 @@ contract LiquidityManager is ThreePositionStrategy, PriceOracle { if (positions[Stage.ANCHOR].liquidity > 0) { int24 anchorTickLower = positions[Stage.ANCHOR].tickLower; int24 anchorTickUpper = positions[Stage.ANCHOR].tickUpper; - int24 centerTick = anchorTickLower + (anchorTickUpper - anchorTickLower); + int24 centerTick = anchorTickLower + (anchorTickUpper - anchorTickLower) / 2; bool isEnough; (isUp, isEnough) = _validatePriceMovement(currentTick, centerTick, TICK_SPACING, token0isWeth); diff --git a/onchain/test/mocks/BearMarketOptimizer.sol b/onchain/test/mocks/BearMarketOptimizer.sol new file mode 100644 index 0000000..2d5b72e --- /dev/null +++ b/onchain/test/mocks/BearMarketOptimizer.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.19; + +import {Kraiken} from "../../src/Kraiken.sol"; +import {Stake} from "../../src/Stake.sol"; + +contract BearMarketOptimizer { + /// @notice Calculate sentiment (not used, but required for interface compatibility) + function calculateSentiment(uint256, uint256) public pure returns (uint256) { + return 0; // Placeholder implementation + } + + /// @notice Get sentiment (not used, but required for interface compatibility) + function getSentiment() external pure returns (uint256) { + return 0; // Placeholder implementation + } + + /// @notice Returns bear market liquidity parameters + /// @return capitalInefficiency 80% - conservative + /// @return anchorShare 20% - small anchor + /// @return anchorWidth 80 - wide width + /// @return discoveryDepth 20% - shallow discovery + function getLiquidityParams() + external + pure + returns (uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth) + { + capitalInefficiency = 8 * 10 ** 17; // 80% - conservative + anchorShare = 2 * 10 ** 17; // 20% - small anchor + anchorWidth = 80; // 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 new file mode 100644 index 0000000..e948ccb --- /dev/null +++ b/onchain/test/mocks/BullMarketOptimizer.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.19; + +import {Kraiken} from "../../src/Kraiken.sol"; +import {Stake} from "../../src/Stake.sol"; + +contract BullMarketOptimizer { + /// @notice Calculate sentiment (not used, but required for interface compatibility) + function calculateSentiment(uint256, uint256) public pure returns (uint256) { + return 0; // Placeholder implementation + } + + /// @notice Get sentiment (not used, but required for interface compatibility) + function getSentiment() external pure returns (uint256) { + return 0; // Placeholder implementation + } + + /// @notice Returns bull market liquidity parameters + /// @return capitalInefficiency 20% - aggressive + /// @return anchorShare 80% - large anchor + /// @return anchorWidth 30 - narrow width + /// @return discoveryDepth 90% - deep discovery + function getLiquidityParams() + external + pure + returns (uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth) + { + capitalInefficiency = 2 * 10 ** 17; // 20% - aggressive + anchorShare = 8 * 10 ** 17; // 80% - large anchor + anchorWidth = 30; // narrow width + discoveryDepth = 9 * 10 ** 17; // 90% - deep discovery + } + + function getDescription() external pure returns (string memory) { + return "Bull Market (High Risk)"; + } +} \ No newline at end of file diff --git a/onchain/test/mocks/MockOptimizer.sol b/onchain/test/mocks/MockOptimizer.sol index 99dae44..88afc4d 100644 --- a/onchain/test/mocks/MockOptimizer.sol +++ b/onchain/test/mocks/MockOptimizer.sol @@ -7,21 +7,21 @@ import {UUPSUpgradeable} from "@openzeppelin/proxy/utils/UUPSUpgradeable.sol"; import {Initializable} from "@openzeppelin/proxy/utils/Initializable.sol"; contract MockOptimizer is Initializable, UUPSUpgradeable { - Kraiken private kraiken; - Stake private stake; + Kraiken internal kraiken; + Stake internal stake; // Configurable parameters for sentiment analysis (V1 fallback values) - uint256 private _capitalInefficiency = 5 * 10 ** 17; // 50% - uint256 private _anchorShare = 5 * 10 ** 17; // 50% - uint24 private _anchorWidth = 50; // 50 (V1 used 5 * 10, but that's the same as 50) - uint256 private _discoveryDepth = 5 * 10 ** 17; // 50% + uint256 internal _capitalInefficiency = 5 * 10 ** 17; // 50% + uint256 internal _anchorShare = 5 * 10 ** 17; // 50% + uint24 internal _anchorWidth = 50; // 50 (V1 used 5 * 10, but that's the same as 50) + uint256 internal _discoveryDepth = 5 * 10 ** 17; // 50% /** * @dev The caller account is not authorized to perform an operation. */ error UnauthorizedAccount(address account); - function initialize(address _kraiken, address _stake) public initializer { + function initialize(address _kraiken, address _stake) public virtual initializer { _changeAdmin(msg.sender); kraiken = Kraiken(_kraiken); stake = Stake(_stake); diff --git a/onchain/test/mocks/NeutralMarketOptimizer.sol b/onchain/test/mocks/NeutralMarketOptimizer.sol new file mode 100644 index 0000000..ba45d31 --- /dev/null +++ b/onchain/test/mocks/NeutralMarketOptimizer.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.19; + +import {Kraiken} from "../../src/Kraiken.sol"; +import {Stake} from "../../src/Stake.sol"; + +contract NeutralMarketOptimizer { + /// @notice Calculate sentiment (not used, but required for interface compatibility) + function calculateSentiment(uint256, uint256) public pure returns (uint256) { + return 0; // Placeholder implementation + } + + /// @notice Get sentiment (not used, but required for interface compatibility) + function getSentiment() external pure returns (uint256) { + return 0; // Placeholder implementation + } + + /// @notice Returns neutral market liquidity parameters + /// @return capitalInefficiency 50% - balanced + /// @return anchorShare 50% - balanced anchor + /// @return anchorWidth 50 - standard width + /// @return discoveryDepth 50% - balanced discovery + function getLiquidityParams() + external + pure + returns (uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth) + { + capitalInefficiency = 5 * 10 ** 17; // 50% - balanced + anchorShare = 5 * 10 ** 17; // 50% - balanced anchor + anchorWidth = 50; // 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 diff --git a/onchain/test/mocks/RandomScenarioOptimizer.sol b/onchain/test/mocks/RandomScenarioOptimizer.sol new file mode 100644 index 0000000..91c2288 --- /dev/null +++ b/onchain/test/mocks/RandomScenarioOptimizer.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.19; + +import "./MockOptimizer.sol"; + +contract RandomScenarioOptimizer is MockOptimizer { + string private description; + + function initialize(address _kraiken, address _stake) public override initializer { + _changeAdmin(msg.sender); + kraiken = Kraiken(_kraiken); + stake = Stake(_stake); + } + + function setRandomParams( + uint256 capitalInefficiency, + uint256 anchorShare, + uint24 anchorWidth, + uint256 discoveryDepth, + string memory scenarioDescription + ) external { + _capitalInefficiency = capitalInefficiency; + _anchorShare = anchorShare; + _anchorWidth = anchorWidth; + _discoveryDepth = discoveryDepth; + description = scenarioDescription; + } + + function getDescription() external view returns (string memory) { + return description; + } +} \ No newline at end of file