From 5db3ecb3d705e70633fccf1deda0c3af815c271e Mon Sep 17 00:00:00 2001 From: giteadmin Date: Mon, 9 Dec 2024 23:08:24 +0100 Subject: [PATCH] wip --- onchain/src/helpers/UniswapHelpers.sol | 21 +++--- onchain/test/Simulations.t.sol | 86 +++++++++++++++++++++--- onchain/test/data/scenarios.json | 29 +++++++- onchain/test/helpers/UniswapTestBase.sol | 77 +++++++++++++-------- 4 files changed, 163 insertions(+), 50 deletions(-) diff --git a/onchain/src/helpers/UniswapHelpers.sol b/onchain/src/helpers/UniswapHelpers.sol index 0878aab..bef560e 100644 --- a/onchain/src/helpers/UniswapHelpers.sol +++ b/onchain/src/helpers/UniswapHelpers.sol @@ -24,13 +24,7 @@ library UniswapHelpers { } } - /** - * @notice Initializes a Uniswap V3 pool with the price of 1 cent per ETH or 1 ETH in terms of token price. - * @dev Uses the square root of the price to initialize the pool with proper scaling. - * @param _pool The address of the Uniswap V3 pool to initialize. - * @param token0isWeth A boolean indicating if WETH is token0 in the pool. - */ - function initializePoolFor1Cent(IUniswapV3Pool _pool, bool token0isWeth) internal { + function calcPriceFor1Cent(bool token0isWeth) public pure returns (uint160 sqrtPriceX96) { uint256 price; if (token0isWeth) { // ETH as token0, so we are setting the price of 1 ETH in terms of token1 (USD cent) @@ -42,8 +36,17 @@ library UniswapHelpers { } // Calculate the square root of the price with scaling for Uniswap V3 initialization - uint160 sqrtPriceX96 = uint160(sqrt(price) * 2**96 / 10**9); + sqrtPriceX96 = uint160(sqrt(price) * 2**96 / 10**9); + } + /** + * @notice Initializes a Uniswap V3 pool with the price of 1 cent per ETH or 1 ETH in terms of token price. + * @dev Uses the square root of the price to initialize the pool with proper scaling. + * @param _pool The address of the Uniswap V3 pool to initialize. + * @param token0isWeth A boolean indicating if WETH is token0 in the pool. + */ + function initializePoolFor1Cent(IUniswapV3Pool _pool, bool token0isWeth) internal { + uint160 sqrtPriceX96 = calcPriceFor1Cent(token0isWeth); // Initialize the pool with the calculated price _pool.initialize(sqrtPriceX96); } @@ -75,4 +78,4 @@ library UniswapHelpers { } } } -} \ No newline at end of file +} diff --git a/onchain/test/Simulations.t.sol b/onchain/test/Simulations.t.sol index a20fb02..d6b4eef 100644 --- a/onchain/test/Simulations.t.sol +++ b/onchain/test/Simulations.t.sol @@ -29,6 +29,7 @@ contract SimulationsTest is UniswapTestBase, CSVManager { IUniswapV3Factory factory; Stake stakingPool; + PoolKey private poolKey; LiquidityManager lm; address feeDestination = makeAddr("fees"); uint256 supplyOnRecenter; @@ -36,9 +37,9 @@ contract SimulationsTest is UniswapTestBase, CSVManager { int256 supplyChange; struct Position { - uint256 liquidity; - int32 tickLower; - int32 tickUpper; + uint128 liquidity; + int24 tickLower; + int24 tickUpper; } enum ActionType { @@ -54,18 +55,19 @@ contract SimulationsTest is UniswapTestBase, CSVManager { struct Action { uint256 kind; // buy, sell, snatch, unstake, paytax, recenter, mint, burn - uint256 amount1; // x , x , x , x , x , x , , - uint256 amount2; // , , x , , , , , + uint256 amount1; // x , x , x , x , x , x , x , x + uint256 amount2; // , , x , , , , x , x string position; // , , x , , , , , } struct Scenario { uint256 VWAP; - uint256 comEthBal; uint256 comHarbBal; uint256 comStakeShare; Position[] liquidity; // the positions are floor, anchor, liquidity, [comPos1, comPos2 ...] + uint160 sqrtPriceX96; uint256 time; + bool token0IsWeth; Action[] txns; } @@ -77,8 +79,9 @@ contract SimulationsTest is UniswapTestBase, CSVManager { harberg = new Harberg("Harberg", "HRB", tc); pool = IUniswapV3Pool(factory.createPool(address(weth), address(harberg), FEE)); + poolKey = PoolAddress.getPoolKey(address(weth), address(harberg), FEE); token0isWeth = address(weth) < address(harberg); - pool.initializePoolFor1Cent(token0isWeth); + //pool.initializePoolFor1Cent(token0isWeth); stakingPool = new Stake(address(harberg)); harberg.setStakingPool(address(stakingPool)); @@ -94,6 +97,51 @@ contract SimulationsTest is UniswapTestBase, CSVManager { initializeTimeSeriesCSV(); } + function setUpCustomToken0(bool token0shouldBeWeth) public { + factory = UniswapHelpers.deployUniswapFactory(); + + TwabController tc = new TwabController(60 * 60 * 24, uint32(block.timestamp)); + + bool setupComplete = false; + uint retryCount = 0; + while (!setupComplete && retryCount < 5) { + // Clean slate if retrying + if (retryCount > 0) { + deployDummies(1); // Deploy a dummy contract to shift addresses + } + + weth = IWETH9(address(new WETH())); + harberg = new Harberg("HARB", "HARB", tc); + + // Check if the setup meets the required condition + if (token0shouldBeWeth == address(weth) < address(harberg)) { + setupComplete = true; + } else { + // Clear current instances for re-deployment + delete weth; + delete harberg; + retryCount++; + } + } + require(setupComplete, "Setup failed to meet the condition after several retries"); + + pool = IUniswapV3Pool(factory.createPool(address(weth), address(harberg), FEE)); + + token0isWeth = address(weth) < address(harberg); + stake = new Stake(address(harberg)); + harberg.setStakingPool(address(stake)); + Sentimenter senti = Sentimenter(address(new MockSentimenter())); + senti.initialize(address(harberg), address(stake)); + lm = new LiquidityManager(address(factory), address(weth), address(harberg), address(senti)); + lm.setFeeDestination(feeDestination); + vm.prank(feeDestination); + harberg.setLiquidityManager(address(lm)); + harberg.setLiquidityPool(address(pool)); + vm.deal(address(lm), 10 ether); + initializePositionsCSV(); // Set up the CSV header + } + + function buy(uint256 amountEth) internal { performSwap(amountEth, true); } @@ -209,12 +257,24 @@ contract SimulationsTest is UniswapTestBase, CSVManager { } function testGeneration() public { + // for each member of the generation, run all scenarios string memory json = vm.readFile("test/data/scenarios.json"); bytes memory data = vm.parseJson(json); - Scenario memory scenario = abi.decode(data, (Scenario)); - - vm.deal(account, scenario.comEthBal * 10**18); + Scenario memory scenario = abi.decode(data, (Scenario)); + // vm.deal(account, scenario.comEthBal * 10**18); vm.prank(account); + pool.initialize(scenario.sqrtPriceX96); + // initialize the liquidity + for (uint256 i = 0; i < scenario.liquidity.length; i++) { + + pool.mint( + address(this), + scenario.liquidity[i].tickLower, + scenario.liquidity[i].tickUpper, + scenario.liquidity[i].liquidity, + abi.encode(poolKey) + ); + } weth.deposit{value: address(account).balance}(); for (uint256 i = 0; i < scenario.txns.length; i++) { @@ -222,8 +282,12 @@ contract SimulationsTest is UniswapTestBase, CSVManager { recordState(); } - //writeCsv(); + //writeCsv(); + // for each member, combine the single results into an overall fitness core + // apply the selection + // apply mating + // apply mutation } diff --git a/onchain/test/data/scenarios.json b/onchain/test/data/scenarios.json index f5c1d2c..2dd6217 100644 --- a/onchain/test/data/scenarios.json +++ b/onchain/test/data/scenarios.json @@ -1,9 +1,32 @@ { "VWAP": 0, - "comEthBal": 0, "comHarbBal": 0, "comStakeShare": 0, - "txns": [], + "liquidity": [{ + "liquidity": 0, + "tickLower": -123891, + "tickUpper": -125000 + }, { + "liquidity": 0, + "tickLower": -123891, + "tickUpper": -125000 + }, { + "liquidity": 0, + "tickLower": -123891, + "tickUpper": -125000 + }], + "sqrtPriceX96": 38813714283599478074587411019430, "time": 0, - "liquidity": [] + "token0IsWeth": true, + "txns": [{ + "kind": 0, + "amount1": 10, + "amount2": 0, + "position": "" + }, { + "kind": 1, + "amount1": 1, + "amount2": 0, + "position": "" + }] } diff --git a/onchain/test/helpers/UniswapTestBase.sol b/onchain/test/helpers/UniswapTestBase.sol index 381282b..080624c 100644 --- a/onchain/test/helpers/UniswapTestBase.sol +++ b/onchain/test/helpers/UniswapTestBase.sol @@ -25,36 +25,36 @@ abstract contract UniswapTestBase is Test { * @param isBuy True if buying WETH, false if selling. */ function performSwap(uint256 amount, bool isBuy) internal { - uint160 limit; - // Determine the swap direction - bool zeroForOne = isBuy ? token0isWeth : !token0isWeth; + uint160 limit; + // Determine the swap direction + bool zeroForOne = isBuy ? token0isWeth : !token0isWeth; - if (isBuy) { - vm.prank(account); - weth.transfer(address(this), amount); - } else { - vm.prank(account); - harberg.approve(address(this), amount); - } + if (isBuy) { + vm.prank(account); + weth.transfer(address(this), amount); + } else { + vm.prank(account); + harberg.approve(address(this), amount); + } - // Set the sqrtPriceLimitX96 based on the swap direction - if (zeroForOne) { - // Swapping token0 for token1 - // sqrtPriceLimitX96 must be less than current price but greater than MIN_SQRT_RATIO - limit = TickMath.MIN_SQRT_RATIO + 1; - } else { - // Swapping token1 for token0 - // sqrtPriceLimitX96 must be greater than current price but less than MAX_SQRT_RATIO - limit = TickMath.MAX_SQRT_RATIO - 1; - } + // Set the sqrtPriceLimitX96 based on the swap direction + if (zeroForOne) { + // Swapping token0 for token1 + // sqrtPriceLimitX96 must be less than current price but greater than MIN_SQRT_RATIO + limit = TickMath.MIN_SQRT_RATIO + 1; + } else { + // Swapping token1 for token0 + // sqrtPriceLimitX96 must be greater than current price but less than MAX_SQRT_RATIO + limit = TickMath.MAX_SQRT_RATIO - 1; + } - pool.swap( - account, - zeroForOne, - int256(amount), - limit, - abi.encode(account, int256(amount), isBuy) - ); + pool.swap( + account, + zeroForOne, + int256(amount), + limit, + abi.encode(account, int256(amount), isBuy) + ); } /** @@ -78,4 +78,27 @@ abstract contract UniswapTestBase is Test { require(harberg.transferFrom(seller, msg.sender, amountToPay), "Transfer failed"); } } + + /// @notice Callback function that Uniswap V3 calls for liquidity actions requiring minting or burning of tokens. + /// @param amount0Owed The amount of token0 owed for the liquidity provision. + /// @param amount1Owed The amount of token1 owed for the liquidity provision. + /// @dev This function mints Harberg tokens as needed and handles WETH deposits for ETH conversions during liquidity interactions. + function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata) external { + // CallbackValidation.verifyCallback(factory, poolKey); + // take care of harb + uint256 harbPulled = token0isWeth ? amount1Owed : amount0Owed; + if (harbPulled > 0) { + harberg.mint(harbPulled); + harberg.transfer(msg.sender, harbPulled); + } + + // pack ETH + uint256 ethOwed = token0isWeth ? amount0Owed : amount1Owed; + if (weth.balanceOf(address(this)) < ethOwed) { + weth.deposit{value: address(this).balance}(); + } + if (ethOwed > 0) { + weth.transfer(msg.sender, amount1Owed); + } + } }