Skip to content

Commit

Permalink
refactor swap and protocol fees
Browse files Browse the repository at this point in the history
  • Loading branch information
hensha256 committed Mar 8, 2024
1 parent 0e8e6c9 commit 1f73591
Show file tree
Hide file tree
Showing 39 changed files with 115 additions and 110 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity with empty hook.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
312661
312595
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity with native token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
192843
192777
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
192821
192755
Original file line number Diff line number Diff line change
@@ -1 +1 @@
184735
184650
2 changes: 1 addition & 1 deletion .forge-snapshots/cached dynamic fee, no hooks.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
138762
138677
2 changes: 1 addition & 1 deletion .forge-snapshots/donate gas with 1 token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
131854
131769
2 changes: 1 addition & 1 deletion .forge-snapshots/donate gas with 2 tokens.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
176958
176850
2 changes: 1 addition & 1 deletion .forge-snapshots/erc20 collect protocol fees.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24982
24938
2 changes: 1 addition & 1 deletion .forge-snapshots/initialize.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
51725
51768
2 changes: 1 addition & 1 deletion .forge-snapshots/native collect protocol fees.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
36655
36611
2 changes: 1 addition & 1 deletion .forge-snapshots/poolManager bytecode size.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
23345
23300
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity with empty hook.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
99632
99612
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity with native token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
201005
200985
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
197282
197262
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap with native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
187335
187250
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
195872
195787
Original file line number Diff line number Diff line change
@@ -1 +1 @@
117998
117913
2 changes: 1 addition & 1 deletion .forge-snapshots/swap against liquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
105462
105377
2 changes: 1 addition & 1 deletion .forge-snapshots/swap burn 6909 for input.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125651
125589
2 changes: 1 addition & 1 deletion .forge-snapshots/swap burn native 6909 for input.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
121604
121542
2 changes: 1 addition & 1 deletion .forge-snapshots/swap mint native output as 6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
189861
189776
2 changes: 1 addition & 1 deletion .forge-snapshots/swap mint output as 6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
206666
206581
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with dynamic fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
183836
183751
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with hooks.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
105440
105355
2 changes: 1 addition & 1 deletion .forge-snapshots/update dynamic fee in before swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
190540
190516
19 changes: 9 additions & 10 deletions src/PoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ import {Hooks} from "./libraries/Hooks.sol";
import {Pool} from "./libraries/Pool.sol";
import {SafeCast} from "./libraries/SafeCast.sol";
import {Position} from "./libraries/Position.sol";
import {FeeLibrary} from "./libraries/FeeLibrary.sol";
import {SwapFeeLibrary} from "./libraries/SwapFeeLibrary.sol";
import {Currency, CurrencyLibrary} from "./types/Currency.sol";
import {PoolKey} from "./types/PoolKey.sol";
import {TickMath} from "./libraries/TickMath.sol";
import {NoDelegateCall} from "./NoDelegateCall.sol";
import {Owned} from "./Owned.sol";
import {IHooks} from "./interfaces/IHooks.sol";
import {IDynamicFeeManager} from "./interfaces/IDynamicFeeManager.sol";
import {IPoolManager} from "./interfaces/IPoolManager.sol";
import {ILockCallback} from "./interfaces/callback/ILockCallback.sol";
import {Fees} from "./Fees.sol";
import {ProtocolFees} from "./ProtocolFees.sol";
import {ERC6909Claims} from "./ERC6909Claims.sol";
import {PoolId, PoolIdLibrary} from "./types/PoolId.sol";
import {BalanceDelta, BalanceDeltaLibrary} from "./types/BalanceDelta.sol";
Expand All @@ -24,14 +23,14 @@ import {NonZeroDeltaCount} from "./libraries/NonZeroDeltaCount.sol";
import {PoolGetters} from "./libraries/PoolGetters.sol";

/// @notice Holds the state for all pools
contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {
contract PoolManager is IPoolManager, ProtocolFees, NoDelegateCall, ERC6909Claims {
using PoolIdLibrary for PoolKey;
using SafeCast for *;
using Pool for *;
using Hooks for IHooks;
using Position for mapping(bytes32 => Position.Info);
using CurrencyLibrary for Currency;
using FeeLibrary for uint24;
using SwapFeeLibrary for uint24;
using PoolGetters for Pool.State;

/// @inheritdoc IPoolManager
Expand All @@ -50,7 +49,7 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {

mapping(PoolId id => Pool.State) public pools;

constructor(uint256 controllerGasLimit) Fees(controllerGasLimit) {}
constructor(uint256 controllerGasLimit) ProtocolFees(controllerGasLimit) {}

/// @inheritdoc IPoolManager
function getSlot0(PoolId id)
Expand Down Expand Up @@ -105,8 +104,6 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {
override
returns (int24 tick)
{
if (key.fee.isStaticFeeTooLarge()) revert FeeTooLarge();

// see TickBitmap.sol for overflow conditions that can arise from tick spacing being too large
if (key.tickSpacing > MAX_TICK_SPACING) revert TickSpacingTooLarge();
if (key.tickSpacing < MIN_TICK_SPACING) revert TickSpacingTooSmall();
Expand All @@ -117,7 +114,8 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {

PoolId id = key.toId();
(, uint16 protocolFee) = _fetchProtocolFee(key);
uint24 swapFee = key.fee.isDynamicFee() ? _fetchDynamicSwapFee(key) : key.fee.getStaticFee();
uint24 swapFee = key.fee.isDynamicFee() ? key.hooks.fetchDynamicSwapFee(key) : key.fee.getStaticFee();
swapFee.validateSwapFee();

tick = pools[id].initialize(sqrtPriceX96, protocolFee, swapFee);

Expand Down Expand Up @@ -294,7 +292,8 @@ contract PoolManager is IPoolManager, Fees, NoDelegateCall, ERC6909Claims {

function updateDynamicSwapFee(PoolKey memory key) external {
if (key.fee.isDynamicFee()) {
uint24 newDynamicSwapFee = _fetchDynamicSwapFee(key);
uint24 newDynamicSwapFee = key.hooks.fetchDynamicSwapFee(key);
newDynamicSwapFee.validateSwapFee();
PoolId id = key.toId();
pools[id].setSwapFee(newDynamicSwapFee);
emit DynamicSwapFeeUpdated(id, newDynamicSwapFee);
Expand Down
19 changes: 4 additions & 15 deletions src/Fees.sol → src/ProtocolFees.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,16 @@ pragma solidity ^0.8.19;

import {Currency, CurrencyLibrary} from "./types/Currency.sol";
import {IProtocolFeeController} from "./interfaces/IProtocolFeeController.sol";
import {IFees} from "./interfaces/IFees.sol";
import {FeeLibrary} from "./libraries/FeeLibrary.sol";
import {IProtocolFees} from "./interfaces/IProtocolFees.sol";
import {Pool} from "./libraries/Pool.sol";
import {PoolKey} from "./types/PoolKey.sol";
import {Owned} from "./Owned.sol";
import {IDynamicFeeManager} from "./interfaces/IDynamicFeeManager.sol";

abstract contract Fees is IFees, Owned {
using FeeLibrary for uint24;
abstract contract ProtocolFees is IProtocolFees, Owned {
using CurrencyLibrary for Currency;

uint8 public constant MIN_PROTOCOL_FEE_DENOMINATOR = 4;

// the swap fee is represented in hundredths of a bip, so the max is 100%
uint24 public constant MAX_SWAP_FEE = 1000000;

mapping(Currency currency => uint256) public protocolFeesAccrued;

IProtocolFeeController public protocolFeeController;
Expand Down Expand Up @@ -51,18 +45,13 @@ abstract contract Fees is IFees, Owned {
returnData := mload(add(_data, 0x20))
}
// Ensure return data does not overflow a uint16 and that the underlying fees are within bounds.
(success, protocolFees) = returnData == uint16(returnData) && _isFeeWithinBounds(uint16(returnData))
(success, protocolFees) = returnData == uint16(returnData) && _isValidProtocolFee(uint16(returnData))
? (true, uint16(returnData))
: (false, 0);
}
}

function _fetchDynamicSwapFee(PoolKey memory key) internal view returns (uint24 dynamicSwapFee) {
dynamicSwapFee = IDynamicFeeManager(address(key.hooks)).getFee(msg.sender, key);
if (dynamicSwapFee >= MAX_SWAP_FEE) revert FeeTooLarge();
}

function _isFeeWithinBounds(uint16 fee) internal pure returns (bool) {
function _isValidProtocolFee(uint16 fee) internal pure returns (bool) {
if (fee != 0) {
uint16 fee0 = fee % 256;
uint16 fee1 = fee >> 8;
Expand Down
4 changes: 2 additions & 2 deletions src/interfaces/IPoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {PoolKey} from "../types/PoolKey.sol";
import {Pool} from "../libraries/Pool.sol";
import {IHooks} from "./IHooks.sol";
import {IERC6909Claims} from "./external/IERC6909Claims.sol";
import {IFees} from "./IFees.sol";
import {IProtocolFees} from "./IProtocolFees.sol";
import {BalanceDelta} from "../types/BalanceDelta.sol";
import {PoolId} from "../types/PoolId.sol";
import {Position} from "../libraries/Position.sol";

interface IPoolManager is IFees, IERC6909Claims {
interface IPoolManager is IProtocolFees, IERC6909Claims {
/// @notice Thrown when currencies touched has exceeded max of 256
error MaxCurrenciesTouched();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ pragma solidity ^0.8.19;

import {Currency} from "../types/Currency.sol";

interface IFees {
/// @notice Thrown when the protocol fee denominator is less than 4. Also thrown when the static or dynamic fee on a pool is exceeds 100%.
error FeeTooLarge();
interface IProtocolFees {
/// @notice Thrown when not enough gas is provided to look up the protocol fee
error ProtocolFeeCannotBeFetched();
/// @notice Thrown when the call to fetch the protocol fee reverts or returns invalid data.
Expand Down
19 changes: 0 additions & 19 deletions src/libraries/FeeLibrary.sol

This file was deleted.

9 changes: 7 additions & 2 deletions src/libraries/Hooks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ pragma solidity ^0.8.24;

import {PoolKey} from "../types/PoolKey.sol";
import {IHooks} from "../interfaces/IHooks.sol";
import {FeeLibrary} from "./FeeLibrary.sol";
import {SwapFeeLibrary} from "./SwapFeeLibrary.sol";
import {BalanceDelta} from "../types/BalanceDelta.sol";
import {IPoolManager} from "../interfaces/IPoolManager.sol";
import {Locker} from "./Locker.sol";
import {IDynamicFeeManager} from "../interfaces/IDynamicFeeManager.sol";

/// @notice V4 decides whether to invoke specific hooks by inspecting the leading bits of the address that
/// the hooks contract is deployed to.
/// For example, a hooks contract deployed to address: 0x9000000000000000000000000000000000000000
/// has leading bits '1001' which would cause the 'before initialize' and 'after add liquidity' hooks to be used.
library Hooks {
using FeeLibrary for uint24;
using SwapFeeLibrary for uint24;
using Hooks for IHooks;

uint256 internal constant BEFORE_INITIALIZE_FLAG = 1 << 159;
Expand Down Expand Up @@ -209,6 +210,10 @@ library Hooks {
return uint256(uint160(address(self))) & flag != 0;
}

function fetchDynamicSwapFee(IHooks self, PoolKey memory key) internal view returns (uint24 dynamicSwapFee) {
dynamicSwapFee = IDynamicFeeManager(address(self)).getFee(msg.sender, key);
}

/// @notice bubble up revert if present. Else throw FailedHookCall
function _revert(bytes memory result) private pure {
if (result.length > 0) {
Expand Down
29 changes: 29 additions & 0 deletions src/libraries/SwapFeeLibrary.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import {PoolKey} from "../types/PoolKey.sol";

library SwapFeeLibrary {
using SwapFeeLibrary for uint24;

/// @notice Thrown when the static or dynamic fee on a pool is exceeds 100%.
error FeeTooLarge();

uint24 public constant STATIC_FEE_MASK = 0x0FFFFF;
uint24 public constant DYNAMIC_FEE_FLAG = 0x800000;

// the swap fee is represented in hundredths of a bip, so the max is 100%
uint24 public constant MAX_SWAP_FEE = 1000000;

function isDynamicFee(uint24 self) internal pure returns (bool) {
return self & DYNAMIC_FEE_FLAG != 0;
}

function validateSwapFee(uint24 self) internal pure {
if (self >= MAX_SWAP_FEE) revert FeeTooLarge();
}

function getStaticFee(uint24 self) internal pure returns (uint24) {
return self & STATIC_FEE_MASK;
}
}
4 changes: 2 additions & 2 deletions src/test/PoolModifyLiquidityTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import {PoolTestBase} from "./PoolTestBase.sol";
import {IHooks} from "../interfaces/IHooks.sol";
import {Hooks} from "../libraries/Hooks.sol";
import {Test} from "forge-std/Test.sol";
import {FeeLibrary} from "../libraries/FeeLibrary.sol";
import {SwapFeeLibrary} from "../libraries/SwapFeeLibrary.sol";

contract PoolModifyLiquidityTest is Test, PoolTestBase {
using CurrencyLibrary for Currency;
using Hooks for IHooks;
using FeeLibrary for uint24;
using SwapFeeLibrary for uint24;

constructor(IPoolManager _manager) PoolTestBase(_manager) {}

Expand Down
Loading

0 comments on commit 1f73591

Please sign in to comment.