Skip to content

Commit 391bb90

Browse files
committed
feat: update the default protocolFee for dynamic fee pool to 3bps
1 parent 40c2d50 commit 391bb90

2 files changed

Lines changed: 160 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.3% i.e. 3bps
39+
uint24 public defaultProtocolFeeForDynamicFeePool = 3000;
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.3% 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: 129 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;
@@ -43,6 +44,8 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
4344
BinSwapHelper public binSwapHelper;
4445
BinLiquidityHelper public binLiquidityHelper;
4546

47+
HooksContract public hooksContract;
48+
4649
function setUp() public {
4750
initializeTokens();
4851

@@ -58,6 +61,8 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
5861
IERC20(Currency.unwrap(currency1)).approve(address(binSwapHelper), 1000 ether);
5962
IERC20(Currency.unwrap(currency0)).approve(address(binLiquidityHelper), 1000 ether);
6063
IERC20(Currency.unwrap(currency1)).approve(address(binLiquidityHelper), 1000 ether);
64+
65+
hooksContract = new HooksContract(0);
6166
}
6267

6368
function testOwnerTransfer() public {
@@ -90,6 +95,32 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
9095
assertEq(controller.owner(), makeAddr("newOwner"));
9196
}
9297

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

@@ -363,6 +394,104 @@ contract ProtocolFeeControllerTest is Test, BinTestHelper, TokenFixture {
363394
}
364395
}
365396

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

0 commit comments

Comments
 (0)