From c0dd7341605bb6c5107d0f6b43fb3e42c100607b Mon Sep 17 00:00:00 2001 From: chefburger Date: Thu, 28 Nov 2024 11:41:46 +0800 Subject: [PATCH] feat: resolve issues and add test cases per comment --- test/ProtocolFeeController.t.sol | 91 +++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/test/ProtocolFeeController.t.sol b/test/ProtocolFeeController.t.sol index 2dbf017..e2ba8ed 100644 --- a/test/ProtocolFeeController.t.sol +++ b/test/ProtocolFeeController.t.sol @@ -19,12 +19,16 @@ import {IProtocolFees} from "../src/interfaces/IProtocolFees.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {CLPoolManagerRouter} from "../test/pool-cl/helpers/CLPoolManagerRouter.sol"; import {ICLPoolManager} from "../src/pool-cl/interfaces/ICLPoolManager.sol"; +import {IBinPoolManager} from "../src/pool-bin/interfaces/IBinPoolManager.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Currency} from "../src/types/Currency.sol"; import {TickMath} from "../src/pool-cl/libraries/TickMath.sol"; import {BalanceDelta} from "../src/types/BalanceDelta.sol"; +import {BinTestHelper} from "./pool-bin/helpers/BinTestHelper.sol"; +import {BinSwapHelper} from "./pool-bin/helpers/BinSwapHelper.sol"; +import {BinLiquidityHelper} from "./pool-bin/helpers/BinLiquidityHelper.sol"; -contract ProtocolFeeControllerTest is TokenFixture, Test { +contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture { using CLPoolParametersHelper for bytes32; using BinPoolParametersHelper for bytes32; using ProtocolFeeLibrary for *; @@ -33,14 +37,24 @@ contract ProtocolFeeControllerTest is TokenFixture, Test { CLPoolManager clPoolManager; BinPoolManager binPoolManager; + BinSwapHelper public binSwapHelper; + BinLiquidityHelper public binLiquidityHelper; + function setUp() public { + initializeTokens(); + vault = new Vault(); clPoolManager = new CLPoolManager(vault); binPoolManager = new BinPoolManager(vault); vault.registerApp(address(clPoolManager)); vault.registerApp(address(binPoolManager)); - initializeTokens(); + binSwapHelper = new BinSwapHelper(binPoolManager, vault); + binLiquidityHelper = new BinLiquidityHelper(binPoolManager, vault); + IERC20(Currency.unwrap(currency0)).approve(address(binSwapHelper), 1000 ether); + IERC20(Currency.unwrap(currency1)).approve(address(binSwapHelper), 1000 ether); + IERC20(Currency.unwrap(currency0)).approve(address(binLiquidityHelper), 1000 ether); + IERC20(Currency.unwrap(currency1)).approve(address(binLiquidityHelper), 1000 ether); } function testOwnerTransfer() public { @@ -241,6 +255,9 @@ contract ProtocolFeeControllerTest is TokenFixture, Test { 100 ether * uint256(actualProtocolFee >> 12) / controller.ONE_HUNDRED_PERCENT_RATIO() ); + // lp fee should be roughly 0.1 ether, allow 2% error + assertApproxEqAbs(clPoolManager.protocolFeesAccrued(currency0), 0.1 ether, 0.1 ether / 50); + // check lp fee is twice the protocol fee (, BalanceDelta accumulatedLPFee) = router.modifyPosition( key, @@ -280,6 +297,76 @@ contract ProtocolFeeControllerTest is TokenFixture, Test { assertEq(IERC20(Currency.unwrap(currency0)).balanceOf(makeAddr("recipient")), protocolFeeAmount); } + function testCollectProtocolFeeForBinPool() public { + // init protocol fee controller and bind it to binPoolManager + ProtocolFeeController controller = new ProtocolFeeController(address(binPoolManager)); + binPoolManager.setProtocolFeeController(controller); + // make protocol fee half of the total fee + controller.setProtocolFeeSplitRatio(500000); + + // init pool with protocol fee controller + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: binPoolManager, + fee: 2000, + parameters: bytes32(0).setBinStep(1) + }); + binPoolManager.initialize(key, ID_ONE); + + (, uint24 actualProtocolFee,) = binPoolManager.getSlot0(key.toId()); + + // add some liquidity + IBinPoolManager.MintParams memory mintParams = _getSingleBinMintParams(ID_ONE, 500 ether, 500 ether); + binLiquidityHelper.mint(key, mintParams, abi.encode(0)); + + // swap to generate protocol fee + // splitRatio=50% so that protcol fee should be half of the total fee + binSwapHelper.swap(key, true, -int128(100 ether), BinSwapHelper.TestSettings(true, true), ""); + + assertEq( + binPoolManager.protocolFeesAccrued(currency0), + 100 ether * uint256(actualProtocolFee >> 12) / controller.ONE_HUNDRED_PERCENT_RATIO() + ); + + // lp fee should be roughly 0.2 ether + assertApproxEqAbs(binPoolManager.protocolFeesAccrued(currency0), 0.2 ether, 0.2 ether / 100); + + // check lp fee equals to the protocol fee + IBinPoolManager.BurnParams memory burnParams = + _getSingleBinBurnLiquidityParams(key, binPoolManager, ID_ONE, address(binLiquidityHelper), 100); + BalanceDelta delta = binLiquidityHelper.burn(key, burnParams, ""); + + assertApproxEqAbs( + binPoolManager.protocolFeesAccrued(currency0) * 2, + // amt1 out roughly 100 ether, but the actual output amount is less due to fee and init liquidity lock + // since no slippage within a given bin, we can calculate the total fee as follows: + uint256(int256(delta.amount1() - 400 ether)), + // we know total fee is roughly 0.4 ether, let's say error caused by init liqudity lock is less than 1% + 0.4 ether / 100 + ); + + // collect protocol fee + { + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, makeAddr("someone"))); + vm.prank(makeAddr("someone")); + controller.collectProtocolFee(makeAddr("recipient"), currency0, 0); + } + + // collect half + uint256 protocolFeeAmount = binPoolManager.protocolFeesAccrued(currency0); + controller.collectProtocolFee(makeAddr("recipient"), currency0, protocolFeeAmount / 2); + + assertEq(binPoolManager.protocolFeesAccrued(currency0), protocolFeeAmount / 2); + assertEq(IERC20(Currency.unwrap(currency0)).balanceOf(makeAddr("recipient")), protocolFeeAmount / 2); + + // collect the rest + controller.collectProtocolFee(makeAddr("recipient"), currency0, 0); + assertEq(binPoolManager.protocolFeesAccrued(currency0), 0); + assertEq(IERC20(Currency.unwrap(currency0)).balanceOf(makeAddr("recipient")), protocolFeeAmount); + } + function _calculateLPFeeThreshold(ProtocolFeeController controller) internal view returns (uint24) { return uint24( (