Skip to content

Commit

Permalink
feat: include cl related test on hook delta
Browse files Browse the repository at this point in the history
  • Loading branch information
ChefMist committed Nov 6, 2024
1 parent ddc315f commit d1b7956
Show file tree
Hide file tree
Showing 6 changed files with 499 additions and 2 deletions.
2 changes: 1 addition & 1 deletion test/pool-bin/BinCustomCurveHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {BinTestHelper} from "./helpers/BinTestHelper.sol";
import {Hooks} from "../../src/libraries/Hooks.sol";
import {BinCustomCurveHook} from "./helpers/BinCustomCurveHook.sol";

contract BinHookReturnsDelta is Test, GasSnapshot, BinTestHelper {
contract BinCustomCurveHookTest is Test, GasSnapshot, BinTestHelper {
using BinPoolParametersHelper for bytes32;

Vault public vault;
Expand Down
2 changes: 1 addition & 1 deletion test/pool-bin/BinMintBurnFeeHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {BinTestHelper} from "./helpers/BinTestHelper.sol";
import {Hooks} from "../../src/libraries/Hooks.sol";
import {BinMintBurnFeeHook} from "./helpers/BinMintBurnFeeHook.sol";

contract BinHookReturnsDelta is Test, GasSnapshot, BinTestHelper {
contract BinMintBurnFeeHookTest is Test, GasSnapshot, BinTestHelper {
using BinPoolParametersHelper for bytes32;

Vault public vault;
Expand Down
133 changes: 133 additions & 0 deletions test/pool-cl/CLCustomCurveHook.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Test.sol";
import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol";
import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol";
import {IVault} from "../../src/interfaces/IVault.sol";
import {Vault} from "../../src/Vault.sol";
import {IPoolManager} from "../../src/interfaces/IPoolManager.sol";
import {ICLPoolManager} from "../../src/pool-cl/interfaces/ICLPoolManager.sol";
import {CLPoolManager} from "../../src/pool-cl/CLPoolManager.sol";
import {CLPool} from "../../src/pool-cl/libraries/CLPool.sol";
import {Currency, CurrencyLibrary} from "../../src/types/Currency.sol";
import {PoolKey} from "../../src/types/PoolKey.sol";
import {IHooks} from "../../src/interfaces/IHooks.sol";
import {Hooks} from "../../src/libraries/Hooks.sol";
import {CLPoolManagerRouter} from "./helpers/CLPoolManagerRouter.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Deployers} from "./helpers/Deployers.sol";
import {TokenFixture} from "../helpers/TokenFixture.sol";
import {LPFeeLibrary} from "../../src/libraries/LPFeeLibrary.sol";
import {CLPoolParametersHelper} from "../../src/pool-cl/libraries/CLPoolParametersHelper.sol";
import {CLCustomCurveHook} from "./helpers/CLCustomCurveHook.sol";
import {BalanceDelta} from "../../src/types/BalanceDelta.sol";
import {TickMath} from "../../src/pool-cl/libraries/TickMath.sol";

contract CLCustomCurveHookTest is Test, Deployers, TokenFixture, GasSnapshot {
using CLPoolParametersHelper for bytes32;
using LPFeeLibrary for uint24;

PoolKey key;
IVault public vault;
CLPoolManager public poolManager;
CLPoolManagerRouter public router;
CLCustomCurveHook public clCustomCurveHook;

MockERC20 token0;
MockERC20 token1;

function setUp() public {
initializeTokens();
token0 = MockERC20(Currency.unwrap(currency0));
token1 = MockERC20(Currency.unwrap(currency1));

// burn all tokens minted via initializeTokens
token0.burn(address(this), token0.balanceOf(address(this)));
token1.burn(address(this), token1.balanceOf(address(this)));
(vault, poolManager) = createFreshManager();

router = new CLPoolManagerRouter(vault, poolManager);
clCustomCurveHook = new CLCustomCurveHook(vault, poolManager);

IERC20(Currency.unwrap(currency0)).approve(address(router), 1000 ether);
IERC20(Currency.unwrap(currency1)).approve(address(router), 1000 ether);
IERC20(Currency.unwrap(currency0)).approve(address(clCustomCurveHook), 1000 ether);
IERC20(Currency.unwrap(currency1)).approve(address(clCustomCurveHook), 1000 ether);

key = PoolKey({
currency0: currency0,
currency1: currency1,
hooks: clCustomCurveHook,
poolManager: poolManager,
fee: uint24(3000),
parameters: bytes32(uint256(clCustomCurveHook.getHooksRegistrationBitmap())).setTickSpacing(10)
});
clCustomCurveHook.setPoolKey(key);
poolManager.initialize(key, SQRT_RATIO_1_1);
}

/// @dev only meant for sanity test for the hook example
function test_addLiquidity_removeLiquidityXX() external {
// pre-req: mint token on this contract
token0.mint(address(this), 10 ether);
token1.mint(address(this), 10 ether);

assertEq(token0.balanceOf(address(this)), 10 ether);
assertEq(token1.balanceOf(address(this)), 10 ether);
assertEq(token0.balanceOf(address(vault)), 0 ether);
assertEq(token1.balanceOf(address(vault)), 0 ether);

// add liquidity and verify tokens are in the vault
clCustomCurveHook.addLiquidity(1 ether, 2 ether);
assertEq(token0.balanceOf(address(this)), 9 ether);
assertEq(token1.balanceOf(address(this)), 8 ether);
assertEq(token0.balanceOf(address(vault)), 1 ether);
assertEq(token1.balanceOf(address(vault)), 2 ether);

// remove liquidity and verify tokens are returned to this contract
clCustomCurveHook.removeLiquidity(1 ether, 1 ether);
assertEq(token0.balanceOf(address(this)), 10 ether);
assertEq(token1.balanceOf(address(this)), 9 ether);
assertEq(token0.balanceOf(address(vault)), 0 ether);
assertEq(token1.balanceOf(address(vault)), 1 ether);
}

function test_Swap_CustomCurve(uint256 _amtIn) public {
// preq-req: add liqudiity
token0.mint(address(this), 10 ether);
token1.mint(address(this), 10 ether);
clCustomCurveHook.addLiquidity(4 ether, 8 ether);

// before verify
assertEq(token0.balanceOf(address(this)), 6 ether);
assertEq(token1.balanceOf(address(this)), 2 ether);
assertEq(token0.balanceOf(address(vault)), 4 ether);
assertEq(token1.balanceOf(address(vault)), 8 ether);

// swap exactIn token0 for token1
uint128 amtIn = uint128(bound(_amtIn, 0.1 ether, 6 ether)); // 6 as token0.balanceOf(address(this) == 6 ethers
BalanceDelta delta = router.swap(
key,
ICLPoolManager.SwapParams({
zeroForOne: true,
amountSpecified: -int128(amtIn),
sqrtPriceLimitX96: TickMath.MIN_SQRT_RATIO + 1
}),
CLPoolManagerRouter.SwapTestSettings({withdrawTokens: true, settleUsingTransfer: true}),
""
);

// verify 1:1 swap
assertEq(delta.amount0(), -int128(amtIn));
assertEq(delta.amount1(), int128(amtIn));

// after verify
assertEq(token0.balanceOf(address(this)), 6 ether - amtIn);
assertEq(token1.balanceOf(address(this)), 2 ether + amtIn);
assertEq(token0.balanceOf(address(vault)), 4 ether + amtIn);
assertEq(token1.balanceOf(address(vault)), 8 ether - amtIn);
}

receive() external payable {}
}
135 changes: 135 additions & 0 deletions test/pool-cl/CLMintBurnFeeHook.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import "forge-std/Test.sol";
import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol";
import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol";
import {IVault} from "../../src/interfaces/IVault.sol";
import {Vault} from "../../src/Vault.sol";
import {IPoolManager} from "../../src/interfaces/IPoolManager.sol";
import {ICLPoolManager} from "../../src/pool-cl/interfaces/ICLPoolManager.sol";
import {CLPoolManager} from "../../src/pool-cl/CLPoolManager.sol";
import {CLPool} from "../../src/pool-cl/libraries/CLPool.sol";
import {Currency, CurrencyLibrary} from "../../src/types/Currency.sol";
import {PoolKey} from "../../src/types/PoolKey.sol";
import {IHooks} from "../../src/interfaces/IHooks.sol";
import {Hooks} from "../../src/libraries/Hooks.sol";
import {CLPoolManagerRouter} from "./helpers/CLPoolManagerRouter.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Deployers} from "./helpers/Deployers.sol";
import {TokenFixture} from "../helpers/TokenFixture.sol";
import {CLPoolParametersHelper} from "../../src/pool-cl/libraries/CLPoolParametersHelper.sol";
import {CLMintBurnFeeHook} from "./helpers/CLMintBurnFeeHook.sol";
import {BalanceDelta} from "../../src/types/BalanceDelta.sol";

contract CLMintBurnFeeHookTest is Test, Deployers, TokenFixture, GasSnapshot {
using CLPoolParametersHelper for bytes32;

PoolKey key;
IVault public vault;
CLPoolManager public poolManager;
CLPoolManagerRouter public router;
CLMintBurnFeeHook public clMintBurnFeeHook;

MockERC20 token0;
MockERC20 token1;

function setUp() public {
initializeTokens();
token0 = MockERC20(Currency.unwrap(currency0));
token1 = MockERC20(Currency.unwrap(currency1));

// burn all tokens minted via initializeTokens
token0.burn(address(this), token0.balanceOf(address(this)));
token1.burn(address(this), token1.balanceOf(address(this)));
(vault, poolManager) = createFreshManager();

router = new CLPoolManagerRouter(vault, poolManager);
clMintBurnFeeHook = new CLMintBurnFeeHook(vault, poolManager);

IERC20(Currency.unwrap(currency0)).approve(address(router), 1000 ether);
IERC20(Currency.unwrap(currency1)).approve(address(router), 1000 ether);
IERC20(Currency.unwrap(currency0)).approve(address(clMintBurnFeeHook), 1000 ether);
IERC20(Currency.unwrap(currency1)).approve(address(clMintBurnFeeHook), 1000 ether);

key = PoolKey({
currency0: currency0,
currency1: currency1,
hooks: clMintBurnFeeHook,
poolManager: poolManager,
fee: uint24(3000),
parameters: bytes32(uint256(clMintBurnFeeHook.getHooksRegistrationBitmap())).setTickSpacing(10)
});
poolManager.initialize(key, SQRT_RATIO_1_1);
}

/// @dev only meant for sanity test for the hook example
function test_MintXX() external {
token0.mint(address(this), 10 ether);
token1.mint(address(this), 10 ether);

// before
assertEq(token0.balanceOf(address(this)), 10 ether);
assertEq(token1.balanceOf(address(this)), 10 ether);
assertEq(token0.balanceOf(address(vault)), 0 ether);
assertEq(token1.balanceOf(address(vault)), 0 ether);
assertEq(token0.balanceOf(address(clMintBurnFeeHook)), 0 ether);
assertEq(token1.balanceOf(address(clMintBurnFeeHook)), 0 ether);

// around 0.5 eth token0 / 0.5 eth token1 liquidity added
(BalanceDelta delta,) = router.modifyPosition(
key,
ICLPoolManager.ModifyLiquidityParams({tickLower: -10, tickUpper: 10, liquidityDelta: 1000 ether, salt: 0}),
""
);

assertEq(token0.balanceOf(address(this)), 8500449895020996220); // ~8.5 ether
assertEq(token1.balanceOf(address(this)), 8500449895020996220); // ~8.4 ether
assertEq(token0.balanceOf(address(vault)), 1499550104979003780); // ~1.5 ether
assertEq(token1.balanceOf(address(vault)), 1499550104979003780); // ~1.5 ether

// // hook mint VaultToken instead of taking token from vault as vault does not have token in this case
assertEq(vault.balanceOf(address(clMintBurnFeeHook), key.currency0), 999700069986002520); // ~1 eth
assertEq(vault.balanceOf(address(clMintBurnFeeHook), key.currency1), 999700069986002520); // ~1 eth
}

function test_Burn() external {
token0.mint(address(this), 10 ether);
token1.mint(address(this), 10 ether);

// around 0.5 eth token0 / 0.5 eth token1 liquidity added
router.modifyPosition(
key,
ICLPoolManager.ModifyLiquidityParams({tickLower: -10, tickUpper: 10, liquidityDelta: 1000 ether, salt: 0}),
""
);

assertEq(token0.balanceOf(address(this)), 8500449895020996220); // ~8.5 ether
assertEq(token1.balanceOf(address(this)), 8500449895020996220); // ~8.5 ether
assertEq(token0.balanceOf(address(vault)), 1499550104979003780); // ~1.5 ether
assertEq(token1.balanceOf(address(vault)), 1499550104979003780); // ~1.5 ether
assertEq(vault.balanceOf(address(clMintBurnFeeHook), key.currency0), 999700069986002520); // ~1 eth
assertEq(vault.balanceOf(address(clMintBurnFeeHook), key.currency1), 999700069986002520); // ~1 eth

// remove liquidity
router.modifyPosition(
key,
ICLPoolManager.ModifyLiquidityParams({tickLower: -10, tickUpper: 10, liquidityDelta: -1000 ether, salt: 0}),
""
);

// 8.5 to 7 eth = 1.5 eth diff :: -2 eth was taken by hook for fee and +0.5 was from remove liquidity
assertEq(token0.balanceOf(address(this)), 7000899790041992443); // ~7 eth
assertEq(token1.balanceOf(address(this)), 7000899790041992443); // ~7 eth

// 1.5 to 3 eth = 1.5 eth diff :: -0.5 eth was returned to user and +2 eth deposited by hook
assertEq(token0.balanceOf(address(vault)), 2999100209958007557); // ~3 eth
assertEq(token1.balanceOf(address(vault)), 2999100209958007557); // ~3 eth

// 1 to 3 eth = 2 eth diff :: + 2 eth as fee from remove liquidity
assertEq(vault.balanceOf(address(clMintBurnFeeHook), key.currency0), 2999100209958007556); // ~3 eth
assertEq(vault.balanceOf(address(clMintBurnFeeHook), key.currency1), 2999100209958007556); // ~3 eth
}

receive() external payable {}
}
Loading

0 comments on commit d1b7956

Please sign in to comment.