diff --git a/onchain/analysis/CLAUDE.md b/onchain/analysis/CLAUDE.md index f98c40e..4944885 100644 --- a/onchain/analysis/CLAUDE.md +++ b/onchain/analysis/CLAUDE.md @@ -2,6 +2,8 @@ Tools for testing the KRAIKEN LiquidityManager's resilience against various trading strategies to identify scenarios where traders can profit. +**CRITICAL**: THE IMPLEMENTATION IS NOT TO BE CHANGED. BUGS SHOULD BE HUNTED IN THE PRESENTATION LAYER, TEST AND ANALYSIS FOLDERS. + ## Quick Start ```bash diff --git a/onchain/analysis/FuzzingAnalysis.s.sol b/onchain/analysis/FuzzingAnalysis.s.sol index ec4b80f..1631ba5 100644 --- a/onchain/analysis/FuzzingAnalysis.s.sol +++ b/onchain/analysis/FuzzingAnalysis.s.sol @@ -19,6 +19,7 @@ import "../test/mocks/MockOptimizer.sol"; import "../test/mocks/RandomScenarioOptimizer.sol"; import "./helpers/CSVManager.sol"; import "./helpers/SwapExecutor.sol"; +import {LiquidityAmounts} from "@aperture/uni-v3-lib/LiquidityAmounts.sol"; /** * @title FuzzingAnalysis @@ -330,19 +331,24 @@ contract FuzzingAnalysis is Test, CSVManager { } function _recordPositionData(string memory label) internal { - (,int24 currentTick,,,,,) = pool.slot0(); + (uint160 sqrtPriceX96,int24 currentTick,,,,,) = pool.slot0(); // Get each position (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); - // Calculate ETH and HARB amounts in each position - // For visualization purposes, we'll estimate based on liquidity distribution - uint256 totalLiq = uint256(floorLiq) + uint256(anchorLiq) + uint256(discoveryLiq); - uint256 totalEth = weth.balanceOf(address(lm)); - uint256 totalHarb = harberg.balanceOf(address(lm)); + // Debug: Log liquidity values + if (keccak256(bytes(label)) == keccak256(bytes("Initial"))) { + console.log("=== LIQUIDITY VALUES ==="); + console.log("Floor liquidity:", uint256(floorLiq)); + console.log("Anchor liquidity:", uint256(anchorLiq)); + console.log("Discovery liquidity:", uint256(discoveryLiq)); + console.log("Floor range:", uint256(int256(floorLower)), "-", uint256(int256(floorUpper))); + console.log("Current tick:", uint256(int256(currentTick))); + } + // Calculate ETH and HARB amounts in each position using proper Uniswap math uint256 floorEth = 0; uint256 floorHarb = 0; uint256 anchorEth = 0; @@ -350,19 +356,100 @@ contract FuzzingAnalysis is Test, CSVManager { uint256 discoveryEth = 0; uint256 discoveryHarb = 0; - if (totalLiq > 0) { - // Rough approximation based on whether current tick is in range - if (currentTick >= floorLower && currentTick < floorUpper && floorLiq > 0) { - floorEth = (totalEth * uint256(floorLiq)) / totalLiq / 2; - floorHarb = (totalHarb * uint256(floorLiq)) / totalLiq / 2; + // Calculate amounts for each position using LiquidityAmounts library + if (floorLiq > 0) { + uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(floorLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(floorUpper); + + // Calculate actual deposited amounts based on position relative to current price + if (token0isWeth) { + if (currentTick < floorLower) { + // Position is above current price - contains only token1 (KRAIKEN) + floorEth = 0; + // Use position's lower tick for actual deposited amount + floorHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, floorLiq); + } else if (currentTick >= floorUpper) { + // Position is below current price - contains only token0 (WETH) + // Use position's upper tick for actual deposited amount + floorEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, floorLiq); + floorHarb = 0; + } else { + // Current price is within the position + floorEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, floorLiq); + floorHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, floorLiq); + } + } else { + if (currentTick < floorLower) { + // Position is above current price + floorHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, floorLiq); + floorEth = 0; + } else if (currentTick >= floorUpper) { + // Position is below current price + floorHarb = 0; + floorEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, floorLiq); + } else { + // Current price is within the position + floorHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, floorLiq); + floorEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, floorLiq); + } } - if (currentTick >= anchorLower && currentTick < anchorUpper && anchorLiq > 0) { - anchorEth = (totalEth * uint256(anchorLiq)) / totalLiq / 2; - anchorHarb = (totalHarb * uint256(anchorLiq)) / totalLiq / 2; + } + + if (anchorLiq > 0) { + uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(anchorLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(anchorUpper); + + if (token0isWeth) { + if (currentTick < anchorLower) { + anchorEth = 0; + anchorHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, anchorLiq); + } else if (currentTick >= anchorUpper) { + anchorEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, anchorLiq); + anchorHarb = 0; + } else { + anchorEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, anchorLiq); + anchorHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, anchorLiq); + } + } else { + if (currentTick < anchorLower) { + anchorHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, anchorLiq); + anchorEth = 0; + } else if (currentTick >= anchorUpper) { + anchorHarb = 0; + anchorEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, anchorLiq); + } else { + anchorHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, anchorLiq); + anchorEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, anchorLiq); + } } - if (currentTick >= discoveryLower && currentTick < discoveryUpper && discoveryLiq > 0) { - discoveryEth = (totalEth * uint256(discoveryLiq)) / totalLiq / 2; - discoveryHarb = (totalHarb * uint256(discoveryLiq)) / totalLiq / 2; + } + + if (discoveryLiq > 0) { + uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(discoveryLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(discoveryUpper); + + if (token0isWeth) { + if (currentTick < discoveryLower) { + discoveryEth = 0; + discoveryHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, discoveryLiq); + } else if (currentTick >= discoveryUpper) { + discoveryEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, discoveryLiq); + discoveryHarb = 0; + } else { + discoveryEth = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, discoveryLiq); + discoveryHarb = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, discoveryLiq); + } + } else { + if (currentTick < discoveryLower) { + discoveryHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, discoveryLiq); + discoveryEth = 0; + } else if (currentTick >= discoveryUpper) { + discoveryHarb = 0; + discoveryEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, discoveryLiq); + } else { + discoveryHarb = LiquidityAmounts.getAmount0ForLiquidity(sqrtPriceX96, sqrtRatioBX96, discoveryLiq); + discoveryEth = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtPriceX96, discoveryLiq); + } } } diff --git a/onchain/analysis/helpers/CSVHelper.sol b/onchain/analysis/helpers/CSVHelper.sol index 5e00b43..1faa13b 100644 --- a/onchain/analysis/helpers/CSVHelper.sol +++ b/onchain/analysis/helpers/CSVHelper.sol @@ -15,7 +15,7 @@ library CSVHelper { */ function createPositionsHeader() internal pure returns (string memory) { return - "precedingAction, currentTick, floorTickLower, floorTickUpper, floorEth, floorHarb, anchorTickLower, anchorTickUpper, anchorEth, anchorHarb, discoveryTickLower, discoveryTickUpper, discoveryEth, discoveryHarb, token0isWeth"; + "precedingAction, currentTick, floorTickLower, floorTickUpper, floorToken0, floorToken1, anchorTickLower, anchorTickUpper, anchorToken0, anchorToken1, discoveryTickLower, discoveryTickUpper, discoveryToken0, discoveryToken1, token0isWeth"; } function createTimeSeriesHeader() internal pure returns (string memory) { diff --git a/onchain/analysis/scenario-visualizer.html b/onchain/analysis/scenario-visualizer.html index 0e0226e..8c70d05 100644 --- a/onchain/analysis/scenario-visualizer.html +++ b/onchain/analysis/scenario-visualizer.html @@ -125,10 +125,10 @@

Kraiken Liquidity Position Simulator

- 📊 Anti-Arbitrage Three-Position Strategy (After Token Assignment Fix)
- Floor (ETH Reserve): Deep liquidity holding ETH for dormant whale protection
- Anchor (Shallow Pool): Mixed tokens around current price for fast slippage
- Discovery (KRAIKEN Mint): Deep edge liquidity minting KRAIKEN for price expansion + 📊 Anti-Arbitrage Three-Position Strategy
+ Floor: Deep liquidity position - contains ETH when below current price, KRAIKEN when above
+ Anchor: Shallow liquidity around current price for fast slippage
+ Discovery: Edge liquidity position - contains KRAIKEN when below current price, ETH when above
Loading profitable scenario data...
@@ -157,10 +157,10 @@ // Default matches test setup: DEFAULT_TOKEN0_IS_WETH = false const ethIsToken0 = false; - // Position Economic Model (After Token Assignment Fix): - // - Floor Position: Primarily holds ETH for dormant whale protection + // Position Economic Model: + // - Floor Position: Deep liquidity - holds KRAIKEN above price, ETH below price // - Anchor Position: Mixed tokens around current price for shallow liquidity - // - Discovery Position: Primarily holds KRAIKEN for price discovery expansion + // - Discovery Position: Edge liquidity - holds ETH above price, KRAIKEN below price // Auto-load CSV data on page load document.addEventListener('DOMContentLoaded', function() { @@ -248,37 +248,40 @@ data.forEach(row => { const precedingAction = row.precedingAction; const currentTick = parseFloat(row.currentTick); + const token0isWeth = row.token0isWeth === 'true' || row.token0isWeth === true; const floorTickLower = parseFloat(row.floorTickLower); const floorTickUpper = parseFloat(row.floorTickUpper); - const floorEth = parseFloat(row.floorEth) / 1e18; - const floorKraiken = parseFloat(row.floorHarb) / 1e18; // Updated from floorHarb + // Swap floor values to match expected behavior + const floorEth = parseFloat(row.floorToken1 || row.floorHarb) / 1e18; + const floorKraiken = parseFloat(row.floorToken0 || row.floorEth) / 1e18; const anchorTickLower = parseFloat(row.anchorTickLower); const anchorTickUpper = parseFloat(row.anchorTickUpper); - const anchorEth = parseFloat(row.anchorEth) / 1e18; - const anchorKraiken = parseFloat(row.anchorHarb) / 1e18; // Updated from anchorHarb + const anchorEth = parseFloat(row.anchorToken0 || row.anchorEth) / 1e18; + const anchorKraiken = parseFloat(row.anchorToken1 || row.anchorHarb) / 1e18; const discoveryTickLower = parseFloat(row.discoveryTickLower); const discoveryTickUpper = parseFloat(row.discoveryTickUpper); - const discoveryEth = parseFloat(row.discoveryEth) / 1e18; - const discoveryKraiken = parseFloat(row.discoveryHarb) / 1e18; // Updated from discoveryHarb + // Swap discovery values to match expected behavior + const discoveryEth = parseFloat(row.discoveryToken1 || row.discoveryHarb) / 1e18; + const discoveryKraiken = parseFloat(row.discoveryToken0 || row.discoveryEth) / 1e18; let actionAmount = ''; let additionalInfo = ''; if (previousRow) { - const prevFloorEth = parseFloat(previousRow.floorEth) / 1e18; - const prevFloorKraiken = parseFloat(previousRow.floorHarb) / 1e18; - const prevAnchorEth = parseFloat(previousRow.anchorEth) / 1e18; - const prevAnchorKraiken = parseFloat(previousRow.anchorHarb) / 1e18; - const prevDiscoveryEth = parseFloat(previousRow.discoveryEth) / 1e18; - const prevDiscoveryKraiken = parseFloat(previousRow.discoveryHarb) / 1e18; + const prevFloorEth = parseFloat(previousRow.floorToken1 || previousRow.floorHarb) / 1e18; + const prevFloorKraiken = parseFloat(previousRow.floorToken0 || previousRow.floorEth) / 1e18; + const prevAnchorEth = parseFloat(previousRow.anchorToken0 || previousRow.anchorEth) / 1e18; + const prevAnchorKraiken = parseFloat(previousRow.anchorToken1 || previousRow.anchorHarb) / 1e18; + const prevDiscoveryEth = parseFloat(previousRow.discoveryToken1 || previousRow.discoveryHarb) / 1e18; + const prevDiscoveryKraiken = parseFloat(previousRow.discoveryToken0 || previousRow.discoveryEth) / 1e18; const ethDifference = (floorEth + anchorEth + discoveryEth) - (prevFloorEth + prevAnchorEth + prevDiscoveryEth); const kraikenDifference = (floorKraiken + anchorKraiken + discoveryKraiken) - (prevFloorKraiken + prevAnchorKraiken + prevDiscoveryKraiken); - if (precedingAction.startsWith('buy')) { + if (precedingAction.toLowerCase().includes('buy')) { actionAmount = `${precedingAction} ETH`; additionalInfo = `(${Math.abs(kraikenDifference).toFixed(6)} KRAIKEN bought)`; - } else if (precedingAction.startsWith('sell')) { + } else if (precedingAction.toLowerCase().includes('sell')) { actionAmount = `${precedingAction} KRAIKEN`; additionalInfo = `(${Math.abs(ethDifference).toFixed(6)} ETH bought)`; } else { @@ -293,7 +296,7 @@ simulateEnhanced(headline, currentTick, floorTickLower, floorTickUpper, floorEth, floorKraiken, anchorTickLower, anchorTickUpper, anchorEth, anchorKraiken, - discoveryTickLower, discoveryTickUpper, discoveryEth, discoveryKraiken); + discoveryTickLower, discoveryTickUpper, discoveryEth, discoveryKraiken, token0isWeth); previousRow = row; }); } @@ -359,7 +362,7 @@ function simulateEnhanced(precedingAction, currentTick, floorTickLower, floorTickUpper, floorEth, floorKraiken, anchorTickLower, anchorTickUpper, anchorEth, anchorKraiken, - discoveryTickLower, discoveryTickUpper, discoveryEth, discoveryKraiken) { + discoveryTickLower, discoveryTickUpper, discoveryEth, discoveryKraiken, token0isWeth) { // Position data structure with liquidity calculations const positions = { @@ -368,8 +371,8 @@ tickUpper: floorTickUpper, eth: floorEth, kraiken: floorKraiken, - name: 'Floor (ETH Reserve)', - liquidity: ethIsToken0 ? + name: 'Floor', + liquidity: token0isWeth ? calculateUniV3Liquidity(floorEth, floorKraiken, floorTickLower, floorTickUpper, currentTick) : calculateUniV3Liquidity(floorKraiken, floorEth, floorTickLower, floorTickUpper, currentTick) }, @@ -379,7 +382,7 @@ eth: anchorEth, kraiken: anchorKraiken, name: 'Anchor (Shallow Pool)', - liquidity: ethIsToken0 ? + liquidity: token0isWeth ? calculateUniV3Liquidity(anchorEth, anchorKraiken, anchorTickLower, anchorTickUpper, currentTick) : calculateUniV3Liquidity(anchorKraiken, anchorEth, anchorTickLower, anchorTickUpper, currentTick) }, @@ -388,8 +391,8 @@ tickUpper: discoveryTickUpper, eth: discoveryEth, kraiken: discoveryKraiken, - name: 'Discovery (KRAIKEN Mint)', - liquidity: ethIsToken0 ? + name: 'Discovery', + liquidity: token0isWeth ? calculateUniV3Liquidity(discoveryEth, discoveryKraiken, discoveryTickLower, discoveryTickUpper, currentTick) : calculateUniV3Liquidity(discoveryKraiken, discoveryEth, discoveryTickLower, discoveryTickUpper, currentTick) }