Skip to content

Commit 9c15cf6

Browse files
authored
feat: update the default protocolFee for dynamic fee pool to 3bps (#243)
* feat: update the default protocolFee for dynamic fee pool to 3bps * fix: update to 0.03% instead of 0.3%
1 parent 40c2d50 commit 9c15cf6

2 files changed

Lines changed: 163 additions & 2 deletions

File tree

src/ProtocolFeeController.sol

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ contract ProtocolFeeController is IProtocolFeeController, Ownable2Step {
1818
/// @notice throw when the pool manager saved does not match the pool manager from the pool key
1919
error InvalidPoolManager();
2020

21+
/// @notice throw when the default protocol fee for dynamic fee pool is invalid i.e. greater than 0.4%
22+
error InvalidDefaultProtocolFeeForDynamicFeePool();
23+
2124
/// @notice throw when the protocol fee split ratio is invalid i.e. greater than 100%
2225
error InvalidProtocolFeeSplitRatio();
2326

@@ -30,6 +33,15 @@ contract ProtocolFeeController is IProtocolFeeController, Ownable2Step {
3033

3134
address public immutable poolManager;
3235

36+
/// @notice the default protocol fee for dynamic fee pool,
37+
/// every newly created dynamic fee pool will have this default protocol fee
38+
/// @dev 1000 = 0.1%, the initial setting is 0.03% i.e. 3bps
39+
uint24 public defaultProtocolFeeForDynamicFeePool = 300;
40+
41+
event DefaultProtocolFeeForDynamicFeePoolUpdated(
42+
uint24 oldDefaultProtocolFeeForDynamicFeePool, uint24 newDefaultProtocolFeeForDynamicFeePool
43+
);
44+
3345
event ProtocolFeeSplitRatioUpdated(uint256 oldProtocolFeeSplitRatio, uint256 newProtocolFeeSplitRatio);
3446

3547
/// @notice emit when the protocol fee is collected
@@ -39,6 +51,23 @@ contract ProtocolFeeController is IProtocolFeeController, Ownable2Step {
3951
poolManager = _poolManager;
4052
}
4153

54+
/// @notice Set the default protocol fee for dynamic fee pool, this will only impact the newly created dynamic fee pool
55+
/// all those existing dynamic fee pools will not be affected, they will keep using the default protocol fee set at the time of creation
56+
/// @param newDefaultProtocolFeeForDynamicFeePool 1000 = 0.1%, the initial setting is 0.03% i.e. 3bps
57+
function setDefaultProtocolFeeForDynamicFeePool(uint24 newDefaultProtocolFeeForDynamicFeePool) external onlyOwner {
58+
// cap the protocol fee at 0.4%, if it's over the limit we revert the tx
59+
if (newDefaultProtocolFeeForDynamicFeePool > ProtocolFeeLibrary.MAX_PROTOCOL_FEE) {
60+
revert InvalidDefaultProtocolFeeForDynamicFeePool();
61+
}
62+
63+
uint24 oldDefaultProtocolFeeForDynamicFeePool = defaultProtocolFeeForDynamicFeePool;
64+
defaultProtocolFeeForDynamicFeePool = newDefaultProtocolFeeForDynamicFeePool;
65+
66+
emit DefaultProtocolFeeForDynamicFeePoolUpdated(
67+
oldDefaultProtocolFeeForDynamicFeePool, newDefaultProtocolFeeForDynamicFeePool
68+
);
69+
}
70+
4271
/// @notice Set the ratio of the protocol fee in the total fee
4372
/// @param newProtocolFeeSplitRatio 30e4 would mean 30% of the total fee goes to protocol
4473
function setProtocolFeeSplitRatio(uint256 newProtocolFeeSplitRatio) external onlyOwner {
@@ -77,8 +106,8 @@ contract ProtocolFeeController is IProtocolFeeController, Ownable2Step {
77106
// calculate the protocol fee based on the predefined rule
78107
uint256 lpFee = poolKey.fee;
79108
if (lpFee == LPFeeLibrary.DYNAMIC_FEE_FLAG) {
80-
/// @notice for dynamic fee pools, the default protocol fee is 0
81-
return _buildProtocolFee(0);
109+
/// @notice for dynamic fee pools, the default protocol fee is set separately
110+
return _buildProtocolFee(defaultProtocolFeeForDynamicFeePool);
82111
} else if (protocolFeeSplitRatio == 0) {
83112
return _buildProtocolFee(0);
84113
} else if (protocolFeeSplitRatio == ONE_HUNDRED_PERCENT_RATIO) {

test/ProtocolFeeController.t.sol

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {BalanceDelta} from "../src/types/BalanceDelta.sol";
2727
import {BinTestHelper} from "./pool-bin/helpers/BinTestHelper.sol";
2828
import {BinSwapHelper} from "./pool-bin/helpers/BinSwapHelper.sol";
2929
import {BinLiquidityHelper} from "./pool-bin/helpers/BinLiquidityHelper.sol";
30+
import {HooksContract} from "./libraries/Hooks/HooksContract.sol";
3031

3132
contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
3233
using CLPoolParametersHelper for bytes32;
@@ -36,13 +37,18 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
3637
/// @notice 100% in hundredths of a bip
3738
uint256 private constant ONE_HUNDRED_PERCENT_RATIO = 1e6;
3839

40+
/// @dev the initial setting of the default protocol fee for dynamic fee pool is 0.03% i.e. 3bps
41+
uint24 private constant DEFAULT_PROTOCOL_FEE_FOR_DYNAMIC_FEE_POOL = 300;
42+
3943
Vault vault;
4044
CLPoolManager clPoolManager;
4145
BinPoolManager binPoolManager;
4246

4347
BinSwapHelper public binSwapHelper;
4448
BinLiquidityHelper public binLiquidityHelper;
4549

50+
HooksContract public hooksContract;
51+
4652
function setUp() public {
4753
initializeTokens();
4854

@@ -58,6 +64,8 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
5864
IERC20(Currency.unwrap(currency1)).approve(address(binSwapHelper), 1000 ether);
5965
IERC20(Currency.unwrap(currency0)).approve(address(binLiquidityHelper), 1000 ether);
6066
IERC20(Currency.unwrap(currency1)).approve(address(binLiquidityHelper), 1000 ether);
67+
68+
hooksContract = new HooksContract(0);
6169
}
6270

6371
function testOwnerTransfer() public {
@@ -90,6 +98,32 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
9098
assertEq(controller.owner(), makeAddr("newOwner"));
9199
}
92100

101+
function testSetDefaultProtocolFeeForDynamicFeePool(uint24 newDefaultProtocolFeeForDynamicFeePool) public {
102+
ProtocolFeeController controller = new ProtocolFeeController(address(clPoolManager));
103+
104+
// it should start with 0.03% as default
105+
assertEq(controller.defaultProtocolFeeForDynamicFeePool(), DEFAULT_PROTOCOL_FEE_FOR_DYNAMIC_FEE_POOL);
106+
107+
{
108+
// must from owner
109+
vm.prank(makeAddr("someone"));
110+
vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, makeAddr("someone")));
111+
controller.setDefaultProtocolFeeForDynamicFeePool(newDefaultProtocolFeeForDynamicFeePool);
112+
}
113+
114+
if (newDefaultProtocolFeeForDynamicFeePool > ProtocolFeeLibrary.MAX_PROTOCOL_FEE) {
115+
vm.expectRevert(ProtocolFeeController.InvalidDefaultProtocolFeeForDynamicFeePool.selector);
116+
controller.setDefaultProtocolFeeForDynamicFeePool(newDefaultProtocolFeeForDynamicFeePool);
117+
} else {
118+
vm.expectEmit(true, true, true, true);
119+
emit ProtocolFeeController.DefaultProtocolFeeForDynamicFeePoolUpdated(
120+
DEFAULT_PROTOCOL_FEE_FOR_DYNAMIC_FEE_POOL, newDefaultProtocolFeeForDynamicFeePool
121+
);
122+
controller.setDefaultProtocolFeeForDynamicFeePool(newDefaultProtocolFeeForDynamicFeePool);
123+
assertEq(controller.defaultProtocolFeeForDynamicFeePool(), newDefaultProtocolFeeForDynamicFeePool);
124+
}
125+
}
126+
93127
function testSetProcotolFeeSplitRatio(uint256 newProtocolFeeSplitRatio) public {
94128
ProtocolFeeController controller = new ProtocolFeeController(address(clPoolManager));
95129

@@ -363,6 +397,104 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
363397
}
364398
}
365399

400+
function testCLDynamicPoolInitWithProtolFeeControllerFuzz(uint24 newDefaultProtocolFeeForDynamicFeePool) public {
401+
ProtocolFeeController controller = new ProtocolFeeController(address(clPoolManager));
402+
newDefaultProtocolFeeForDynamicFeePool =
403+
uint24(bound(newDefaultProtocolFeeForDynamicFeePool, 0, ProtocolFeeLibrary.MAX_PROTOCOL_FEE));
404+
405+
clPoolManager.setProtocolFeeController(controller);
406+
407+
PoolKey memory key = PoolKey({
408+
currency0: currency0,
409+
currency1: currency1,
410+
hooks: hooksContract,
411+
poolManager: clPoolManager,
412+
fee: LPFeeLibrary.DYNAMIC_FEE_FLAG,
413+
parameters: bytes32(0).setTickSpacing(10)
414+
});
415+
clPoolManager.initialize(key, Constants.SQRT_RATIO_1_1);
416+
417+
(,, uint24 actualProtocolFee,) = clPoolManager.getSlot0(key.toId());
418+
419+
// under default rule protocol fee must be equal for both directions
420+
uint16 protocolFeeZeroForOne = actualProtocolFee.getZeroForOneFee();
421+
uint16 protocolFeeOneForZero = actualProtocolFee.getOneForZeroFee();
422+
assertEq(protocolFeeOneForZero, protocolFeeZeroForOne);
423+
assertEq(protocolFeeOneForZero, DEFAULT_PROTOCOL_FEE_FOR_DYNAMIC_FEE_POOL);
424+
425+
controller.setDefaultProtocolFeeForDynamicFeePool(newDefaultProtocolFeeForDynamicFeePool);
426+
427+
key.parameters = bytes32(0).setTickSpacing(30);
428+
clPoolManager.initialize(key, Constants.SQRT_RATIO_1_1);
429+
430+
(,, actualProtocolFee,) = clPoolManager.getSlot0(key.toId());
431+
// under default rule protocol fee must be equal for both directions
432+
protocolFeeZeroForOne = actualProtocolFee.getZeroForOneFee();
433+
protocolFeeOneForZero = actualProtocolFee.getOneForZeroFee();
434+
assertEq(protocolFeeOneForZero, protocolFeeZeroForOne);
435+
assertEq(protocolFeeOneForZero, newDefaultProtocolFeeForDynamicFeePool);
436+
437+
// verify the original pool is not affected
438+
{
439+
key.parameters = bytes32(0).setTickSpacing(10);
440+
441+
(,, actualProtocolFee,) = clPoolManager.getSlot0(key.toId());
442+
// under default rule protocol fee must be equal for both directions
443+
protocolFeeZeroForOne = actualProtocolFee.getZeroForOneFee();
444+
protocolFeeOneForZero = actualProtocolFee.getOneForZeroFee();
445+
assertEq(protocolFeeOneForZero, protocolFeeZeroForOne);
446+
}
447+
}
448+
449+
function testBinDynamicPoolInitWithProtolFeeControllerFuzz(uint24 newDefaultProtocolFeeForDynamicFeePool) public {
450+
ProtocolFeeController controller = new ProtocolFeeController(address(binPoolManager));
451+
newDefaultProtocolFeeForDynamicFeePool =
452+
uint24(bound(newDefaultProtocolFeeForDynamicFeePool, 0, ProtocolFeeLibrary.MAX_PROTOCOL_FEE));
453+
454+
binPoolManager.setProtocolFeeController(controller);
455+
456+
PoolKey memory key = PoolKey({
457+
currency0: currency0,
458+
currency1: currency1,
459+
hooks: hooksContract,
460+
poolManager: binPoolManager,
461+
fee: LPFeeLibrary.DYNAMIC_FEE_FLAG,
462+
parameters: bytes32(0).setBinStep(1)
463+
});
464+
binPoolManager.initialize(key, ID_ONE);
465+
466+
(, uint24 actualProtocolFee,) = binPoolManager.getSlot0(key.toId());
467+
468+
// under default rule protocol fee must be equal for both directions
469+
uint16 protocolFeeZeroForOne = actualProtocolFee.getZeroForOneFee();
470+
uint16 protocolFeeOneForZero = actualProtocolFee.getOneForZeroFee();
471+
assertEq(protocolFeeOneForZero, protocolFeeZeroForOne);
472+
assertEq(protocolFeeOneForZero, DEFAULT_PROTOCOL_FEE_FOR_DYNAMIC_FEE_POOL);
473+
474+
controller.setDefaultProtocolFeeForDynamicFeePool(newDefaultProtocolFeeForDynamicFeePool);
475+
476+
key.parameters = bytes32(0).setBinStep(5);
477+
binPoolManager.initialize(key, ID_ONE);
478+
479+
(, actualProtocolFee,) = binPoolManager.getSlot0(key.toId());
480+
// under default rule protocol fee must be equal for both directions
481+
protocolFeeZeroForOne = actualProtocolFee.getZeroForOneFee();
482+
protocolFeeOneForZero = actualProtocolFee.getOneForZeroFee();
483+
assertEq(protocolFeeOneForZero, protocolFeeZeroForOne);
484+
assertEq(protocolFeeOneForZero, newDefaultProtocolFeeForDynamicFeePool);
485+
486+
// verify the original pool is not affected
487+
{
488+
key.parameters = bytes32(0).setBinStep(1);
489+
490+
(, actualProtocolFee,) = binPoolManager.getSlot0(key.toId());
491+
// under default rule protocol fee must be equal for both directions
492+
protocolFeeZeroForOne = actualProtocolFee.getZeroForOneFee();
493+
protocolFeeOneForZero = actualProtocolFee.getOneForZeroFee();
494+
assertEq(protocolFeeOneForZero, protocolFeeZeroForOne);
495+
}
496+
}
497+
366498
function testSetProtocolFeeForCLPool(uint24 newProtocolFee) public {
367499
ProtocolFeeController controller = new ProtocolFeeController(address(clPoolManager));
368500
clPoolManager.setProtocolFeeController(controller);

0 commit comments

Comments
 (0)