diff --git a/onchain/test/ModularComponentsTest.t.sol b/onchain/test/ModularComponentsTest.t.sol index 9ae9d25..0e92c57 100644 --- a/onchain/test/ModularComponentsTest.t.sol +++ b/onchain/test/ModularComponentsTest.t.sol @@ -5,6 +5,7 @@ import "forge-std/Test.sol"; import "../src/libraries/UniswapMath.sol"; import "../src/abstracts/PriceOracle.sol"; import "../src/abstracts/ThreePositionStrategy.sol"; +import "./helpers/TestBase.sol"; /** * @title Modular Components Test @@ -25,12 +26,12 @@ contract TestUniswapMath is UniswapMath, Test { assertLe(tick, 887272, "Tick should be <= MAX_TICK"); } - function getTickAtPrice(bool t0isWeth, uint256 tokenAmount, uint256 ethAmount) external pure returns (int24) { + function tickAtPrice(bool t0isWeth, uint256 tokenAmount, uint256 ethAmount) external pure returns (int24) { return _tickAtPrice(t0isWeth, tokenAmount, ethAmount); } } -contract ModularComponentsTest is Test { +contract ModularComponentsTest is TestUtilities { TestUniswapMath testMath; function setUp() public { @@ -39,7 +40,7 @@ contract ModularComponentsTest is Test { function testUniswapMathCompilation() public { // Test that mathematical utilities work - int24 tick = testMath.getTickAtPrice(true, 1 ether, 1 ether); + int24 tick = testMath.tickAtPrice(true, 1 ether, 1 ether); // Should get a reasonable tick for 1:1 ratio assertGt(tick, -10000, "Tick should be reasonable"); diff --git a/onchain/test/Stake.t.sol b/onchain/test/Stake.t.sol index a7ab413..2c42af2 100644 --- a/onchain/test/Stake.t.sol +++ b/onchain/test/Stake.t.sol @@ -5,8 +5,9 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import "../src/Kraiken.sol"; import {TooMuchSnatch, Stake} from "../src/Stake.sol"; +import "./helpers/TestBase.sol"; -contract StakeTest is Test { +contract StakeTest is TestUtilities { Kraiken kraiken; Stake stakingPool; address liquidityPool; @@ -166,13 +167,7 @@ contract StakeTest is Test { vm.stopPrank(); } - function bp(uint256 val) internal pure returns (uint256) { - return val / 1e15; - } - - function denormTR(uint256 normalizedTaxRate) internal pure returns (uint256) { - return normalizedTaxRate * 97; - } + // Using bp() and denormTR() from TestBase function testAvgTaxRateAndPercentageStaked() public { uint256 smallstake = 0.3e17; diff --git a/onchain/test/VWAPDoubleOverflowAnalysis.t.sol b/onchain/test/VWAPDoubleOverflowAnalysis.t.sol index a93337f..1e75e09 100644 --- a/onchain/test/VWAPDoubleOverflowAnalysis.t.sol +++ b/onchain/test/VWAPDoubleOverflowAnalysis.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "../src/VWAPTracker.sol"; +import "./mocks/MockVWAPTracker.sol"; /** * @title VWAP Double-Overflow Analysis @@ -11,15 +12,7 @@ import "../src/VWAPTracker.sol"; * where new volume cannot be recorded due to overflow even after compression */ -contract MockVWAPTracker is VWAPTracker { - function recordVolumeAndPrice(uint256 currentPriceX96, uint256 fee) external { - _recordVolumeAndPrice(currentPriceX96, fee); - } - - function resetVWAP() external { - _resetVWAP(); - } - +contract ExtendedMockVWAPTracker is MockVWAPTracker { // Expose internal function for testing (bounds applied to prevent test overflow) function testRecordVolumeAndPriceUnsafe(uint256 currentPriceX96, uint256 fee) external view { // Cap extreme inputs to prevent overflow during test calculations @@ -48,10 +41,10 @@ contract MockVWAPTracker is VWAPTracker { } contract VWAPDoubleOverflowAnalysisTest is Test { - MockVWAPTracker vwapTracker; + ExtendedMockVWAPTracker vwapTracker; function setUp() public { - vwapTracker = new MockVWAPTracker(); + vwapTracker = new ExtendedMockVWAPTracker(); } diff --git a/onchain/test/VWAPTracker.t.sol b/onchain/test/VWAPTracker.t.sol index 12618c3..0cfef46 100644 --- a/onchain/test/VWAPTracker.t.sol +++ b/onchain/test/VWAPTracker.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "../src/VWAPTracker.sol"; +import "./mocks/MockVWAPTracker.sol"; /** * @title VWAPTracker Test Suite @@ -12,15 +13,6 @@ import "../src/VWAPTracker.sol"; * - Adjusted VWAP with capital inefficiency * - Volume weighted price accumulation */ -contract MockVWAPTracker is VWAPTracker { - function recordVolumeAndPrice(uint256 currentPriceX96, uint256 fee) external { - _recordVolumeAndPrice(currentPriceX96, fee); - } - - function resetVWAP() external { - _resetVWAP(); - } -} contract VWAPTrackerTest is Test { MockVWAPTracker vwapTracker; diff --git a/onchain/test/abstracts/ThreePositionStrategy.t.sol b/onchain/test/abstracts/ThreePositionStrategy.t.sol index 9694d5a..df74199 100644 --- a/onchain/test/abstracts/ThreePositionStrategy.t.sol +++ b/onchain/test/abstracts/ThreePositionStrategy.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol"; import "../../src/abstracts/ThreePositionStrategy.sol"; +import "../helpers/TestBase.sol"; /** * @title ThreePositionStrategy Test Suite @@ -131,7 +132,7 @@ contract MockThreePositionStrategy is ThreePositionStrategy { } } -contract ThreePositionStrategyTest is Test { +contract ThreePositionStrategyTest is TestUtilities { MockThreePositionStrategy strategy; address constant HARB_TOKEN = address(0x1234); @@ -152,21 +153,14 @@ contract ThreePositionStrategyTest is Test { ); } - 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% - }); - } + // Using getDefaultParams() from TestBase // ======================================== // ANCHOR POSITION TESTS // ======================================== function testAnchorPositionBasic() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); uint256 anchorEthBalance = 20 ether; // 20% of total uint256 pulledHarb = strategy.setAnchorPosition(CURRENT_TICK, anchorEthBalance, params); @@ -185,7 +179,7 @@ contract ThreePositionStrategyTest is Test { } function testAnchorPositionSymmetricAroundCurrentTick() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); uint256 anchorEthBalance = 20 ether; strategy.setAnchorPosition(CURRENT_TICK, anchorEthBalance, params); @@ -200,7 +194,7 @@ contract ThreePositionStrategyTest is Test { } function testAnchorPositionWidthScaling() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); params.anchorWidth = 100; // Maximum width uint256 anchorEthBalance = 20 ether; @@ -217,7 +211,7 @@ contract ThreePositionStrategyTest is Test { // ======================================== function testDiscoveryPositionDependsOnAnchor() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); uint256 pulledHarb = 1000 ether; // Simulated from anchor uint256 discoveryAmount = strategy.setDiscoveryPosition(CURRENT_TICK, pulledHarb, params); @@ -231,7 +225,7 @@ contract ThreePositionStrategyTest is Test { } function testDiscoveryPositionPlacement() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); bool token0IsWeth = true; // Test with WETH as token0 @@ -248,7 +242,7 @@ contract ThreePositionStrategyTest is Test { } function testDiscoveryDepthScaling() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); params.discoveryDepth = 10 ** 18; // Maximum depth (100%) uint256 pulledHarb = 1000 ether; @@ -266,7 +260,7 @@ contract ThreePositionStrategyTest is Test { // ======================================== function testFloorPositionUsesVWAP() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); // Set up VWAP data uint256 vwapX96 = 79228162514264337593543950336; // 1.0 in X96 format @@ -287,7 +281,7 @@ contract ThreePositionStrategyTest is Test { } function testFloorPositionEthScarcity() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); // Set up scenario where ETH is insufficient for VWAP price uint256 vwapX96 = 79228162514264337593543950336 * 10; // High VWAP price @@ -305,7 +299,7 @@ contract ThreePositionStrategyTest is Test { } function testFloorPositionEthAbundance() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); // Set up scenario where ETH is sufficient for VWAP price uint256 baseVwap = 79228162514264337593543950336; // 1.0 in X96 format @@ -325,7 +319,7 @@ contract ThreePositionStrategyTest is Test { } function testFloorPositionNoVWAP() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); // No VWAP data (volume = 0) strategy.setVWAP(0, 0); @@ -347,7 +341,7 @@ contract ThreePositionStrategyTest is Test { } function testFloorPositionOutstandingSupplyCalculation() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); uint256 initialSupply = 1000000 ether; uint256 pulledHarb = 50000 ether; @@ -369,7 +363,7 @@ contract ThreePositionStrategyTest is Test { // ======================================== function testSetPositionsOrder() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); strategy.setPositions(CURRENT_TICK, params); @@ -387,7 +381,7 @@ contract ThreePositionStrategyTest is Test { } function testSetPositionsEthAllocation() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); params.anchorShare = 2 * 10 ** 17; // 20% uint256 totalEth = 100 ether; @@ -402,7 +396,7 @@ contract ThreePositionStrategyTest is Test { } function testSetPositionsAsymmetricProfile() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); strategy.setPositions(CURRENT_TICK, params); @@ -431,7 +425,7 @@ contract ThreePositionStrategyTest is Test { // ======================================== function testPositionBoundaries() public { - ThreePositionStrategy.PositionParams memory params = _getDefaultParams(); + ThreePositionStrategy.PositionParams memory params = getDefaultParams(); strategy.setPositions(CURRENT_TICK, params); diff --git a/onchain/test/helpers/CSVHelper.sol b/onchain/test/helpers/CSVHelper.sol index 85071a9..cc20542 100644 --- a/onchain/test/helpers/CSVHelper.sol +++ b/onchain/test/helpers/CSVHelper.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +import "forge-std/Test.sol"; +import "forge-std/Vm.sol"; + /** * @title CSVHelper * @dev Library for managing CSV data in Solidity, including converting values to strings and writing CSV data. @@ -30,62 +33,30 @@ library CSVHelper { } /** - * @notice Converts a `uint256` to a string. + * @notice Converts a `uint256` to a string using Forge's built-in vm.toString() + * @param vm The Forge VM instance * @param _i The integer to convert. * @return The string representation of the integer. + * @dev This function is a wrapper around vm.toString() for consistency with the library interface. + * In practice, use vm.toString() directly in test files. */ - function uintToStr(uint256 _i) internal pure returns (string memory) { - if (_i == 0) { - return "0"; - } - uint256 j = _i; - uint256 len; - while (j != 0) { - len++; - j /= 10; - } - bytes memory bstr = new bytes(len); - uint256 k = len; - while (_i != 0) { - k = k - 1; - uint8 temp = (48 + uint8(_i - _i / 10 * 10)); - bstr[k] = bytes1(temp); - _i /= 10; - } - return string(bstr); + function uintToStr(Vm vm, uint256 _i) internal pure returns (string memory) { + // Note: This function is kept for API compatibility but should not be used in new code. + // Use vm.toString() directly instead. + return vm.toString(_i); } /** - * @notice Converts an `int256` to a string. + * @notice Converts an `int256` to a string using Forge's built-in vm.toString() + * @param vm The Forge VM instance * @param _i The integer to convert. * @return The string representation of the integer. + * @dev This function is a wrapper around vm.toString() for consistency with the library interface. + * In practice, use vm.toString() directly in test files. */ - function intToStr(int256 _i) internal pure returns (string memory) { - if (_i == 0) { - return "0"; - } - bool negative = _i < 0; - uint256 absValue = uint256(negative ? -_i : _i); - uint256 len; - uint256 j = absValue; - while (j != 0) { - len++; - j /= 10; - } - if (negative) { - len++; // Increase length for the minus sign. - } - bytes memory bstr = new bytes(len); - uint256 k = len; - while (absValue != 0) { - k = k - 1; - uint8 temp = (48 + uint8(absValue - absValue / 10 * 10)); - bstr[k] = bytes1(temp); - absValue /= 10; - } - if (negative) { - bstr[0] = "-"; - } - return string(bstr); + function intToStr(Vm vm, int256 _i) internal pure returns (string memory) { + // Note: This function is kept for API compatibility but should not be used in new code. + // Use vm.toString() directly instead. + return vm.toString(_i); } } diff --git a/onchain/test/helpers/TestBase.sol b/onchain/test/helpers/TestBase.sol new file mode 100644 index 0000000..f3f2b9d --- /dev/null +++ b/onchain/test/helpers/TestBase.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import "../../src/abstracts/ThreePositionStrategy.sol"; + +/** + * @title TestBase + * @notice Base contract providing shared utilities and default parameters for tests + * @dev Consolidates common test functionality to reduce code duplication + */ +abstract contract TestUtilities 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; + } +} \ No newline at end of file diff --git a/onchain/test/mocks/MockVWAPTracker.sol b/onchain/test/mocks/MockVWAPTracker.sol new file mode 100644 index 0000000..988c0a0 --- /dev/null +++ b/onchain/test/mocks/MockVWAPTracker.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.19; + +import "../../src/VWAPTracker.sol"; + +/** + * @title MockVWAPTracker + * @notice Shared mock implementation of VWAPTracker for testing + * @dev Exposes internal functions for testing purposes + */ +contract MockVWAPTracker is VWAPTracker { + /** + * @notice Exposes internal VWAP recording function for testing + * @param currentPriceX96 Current price in X96 format + * @param fee Fee amount used to calculate volume + */ + function recordVolumeAndPrice(uint256 currentPriceX96, uint256 fee) external { + _recordVolumeAndPrice(currentPriceX96, fee); + } + + /** + * @notice Exposes internal VWAP reset function for testing + */ + function resetVWAP() external { + _resetVWAP(); + } +} \ No newline at end of file