// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.19; import { Kraiken } from "../../src/Kraiken.sol"; import { LiquidityManager } from "../../src/LiquidityManager.sol"; import "../../src/Optimizer.sol"; import { Stake } from "../../src/Stake.sol"; import "../../src/abstracts/ThreePositionStrategy.sol"; import "../../src/helpers/UniswapHelpers.sol"; import "../../src/interfaces/IWETH9.sol"; import "../../test/mocks/MockOptimizer.sol"; import "@aperture/uni-v3-lib/TickMath.sol"; import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol"; import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol"; import "forge-std/Test.sol"; import { WETH } from "solmate/tokens/WETH.sol"; // Constants uint24 constant FEE = uint24(10_000); // 1% fee uint256 constant INITIAL_LM_ETH_BALANCE = 50 ether; uint256 constant ORACLE_UPDATE_INTERVAL = 5 hours; /** * @title TestConstants * @notice Base contract providing shared constants and default parameters for tests * @dev Consolidates common test constants to reduce code duplication */ abstract contract TestConstants is Test { /** * @notice Returns default parameters for ThreePositionStrategy testing * @return Default PositionParams structure with standard values * @dev Used across multiple test files to ensure consistency */ function getDefaultParams() internal pure returns (ThreePositionStrategy.PositionParams memory) { return ThreePositionStrategy.PositionParams({ capitalInefficiency: 5 * 10 ** 17, // 50% anchorShare: 5 * 10 ** 17, // 50% anchorWidth: 50, // 50% discoveryDepth: 5 * 10 ** 17 // 50% }); } /** * @notice Helper function to convert value to basis points * @param val Value to convert * @return Value in basis points */ function bp(uint256 val) internal pure returns (uint256) { return val / 1e15; } /** * @notice Helper function to denormalize tax rate * @param normalizedTaxRate Normalized tax rate * @return Denormalized tax rate */ function denormTR(uint256 normalizedTaxRate) internal pure returns (uint256) { return normalizedTaxRate * 97; } } /** * @title TestEnvironment * @notice Helper contract that provides test environment setup for LiquidityManager tests and scripts * @dev Extracted from LiquidityManager.t.sol to enable reuse without inheritance */ contract TestEnvironment is TestConstants { using UniswapHelpers for IUniswapV3Pool; // Core contracts IUniswapV3Factory internal factory; IUniswapV3Pool internal pool; IWETH9 internal weth; Kraiken internal harberg; Stake internal stake; LiquidityManager internal lm; Optimizer internal optimizer; // State variables bool internal token0isWeth; address internal feeDestination; constructor(address _feeDestination) { feeDestination = _feeDestination; } /** * @notice Deploy all contracts and set up the environment * @param token0shouldBeWeth Whether WETH should be token0 * @param recenterCaller Address that will be granted recenter access * @return _factory The deployed Uniswap factory * @return _pool The created Uniswap pool * @return _weth The WETH token contract * @return _harberg The Kraiken token contract * @return _stake The staking contract * @return _lm The liquidity manager contract * @return _optimizer The optimizer contract * @return _token0isWeth Whether token0 is WETH */ function setupEnvironment( bool token0shouldBeWeth, address recenterCaller ) external returns ( IUniswapV3Factory _factory, IUniswapV3Pool _pool, IWETH9 _weth, Kraiken _harberg, Stake _stake, LiquidityManager _lm, Optimizer _optimizer, bool _token0isWeth ) { // Deploy factory factory = UniswapHelpers.deployUniswapFactory(); // Deploy tokens in correct order _deployTokensWithOrder(token0shouldBeWeth); // Create and initialize pool _createAndInitializePool(); // Deploy protocol contracts _deployProtocolContracts(); // Configure permissions _configurePermissions(); // Grant recenter access to specified caller vm.prank(feeDestination); lm.setRecenterAccess(recenterCaller); return (factory, pool, weth, harberg, stake, lm, optimizer, token0isWeth); } /** * @notice Deploy tokens ensuring the desired ordering * @param token0shouldBeWeth Whether WETH should be token0 */ function _deployTokensWithOrder(bool token0shouldBeWeth) internal { bool setupComplete = false; uint256 retryCount = 0; while (!setupComplete && retryCount < 20) { // Clean slate if retrying if (retryCount > 0) { // Deploy a dummy contract to shift addresses new WETH(); // Use WETH as dummy to avoid declaring new contract } weth = IWETH9(address(new WETH())); harberg = new Kraiken("KRAIKEN", "KRAIKEN"); // 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"); } /** * @notice Create and initialize the Uniswap pool */ function _createAndInitializePool() internal { pool = IUniswapV3Pool(factory.createPool(address(weth), address(harberg), FEE)); token0isWeth = address(weth) < address(harberg); pool.initializePoolFor1Cent(token0isWeth); } /** * @notice Deploy protocol contracts (Stake, Optimizer, LiquidityManager) */ function _deployProtocolContracts() internal { stake = new Stake(address(harberg), feeDestination); optimizer = Optimizer(address(new MockOptimizer())); optimizer.initialize(address(harberg), address(stake)); lm = new LiquidityManager(address(factory), address(weth), address(harberg), address(optimizer)); lm.setFeeDestination(feeDestination); } /** * @notice Configure permissions and initial funding */ function _configurePermissions() internal { harberg.setStakingPool(address(stake)); harberg.setLiquidityManager(address(lm)); vm.deal(address(lm), INITIAL_LM_ETH_BALANCE); } /** * @notice Setup environment with specific optimizer * @param token0shouldBeWeth Whether WETH should be token0 * @param recenterCaller Address that will be granted recenter access * @param optimizerAddress Address of the optimizer to use * @return _factory The deployed Uniswap factory * @return _pool The created Uniswap pool * @return _weth The WETH token contract * @return _harberg The Kraiken token contract * @return _stake The staking contract * @return _lm The liquidity manager contract * @return _optimizer The optimizer contract * @return _token0isWeth Whether token0 is WETH */ function setupEnvironmentWithOptimizer( bool token0shouldBeWeth, address recenterCaller, address optimizerAddress ) external returns ( IUniswapV3Factory _factory, IUniswapV3Pool _pool, IWETH9 _weth, Kraiken _harberg, Stake _stake, LiquidityManager _lm, Optimizer _optimizer, bool _token0isWeth ) { // Deploy factory factory = UniswapHelpers.deployUniswapFactory(); // Deploy tokens in correct order _deployTokensWithOrder(token0shouldBeWeth); // Create and initialize pool _createAndInitializePool(); // Deploy protocol contracts with custom optimizer stake = new Stake(address(harberg), feeDestination); optimizer = Optimizer(optimizerAddress); lm = new LiquidityManager(address(factory), address(weth), address(harberg), optimizerAddress); lm.setFeeDestination(feeDestination); // Configure permissions _configurePermissions(); // Grant recenter access to specified caller vm.prank(feeDestination); lm.setRecenterAccess(recenterCaller); return (factory, pool, weth, harberg, stake, lm, optimizer, token0isWeth); } /** * @notice Setup environment with existing factory and specific optimizer * @param existingFactory The existing Uniswap factory to use * @param token0shouldBeWeth Whether WETH should be token0 * @param recenterCaller Address that will be granted recenter access * @param optimizerAddress Address of the optimizer to use * @return _factory The existing Uniswap factory * @return _pool The created Uniswap pool * @return _weth The WETH token contract * @return _harberg The Kraiken token contract * @return _stake The staking contract * @return _lm The liquidity manager contract * @return _optimizer The optimizer contract * @return _token0isWeth Whether token0 is WETH */ function setupEnvironmentWithExistingFactory( IUniswapV3Factory existingFactory, bool token0shouldBeWeth, address recenterCaller, address optimizerAddress ) external returns ( IUniswapV3Factory _factory, IUniswapV3Pool _pool, IWETH9 _weth, Kraiken _harberg, Stake _stake, LiquidityManager _lm, Optimizer _optimizer, bool _token0isWeth ) { // Use existing factory factory = existingFactory; // Deploy tokens in correct order _deployTokensWithOrder(token0shouldBeWeth); // Create and initialize pool _createAndInitializePool(); // Deploy protocol contracts with custom optimizer stake = new Stake(address(harberg), feeDestination); optimizer = Optimizer(optimizerAddress); lm = new LiquidityManager(address(factory), address(weth), address(harberg), optimizerAddress); lm.setFeeDestination(feeDestination); // Configure permissions _configurePermissions(); // Grant recenter access to specified caller vm.prank(feeDestination); lm.setRecenterAccess(recenterCaller); return (factory, pool, weth, harberg, stake, lm, optimizer, token0isWeth); } }