From 20db597e32574afc35c309f3cc269a4a591ad8cf Mon Sep 17 00:00:00 2001 From: Jatin Jain Date: Tue, 29 Jul 2025 19:22:19 +0530 Subject: [PATCH 1/7] add: ownable 2 step hook --- remappings.txt | 3 ++- src/RenzoStability.sol | 31 +++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/remappings.txt b/remappings.txt index 9e5432f..9e649a9 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,4 +1,5 @@ @uniswap/v4-core/=lib/v4-core/ v4-core/=lib/v4-core/ v4-periphery/=lib/v4-periphery/ -v4-periphery/=lib/v4-periphery/ \ No newline at end of file +v4-periphery/=lib/v4-periphery/ +openzeppelin-contracts/=lib/v4-core/lib/openzeppelin-contracts/contracts/ \ No newline at end of file diff --git a/src/RenzoStability.sol b/src/RenzoStability.sol index 27cecf8..36562a6 100644 --- a/src/RenzoStability.sol +++ b/src/RenzoStability.sol @@ -10,6 +10,7 @@ import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol"; import {SqrtPriceLibrary} from "./libraries/SqrtPriceLibrary.sol"; import {IRateProvider} from "./interfaces/IRateProvider.sol"; import {LPFeeLibrary} from "v4-core/src/libraries/LPFeeLibrary.sol"; +import {Ownable2Step} from "openzeppelin-contracts/access/Ownable2Step.sol"; /// @title RenzoStability /// @notice A peg stability hook, for pairs that trade at a 1:1 ratio @@ -17,19 +18,18 @@ import {LPFeeLibrary} from "v4-core/src/libraries/LPFeeLibrary.sol"; /// otherwise it charges a linearly-scaled fee based on the distance from the peg /// i.e. if the pool price is off by 0.05% the fee is 0.05%, if the price is off by 0.50% the fee is 0.5% /// In the associated pool, Token 0 should be ETH and Token 1 should be ezETH -contract RenzoStability is PegStabilityHook { +contract RenzoStability is PegStabilityHook, Ownable2Step { using LPFeeLibrary for uint24; - IRateProvider public immutable rateProvider; - // Fee bps range where 1_000_000 = 100 % uint24 public constant MAX_FEE_BPS = 10_000; // 1% max fee allowed, 1% = 10_000 uint24 public constant MIN_FEE_BPS = 100; // 0.01% mix fee allowed - uint24 public immutable maxFeeBps; - uint24 public immutable minFeeBps; + IRateProvider public rateProvider; + uint24 public maxFeeBps; + uint24 public minFeeBps; - address public immutable ezETH; + address public ezETH; // Errors // @dev error when Invalid zero input params @@ -49,8 +49,9 @@ contract RenzoStability is PegStabilityHook { IRateProvider _rateProvider, uint24 _minFee, uint24 _maxFee, - address _ezETH - ) PegStabilityHook(_poolManager) { + address _ezETH, + address _initialOwner + ) PegStabilityHook(_poolManager) Ownable(_initialOwner) { // check for 0 value inputs if ( address(_rateProvider) == address(0) || @@ -71,6 +72,20 @@ contract RenzoStability is PegStabilityHook { ezETH = _ezETH; } + function configureFee(uint24 _minFee, uint24 _maxFee) external onlyOwner { + // check for 0 value inputs + if (_minFee == 0 || _maxFee == 0) revert InvalidZeroInput(); + + // check for maxFee + if (_maxFee > MAX_FEE_BPS) revert InvalidMaxFee(); + + // check for minFee range + if (_minFee > _maxFee || _minFee < MIN_FEE_BPS) revert InvalidMinFee(); + + minFeeBps = _minFee; + maxFeeBps = _maxFee; + } + /** * @dev Check that the pool key has a dynamic fee. * @dev Check that pair is ETH/ezETH From 0a6615bdcbfa8c4d58cb29e2023c7ee0b09c7ffc Mon Sep 17 00:00:00 2001 From: Jatin Jain Date: Tue, 29 Jul 2025 21:00:29 +0530 Subject: [PATCH 2/7] add: ownable hook configure --- script/deployPoolWithRenzoStability.s.sol | 22 +++--- ...olWithRenzoStabilityAndMintLiquidity.s.sol | 24 ++++--- src/RenzoStability.sol | 69 ++++++++++++------- test/RenzoStability.t.sol | 8 ++- 4 files changed, 78 insertions(+), 45 deletions(-) diff --git a/script/deployPoolWithRenzoStability.s.sol b/script/deployPoolWithRenzoStability.s.sol index 965a903..0b0d192 100644 --- a/script/deployPoolWithRenzoStability.s.sol +++ b/script/deployPoolWithRenzoStability.s.sol @@ -23,9 +23,11 @@ contract RenzoStabilityScript is Script, Constants, Config { // sepolia configurations IRateProvider rateProvider = IRateProvider(0x44Ad1be5B5912a497dAa147B7A3c55DC6067BFcF); - uint24 minFee = 100; - uint24 maxFee = 10_000; + uint24 minDynamicFee = 2_500; + uint24 maxDynamicFee = 10_000; + uint24 defaultFee = 100; address ezETH = 0x8d7F20137041334FBd7c87796f03b1999770Cc5f; + address owner = 0xD1e6626310fD54Eceb5b9a51dA2eC329D6D4B68A; // Multisig on mainnet // Pool configs // TODO: configure 0 zero values @@ -42,9 +44,11 @@ contract RenzoStabilityScript is Script, Constants, Config { bytes memory constructorArgs = abi.encode( POOLMANAGER, rateProvider, - minFee, - maxFee, - ezETH + defaultFee, + minDynamicFee, + maxDynamicFee, + ezETH, + owner ); (address hookAddress, bytes32 salt) = HookMiner.find( CREATE2_FACTORY, @@ -61,9 +65,11 @@ contract RenzoStabilityScript is Script, Constants, Config { RenzoStability renzoStability = new RenzoStability{salt: salt}( POOLMANAGER, rateProvider, - minFee, - maxFee, - ezETH + defaultFee, + minDynamicFee, + maxDynamicFee, + ezETH, + owner ); require( address(renzoStability) == hookAddress, diff --git a/script/deployPoolWithRenzoStabilityAndMintLiquidity.s.sol b/script/deployPoolWithRenzoStabilityAndMintLiquidity.s.sol index e26c407..ab66f19 100644 --- a/script/deployPoolWithRenzoStabilityAndMintLiquidity.s.sol +++ b/script/deployPoolWithRenzoStabilityAndMintLiquidity.s.sol @@ -34,9 +34,11 @@ contract CreatePoolAndAddLiquidityScript is Script, Constants, Config { // Hook configuration IRateProvider rateProvider = IRateProvider(0xDb6df3559D2d96985062F0824442550CA7715960); - uint24 minFee = 100; - uint24 maxFee = 10_000; - address ezETH = 0x2416092f143378750bb29b79eD961ab195CcEea5; + uint24 minDynamicFee = 2_500; + uint24 maxDynamicFee = 10_000; + uint24 defaultFee = 100; + address ezETH = 0x8d7F20137041334FBd7c87796f03b1999770Cc5f; + address owner = 0xD1e6626310fD54Eceb5b9a51dA2eC329D6D4B68A; // Multisig on mainnet address payable recipient = payable(0xAdef586efB3287Da4d7d1cbe15F12E0Be69e0DF0); @@ -154,9 +156,11 @@ contract CreatePoolAndAddLiquidityScript is Script, Constants, Config { bytes memory constructorArgs = abi.encode( POOLMANAGER, rateProvider, - minFee, - maxFee, - ezETH + defaultFee, + minDynamicFee, + maxDynamicFee, + ezETH, + owner ); (address hookAddress, bytes32 salt) = HookMiner.find( CREATE2_DEPLOYER, @@ -173,9 +177,11 @@ contract CreatePoolAndAddLiquidityScript is Script, Constants, Config { RenzoStability renzoStability = new RenzoStability{salt: salt}( POOLMANAGER, rateProvider, - minFee, - maxFee, - ezETH + defaultFee, + minDynamicFee, + maxDynamicFee, + ezETH, + owner ); vm.stopBroadcast(); // check that the hook was deployed at the expected address diff --git a/src/RenzoStability.sol b/src/RenzoStability.sol index 36562a6..4d7717f 100644 --- a/src/RenzoStability.sol +++ b/src/RenzoStability.sol @@ -10,7 +10,7 @@ import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol"; import {SqrtPriceLibrary} from "./libraries/SqrtPriceLibrary.sol"; import {IRateProvider} from "./interfaces/IRateProvider.sol"; import {LPFeeLibrary} from "v4-core/src/libraries/LPFeeLibrary.sol"; -import {Ownable2Step} from "openzeppelin-contracts/access/Ownable2Step.sol"; +import "openzeppelin-contracts/access/Ownable2Step.sol"; /// @title RenzoStability /// @notice A peg stability hook, for pairs that trade at a 1:1 ratio @@ -23,11 +23,12 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { // Fee bps range where 1_000_000 = 100 % uint24 public constant MAX_FEE_BPS = 10_000; // 1% max fee allowed, 1% = 10_000 - uint24 public constant MIN_FEE_BPS = 100; // 0.01% mix fee allowed + uint24 public constant MIN_FEE_BPS = 100; // 0.01% min fee allowed + uint24 public immutable defaultFeeBps; // default fee bps to charge if the pool price is off by less than maxDynamicFeeBps IRateProvider public rateProvider; - uint24 public maxFeeBps; - uint24 public minFeeBps; + uint24 public maxDynamicFeeBps; + uint24 public minDynamicFeeBps; address public ezETH; @@ -44,46 +45,62 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { /// @dev Error when Invalid Currency in Pool error InvalidPoolCurrency(); + /// @dev Error when default fee is not in range + error InvalidDefaultFee(); + constructor( IPoolManager _poolManager, IRateProvider _rateProvider, - uint24 _minFee, - uint24 _maxFee, + uint24 _defaultFeeBps, + uint24 _minDynamicFee, + uint24 _maxDynamicFee, address _ezETH, address _initialOwner ) PegStabilityHook(_poolManager) Ownable(_initialOwner) { // check for 0 value inputs if ( address(_rateProvider) == address(0) || - _minFee == 0 || - _maxFee == 0 || + _minDynamicFee == 0 || + _maxDynamicFee == 0 || + _defaultFeeBps == 0 || _ezETH == address(0) ) revert InvalidZeroInput(); - // check for maxFee - if (_maxFee > MAX_FEE_BPS) revert InvalidMaxFee(); + // check for default fee range, MIN_FEE_BPS <= defaultFeeBps <= maxFee + if (_defaultFeeBps < MIN_FEE_BPS || _defaultFeeBps > _minDynamicFee) + revert InvalidDefaultFee(); - // check for minFee range - if (_minFee > _maxFee || _minFee < MIN_FEE_BPS) revert InvalidMinFee(); + // check for maxFee, _maxDynamicFee <= MAX_FEE_BPS + if (_maxDynamicFee > MAX_FEE_BPS) revert InvalidMaxFee(); + + // check for minFee range, MIN_FEE_BPS <= _minDynamicFee <= _maxDynamicFee + if (_minDynamicFee > _maxDynamicFee || _minDynamicFee < MIN_FEE_BPS) + revert InvalidMinFee(); rateProvider = _rateProvider; - minFeeBps = _minFee; - maxFeeBps = _maxFee; + defaultFeeBps = _defaultFeeBps; + minDynamicFeeBps = _minDynamicFee; + maxDynamicFeeBps = _maxDynamicFee; ezETH = _ezETH; } - function configureFee(uint24 _minFee, uint24 _maxFee) external onlyOwner { + function configureFee( + uint24 _minDynamicFee, + uint24 _maxDynamicFee + ) external onlyOwner { // check for 0 value inputs - if (_minFee == 0 || _maxFee == 0) revert InvalidZeroInput(); + if (_minDynamicFee == 0 || _maxDynamicFee == 0) + revert InvalidZeroInput(); // check for maxFee - if (_maxFee > MAX_FEE_BPS) revert InvalidMaxFee(); + if (_maxDynamicFee > MAX_FEE_BPS) revert InvalidMaxFee(); // check for minFee range - if (_minFee > _maxFee || _minFee < MIN_FEE_BPS) revert InvalidMinFee(); + if (_minDynamicFee > _maxDynamicFee || _minDynamicFee < MIN_FEE_BPS) + revert InvalidMinFee(); - minFeeBps = _minFee; - maxFeeBps = _maxFee; + minDynamicFeeBps = _minDynamicFee; + maxDynamicFeeBps = _maxDynamicFee; } /** @@ -140,7 +157,7 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { ) internal view override returns (uint24) { // pool price is less than reference price (over pegged), or zeroForOne trades are moving towards the reference price if (zeroForOne || poolSqrtPriceX96 < referenceSqrtPriceX96) - return minFeeBps; // minFee bip + return defaultFeeBps; // minFee bip // computes the absolute percentage difference between the pool price and the reference price // i.e. 0.005e18 = 0.50% difference between pool price and reference price @@ -153,12 +170,12 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { // convert percentage WAD to pips, i.e. 0.05e18 = 5% = 50_000 // the fee itself is the percentage difference uint24 fee = uint24(absPercentageDiffWad / 1e12); - if (fee < minFeeBps) { - // if % depeg is less than min fee %. charge minFee - fee = minFeeBps; - } else if (fee > maxFeeBps) { + if (fee < minDynamicFeeBps) { + // if % depeg is less than min fee %. charge default fee + fee = defaultFeeBps; + } else if (fee > maxDynamicFeeBps) { // if % depeg is more than max fee %. charge maxFee - fee = maxFeeBps; + fee = maxDynamicFeeBps; } return fee; } diff --git a/test/RenzoStability.t.sol b/test/RenzoStability.t.sol index e27205f..c2e5e4d 100644 --- a/test/RenzoStability.t.sol +++ b/test/RenzoStability.t.sol @@ -27,8 +27,10 @@ contract RenzoStabilityTest is Deployers { // Hook configs. TODO: configure IRateProvider rateProvider = IRateProvider(makeAddr("rateProvider")); uint256 exchangeRate = 1046726277868365115; - uint24 minFee = 100; + uint24 defaultFee = 100; // 0.01% + uint24 minFee = 2_500; uint24 maxFee = 10_000; + address owner = makeAddr("owner"); using PoolIdLibrary for PoolKey; using CurrencyLibrary for Currency; @@ -59,9 +61,11 @@ contract RenzoStabilityTest is Deployers { bytes memory constructorArgs = abi.encode( manager, rateProvider, + defaultFee, minFee, maxFee, - Currency.unwrap(currency1) + Currency.unwrap(currency1), + owner ); //Add all the necessary constructor arguments from the hook deployCodeTo( "RenzoStability.sol:RenzoStability", From 2db2103f1e4106e25e8256724392b396cd161154 Mon Sep 17 00:00:00 2001 From: Jatin Jain Date: Wed, 30 Jul 2025 14:14:15 +0530 Subject: [PATCH 3/7] fix: swap tests --- test/RenzoStability.t.sol | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/RenzoStability.t.sol b/test/RenzoStability.t.sol index c2e5e4d..892da11 100644 --- a/test/RenzoStability.t.sol +++ b/test/RenzoStability.t.sol @@ -146,7 +146,7 @@ contract RenzoStabilityTest is Deployers { vm.recordLogs(); BalanceDelta ref = swap(key, zeroForOne, -int256(0.1e18), ZERO_BYTES); Vm.Log[] memory recordedLogs = vm.getRecordedLogs(); - recordedLogs.assertSwapFee(minFee); + recordedLogs.assertSwapFee(defaultFee); // move the pool price to off peg swap(key, zeroForOne, -int256(1000e18), ZERO_BYTES); @@ -160,7 +160,7 @@ contract RenzoStabilityTest is Deployers { ZERO_BYTES ); recordedLogs = vm.getRecordedLogs(); - recordedLogs.assertSwapFee(zeroForOne ? minFee : maxFee); + recordedLogs.assertSwapFee(zeroForOne ? defaultFee : maxFee); // output of the second swap is much less // highFeeSwap + offset < ref @@ -197,10 +197,10 @@ contract RenzoStabilityTest is Deployers { uint24 lowerFee = recordedLogs.getSwapFeeFromEvent(); if (zeroForOne) { assertGt(higherFee, lowerFee); - assertEq(lowerFee, minFee); // minFee + assertEq(lowerFee, defaultFee); // defaultFee } else { - assertEq(lowerFee, minFee); // minFee - assertEq(higherFee, minFee); // minFee + assertEq(lowerFee, defaultFee); // defaultFee + assertEq(higherFee, defaultFee); // defaultFee } // output of the second swap is much higher @@ -221,10 +221,12 @@ contract RenzoStabilityTest is Deployers { uint160(poolSqrtPriceX96), SqrtPriceLibrary.exchangeRateToSqrtPriceX96(exchangeRate) ); - uint24 expectedFee = uint24(absPercentageDiffWad / 1e12); + uint24 expectedFee = uint24(absPercentageDiffWad / 1e12) > minFee + ? uint24(absPercentageDiffWad / 1e12) + : defaultFee; // move the pool price away from peg vm.recordLogs(); - swap(key, false, -int256(0.1e18), ZERO_BYTES); + swap(key, false, -int256(11e18), ZERO_BYTES); Vm.Log[] memory recordedLogs = vm.getRecordedLogs(); uint24 swapFee = recordedLogs.getSwapFeeFromEvent(); assertEq(swapFee, expectedFee); From 653e3a42932b434df3a14d09721bc55324c0c231 Mon Sep 17 00:00:00 2001 From: Jatin Jain Date: Wed, 30 Jul 2025 14:30:52 +0530 Subject: [PATCH 4/7] add: configure fee test --- src/RenzoStability.sol | 7 ++++-- test/RenzoStability.t.sol | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/RenzoStability.sol b/src/RenzoStability.sol index 4d7717f..f3b0266 100644 --- a/src/RenzoStability.sol +++ b/src/RenzoStability.sol @@ -96,8 +96,11 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { if (_maxDynamicFee > MAX_FEE_BPS) revert InvalidMaxFee(); // check for minFee range - if (_minDynamicFee > _maxDynamicFee || _minDynamicFee < MIN_FEE_BPS) - revert InvalidMinFee(); + if ( + _minDynamicFee > _maxDynamicFee || + _minDynamicFee < MIN_FEE_BPS || + _minDynamicFee < defaultFeeBps + ) revert InvalidMinFee(); minDynamicFeeBps = _minDynamicFee; maxDynamicFeeBps = _maxDynamicFee; diff --git a/test/RenzoStability.t.sol b/test/RenzoStability.t.sol index 892da11..c92b183 100644 --- a/test/RenzoStability.t.sol +++ b/test/RenzoStability.t.sol @@ -22,6 +22,7 @@ import {IRateProvider} from "../src/interfaces/IRateProvider.sol"; import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol"; import {SwapFeeEventAsserter} from "./utils/SwapFeeEventAsserter.sol"; import {SqrtPriceLibrary} from "../src/libraries/SqrtPriceLibrary.sol"; +import "openzeppelin-contracts/access/Ownable.sol"; contract RenzoStabilityTest is Deployers { // Hook configs. TODO: configure @@ -31,6 +32,7 @@ contract RenzoStabilityTest is Deployers { uint24 minFee = 2_500; uint24 maxFee = 10_000; address owner = makeAddr("owner"); + address ALICE = makeAddr("alice"); using PoolIdLibrary for PoolKey; using CurrencyLibrary for Currency; @@ -114,6 +116,56 @@ contract RenzoStabilityTest is Deployers { ); } + function test_configureFee() public { + uint24 _minFee = 2_600; + uint24 _maxFee = 9_000; + + vm.startPrank(ALICE); + // Verify revert if not owner + vm.expectRevert( + abi.encodeWithSelector( + Ownable.OwnableUnauthorizedAccount.selector, + ALICE + ) + ); + hook.configureFee(100, 200); + + vm.stopPrank(); + + vm.startPrank(owner); + // Verify revert if minFee is 0 + vm.expectRevert(RenzoStability.InvalidZeroInput.selector); + hook.configureFee(0, _maxFee); + + // Verify revert if maxFee is 0 + vm.expectRevert(RenzoStability.InvalidZeroInput.selector); + hook.configureFee(_minFee, 0); + + // Verify revert if maxFee is greater than MAX_FEE_BPS + vm.expectRevert(RenzoStability.InvalidMaxFee.selector); + hook.configureFee(_minFee, 100_001); + + // Verify revert if minFee is greater than maxFee + vm.expectRevert(RenzoStability.InvalidMinFee.selector); + hook.configureFee(_maxFee, _minFee); + + // Verify revert if minFee is less than MIN_FEE_BPS + vm.expectRevert(RenzoStability.InvalidMinFee.selector); + hook.configureFee(50, _maxFee); + + // Verify revert if min fee is less than default fee + vm.expectRevert(RenzoStability.InvalidMinFee.selector); + hook.configureFee(defaultFee - 1, _maxFee); + + // Configure the fee + hook.configureFee(_minFee, _maxFee); + vm.stopPrank(); + + // Verify the fee is configured correctly + assertEq(hook.minDynamicFeeBps(), _minFee); + assertEq(hook.maxDynamicFeeBps(), _maxFee); + } + function test_fuzz_swap(bool zeroForOne, bool exactIn) public { int256 amountSpecified = exactIn ? -int256(1e18) : int256(1e18); uint256 msgValue = zeroForOne ? 2e18 : 0; From 6be81ffb242dd07c155881ea1230bbe4e015b39b Mon Sep 17 00:00:00 2001 From: Jatin Jain Date: Thu, 31 Jul 2025 13:12:51 +0530 Subject: [PATCH 5/7] add: natspec and event --- src/RenzoStability.sol | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/RenzoStability.sol b/src/RenzoStability.sol index f3b0266..96db09e 100644 --- a/src/RenzoStability.sol +++ b/src/RenzoStability.sol @@ -32,6 +32,15 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { address public ezETH; + // Events + // @dev Event emitted when the fee configuration is updated + event FeeConfigurationUpdated( + uint24 oldMinDynamicFeeBps, + uint24 oldMaxDynamicFeeBps, + uint24 newMinDynamicFeeBps, + uint24 newMaxDynamicFeeBps + ); + // Errors // @dev error when Invalid zero input params error InvalidZeroInput(); @@ -84,6 +93,12 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { ezETH = _ezETH; } + /** + * @notice Allows the owner to configure the dynamic fee range + * @dev Permissioned call (onlyOwner) + * @param _minDynamicFee new minimum dynamic fee bps + * @param _maxDynamicFee new maximum dynamic fee bps + */ function configureFee( uint24 _minDynamicFee, uint24 _maxDynamicFee @@ -102,6 +117,13 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { _minDynamicFee < defaultFeeBps ) revert InvalidMinFee(); + emit FeeConfigurationUpdated( + minDynamicFeeBps, + maxDynamicFeeBps, + _minDynamicFee, + _maxDynamicFee + ); + minDynamicFeeBps = _minDynamicFee; maxDynamicFeeBps = _maxDynamicFee; } From a4f5731f3ace9de343cca0297440a6c21d77164d Mon Sep 17 00:00:00 2001 From: Jatin Jain Date: Fri, 1 Aug 2025 15:09:08 +0530 Subject: [PATCH 6/7] fix: comment --- src/RenzoStability.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RenzoStability.sol b/src/RenzoStability.sol index 96db09e..597864f 100644 --- a/src/RenzoStability.sol +++ b/src/RenzoStability.sol @@ -24,7 +24,7 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { // Fee bps range where 1_000_000 = 100 % uint24 public constant MAX_FEE_BPS = 10_000; // 1% max fee allowed, 1% = 10_000 uint24 public constant MIN_FEE_BPS = 100; // 0.01% min fee allowed - uint24 public immutable defaultFeeBps; // default fee bps to charge if the pool price is off by less than maxDynamicFeeBps + uint24 public immutable defaultFeeBps; // default fee bps to charge if the pool price is off by less than minDynamicFeeBps IRateProvider public rateProvider; uint24 public maxDynamicFeeBps; From 109f0ecfecc86b358fdc7fa987aa562f24287059 Mon Sep 17 00:00:00 2001 From: Jatin Jain Date: Fri, 1 Aug 2025 20:03:06 +0530 Subject: [PATCH 7/7] add: lower min fee floor value --- ...olWithRenzoStabilityAndMintLiquidity.s.sol | 2 +- src/RenzoStability.sol | 27 ++++++++------ test/RenzoStability.t.sol | 35 ++++++++++++------- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/script/deployPoolWithRenzoStabilityAndMintLiquidity.s.sol b/script/deployPoolWithRenzoStabilityAndMintLiquidity.s.sol index ab66f19..224f089 100644 --- a/script/deployPoolWithRenzoStabilityAndMintLiquidity.s.sol +++ b/script/deployPoolWithRenzoStabilityAndMintLiquidity.s.sol @@ -36,7 +36,7 @@ contract CreatePoolAndAddLiquidityScript is Script, Constants, Config { IRateProvider(0xDb6df3559D2d96985062F0824442550CA7715960); uint24 minDynamicFee = 2_500; uint24 maxDynamicFee = 10_000; - uint24 defaultFee = 100; + uint24 defaultFee = 95; address ezETH = 0x8d7F20137041334FBd7c87796f03b1999770Cc5f; address owner = 0xD1e6626310fD54Eceb5b9a51dA2eC329D6D4B68A; // Multisig on mainnet address payable recipient = diff --git a/src/RenzoStability.sol b/src/RenzoStability.sol index 597864f..09d0bcf 100644 --- a/src/RenzoStability.sol +++ b/src/RenzoStability.sol @@ -23,10 +23,10 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { // Fee bps range where 1_000_000 = 100 % uint24 public constant MAX_FEE_BPS = 10_000; // 1% max fee allowed, 1% = 10_000 - uint24 public constant MIN_FEE_BPS = 100; // 0.01% min fee allowed - uint24 public immutable defaultFeeBps; // default fee bps to charge if the pool price is off by less than minDynamicFeeBps + uint24 public constant MIN_FEE_BPS = 50; // 0.005% min fee allowed IRateProvider public rateProvider; + uint24 public defaultFeeBps; // default fee bps to charge if the pool price is off by less than minDynamicFeeBps uint24 public maxDynamicFeeBps; uint24 public minDynamicFeeBps; @@ -35,8 +35,10 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { // Events // @dev Event emitted when the fee configuration is updated event FeeConfigurationUpdated( + uint24 oldDefaultFeeBps, uint24 oldMinDynamicFeeBps, uint24 oldMaxDynamicFeeBps, + uint24 newDefaultFeeBps, uint24 newMinDynamicFeeBps, uint24 newMaxDynamicFeeBps ); @@ -100,30 +102,35 @@ contract RenzoStability is PegStabilityHook, Ownable2Step { * @param _maxDynamicFee new maximum dynamic fee bps */ function configureFee( + uint24 _defaultFeeBps, uint24 _minDynamicFee, uint24 _maxDynamicFee ) external onlyOwner { // check for 0 value inputs - if (_minDynamicFee == 0 || _maxDynamicFee == 0) + if (_minDynamicFee == 0 || _maxDynamicFee == 0 || _defaultFeeBps == 0) revert InvalidZeroInput(); - // check for maxFee + // check for default fee range, MIN_FEE_BPS <= defaultFeeBps <= maxFee + if (_defaultFeeBps < MIN_FEE_BPS || _defaultFeeBps > _minDynamicFee) + revert InvalidDefaultFee(); + + // check for maxFee, _maxDynamicFee <= MAX_FEE_BPS if (_maxDynamicFee > MAX_FEE_BPS) revert InvalidMaxFee(); - // check for minFee range - if ( - _minDynamicFee > _maxDynamicFee || - _minDynamicFee < MIN_FEE_BPS || - _minDynamicFee < defaultFeeBps - ) revert InvalidMinFee(); + // check for minFee range, MIN_FEE_BPS <= _minDynamicFee <= _maxDynamicFee + if (_minDynamicFee > _maxDynamicFee || _minDynamicFee < MIN_FEE_BPS) + revert InvalidMinFee(); emit FeeConfigurationUpdated( + defaultFeeBps, minDynamicFeeBps, maxDynamicFeeBps, + _defaultFeeBps, _minDynamicFee, _maxDynamicFee ); + defaultFeeBps = _defaultFeeBps; minDynamicFeeBps = _minDynamicFee; maxDynamicFeeBps = _maxDynamicFee; } diff --git a/test/RenzoStability.t.sol b/test/RenzoStability.t.sol index c92b183..a247755 100644 --- a/test/RenzoStability.t.sol +++ b/test/RenzoStability.t.sol @@ -117,9 +117,11 @@ contract RenzoStabilityTest is Deployers { } function test_configureFee() public { + uint24 _defaultFeeBps = 100; uint24 _minFee = 2_600; uint24 _maxFee = 9_000; + uint24 currentMinFee = hook.MIN_FEE_BPS(); vm.startPrank(ALICE); // Verify revert if not owner vm.expectRevert( @@ -128,40 +130,49 @@ contract RenzoStabilityTest is Deployers { ALICE ) ); - hook.configureFee(100, 200); + hook.configureFee(50, 100, 200); vm.stopPrank(); vm.startPrank(owner); + // Verify revert if default fee is 0 + vm.expectRevert(RenzoStability.InvalidZeroInput.selector); + hook.configureFee(0, _minFee, _maxFee); + // Verify revert if minFee is 0 vm.expectRevert(RenzoStability.InvalidZeroInput.selector); - hook.configureFee(0, _maxFee); + hook.configureFee(_defaultFeeBps, 0, _maxFee); // Verify revert if maxFee is 0 vm.expectRevert(RenzoStability.InvalidZeroInput.selector); - hook.configureFee(_minFee, 0); + hook.configureFee(_defaultFeeBps, _minFee, 0); + + // verify revert if default fee is less than MIN_FEE_BPS + vm.expectRevert(RenzoStability.InvalidDefaultFee.selector); + hook.configureFee(currentMinFee - 1, _minFee, _maxFee); + + // verify revert if default fee is greater than minFee + vm.expectRevert(RenzoStability.InvalidDefaultFee.selector); + hook.configureFee(_minFee + 1, _minFee, _maxFee); // Verify revert if maxFee is greater than MAX_FEE_BPS vm.expectRevert(RenzoStability.InvalidMaxFee.selector); - hook.configureFee(_minFee, 100_001); + hook.configureFee(_defaultFeeBps, _minFee, 100_001); // Verify revert if minFee is greater than maxFee vm.expectRevert(RenzoStability.InvalidMinFee.selector); - hook.configureFee(_maxFee, _minFee); + hook.configureFee(_defaultFeeBps, _maxFee, _minFee); // Verify revert if minFee is less than MIN_FEE_BPS - vm.expectRevert(RenzoStability.InvalidMinFee.selector); - hook.configureFee(50, _maxFee); - - // Verify revert if min fee is less than default fee - vm.expectRevert(RenzoStability.InvalidMinFee.selector); - hook.configureFee(defaultFee - 1, _maxFee); + vm.expectRevert(RenzoStability.InvalidDefaultFee.selector); + hook.configureFee(_defaultFeeBps, currentMinFee - 1, _maxFee); // Configure the fee - hook.configureFee(_minFee, _maxFee); + hook.configureFee(_defaultFeeBps, _minFee, _maxFee); vm.stopPrank(); // Verify the fee is configured correctly + assertEq(hook.defaultFeeBps(), _defaultFeeBps); assertEq(hook.minDynamicFeeBps(), _minFee); assertEq(hook.maxDynamicFeeBps(), _maxFee); }