From 06948f236f32f2f5b9cd04183a2d74c4699de869 Mon Sep 17 00:00:00 2001 From: Thai Xuan Dang Date: Fri, 19 Jul 2024 16:57:57 +0700 Subject: [PATCH 1/2] feat: return collected fees of a position --- src/periphery/NonfungiblePositionManager.sol | 17 +++++++++++++++++ .../interfaces/INonfungiblePositionManager.sol | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/src/periphery/NonfungiblePositionManager.sol b/src/periphery/NonfungiblePositionManager.sol index 3a84084..3db5dbe 100644 --- a/src/periphery/NonfungiblePositionManager.sol +++ b/src/periphery/NonfungiblePositionManager.sol @@ -51,6 +51,11 @@ contract NonfungiblePositionManager is uint128 tokensOwed1; } + struct CollectedFees { + uint256 token0; + uint256 token1; + } + /// @dev IDs of pools assigned by this contract mapping(address => uint80) private _poolIds; @@ -60,6 +65,9 @@ contract NonfungiblePositionManager is /// @dev The token ID position data mapping(uint256 => Position) private _positions; + /// @dev How many tokens are collected by the position, as of the last colection + mapping(uint256 => CollectedFees) private _collectedFees; + /// @dev The ID of the next token that will be minted. Skips 0 uint176 private _nextId = 1; /// @dev The ID of the next pool that is used for the first time. Skips 0 @@ -114,6 +122,11 @@ contract NonfungiblePositionManager is ); } + function collectedFees(uint256 tokenId) external view override returns (uint256 token0, uint256 token1) { + CollectedFees memory fees = _collectedFees[tokenId]; + return (fees.token0, fees.token1); + } + /// @dev Caches a pool key function cachePoolKey(address pool, PoolAddress.PoolKey memory poolKey) private returns (uint80 poolId) { poolId = _poolIds[pool]; @@ -333,6 +346,10 @@ contract NonfungiblePositionManager is // the actual amounts collected are returned (amount0, amount1) = pool.collect(recipient, position.tickLower, position.tickUpper, amount0Collect, amount1Collect); + CollectedFees storage fees = _collectedFees[params.tokenId]; + fees.token0 += amount0; + fees.token1 += amount1; + // sometimes there will be a few less wei than expected due to rounding down in core, but we just subtract the full amount expected // instead of the actual amount so we can burn the token (position.tokensOwed0, position.tokensOwed1) = (tokensOwed0 - amount0Collect, tokensOwed1 - amount1Collect); diff --git a/src/periphery/interfaces/INonfungiblePositionManager.sol b/src/periphery/interfaces/INonfungiblePositionManager.sol index 2846245..78b072f 100644 --- a/src/periphery/interfaces/INonfungiblePositionManager.sol +++ b/src/periphery/interfaces/INonfungiblePositionManager.sol @@ -76,6 +76,12 @@ interface INonfungiblePositionManager is uint128 tokensOwed1 ); + /// @notice Returns the collected fees for a given token ID + /// @param tokenId The ID of the token for which fees are collected + /// @return token0 The amount of token0 collected + /// @return token1 The amount of token1 collected + function collectedFees(uint256 tokenId) external view returns (uint256 token0, uint256 token1); + struct MintParams { address token0; address token1; From 698368a1f78f463912f93b8fafca4b365e9882ba Mon Sep 17 00:00:00 2001 From: Thai Xuan Dang Date: Fri, 19 Jul 2024 17:00:25 +0700 Subject: [PATCH 2/2] feat: burn the position NFT when no liquidity and no tokens owed in the collect function --- src/periphery/NonfungiblePositionManager.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/periphery/NonfungiblePositionManager.sol b/src/periphery/NonfungiblePositionManager.sol index 3db5dbe..16c8a80 100644 --- a/src/periphery/NonfungiblePositionManager.sol +++ b/src/periphery/NonfungiblePositionManager.sol @@ -354,6 +354,12 @@ contract NonfungiblePositionManager is // instead of the actual amount so we can burn the token (position.tokensOwed0, position.tokensOwed1) = (tokensOwed0 - amount0Collect, tokensOwed1 - amount1Collect); + // if there's no liquidity and no tokens owed, burn the position + if (position.liquidity == 0 && tokensOwed0 == amount0Collect && tokensOwed1 == amount1Collect) { + delete _positions[params.tokenId]; + _burn(params.tokenId); + } + emit Collect(params.tokenId, recipient, amount0Collect, amount1Collect); }