@oldzeppelin/contract 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/.docker/Dockerfile +17 -0
  2. package/.dockerignore +7 -0
  3. package/.env.sample +24 -0
  4. package/.gitlab-ci.yml +51 -0
  5. package/.gitmodules +15 -0
  6. package/.prettierrc +10 -0
  7. package/.solcover.js +4 -0
  8. package/.vscode/settings.json +23 -0
  9. package/LICENSE.MD +51 -0
  10. package/README.md +135 -0
  11. package/contracts/arbitrum/contracts/controllers/UniswapV2ControllerArbitrum.sol +37 -0
  12. package/contracts/arbitrum/contracts/controllers/UniswapV3ControllerArbitrum.sol +46 -0
  13. package/contracts/arbitrum/contracts/oracle/PriceOracleArbitrum.sol +51 -0
  14. package/contracts/main/contracts/controllers/Controller.sol +61 -0
  15. package/contracts/main/contracts/controllers/IController.sol +81 -0
  16. package/contracts/main/contracts/controllers/OneInchV5Controller.sol +332 -0
  17. package/contracts/main/contracts/controllers/UnoswapV2Controller.sol +789 -0
  18. package/contracts/main/contracts/controllers/UnoswapV3Controller.sol +1018 -0
  19. package/contracts/main/contracts/core/CoreWhitelist.sol +192 -0
  20. package/contracts/main/contracts/core/ICoreWhitelist.sol +92 -0
  21. package/contracts/main/contracts/core/IUFarmCore.sol +95 -0
  22. package/contracts/main/contracts/core/UFarmCore.sol +402 -0
  23. package/contracts/main/contracts/fund/FundFactory.sol +59 -0
  24. package/contracts/main/contracts/fund/IUFarmFund.sol +68 -0
  25. package/contracts/main/contracts/fund/UFarmFund.sol +504 -0
  26. package/contracts/main/contracts/oracle/ChainlinkedOracle.sol +71 -0
  27. package/contracts/main/contracts/oracle/IChainlinkAggregator.sol +18 -0
  28. package/contracts/main/contracts/oracle/IPriceOracle.sol +55 -0
  29. package/contracts/main/contracts/oracle/PriceOracle.sol +20 -0
  30. package/contracts/main/contracts/oracle/PriceOracleCore.sol +212 -0
  31. package/contracts/main/contracts/oracle/WstETHOracle.sol +64 -0
  32. package/contracts/main/contracts/permissions/Permissions.sol +54 -0
  33. package/contracts/main/contracts/permissions/UFarmPermissionsModel.sol +136 -0
  34. package/contracts/main/contracts/pool/IPoolAdmin.sol +57 -0
  35. package/contracts/main/contracts/pool/IUFarmPool.sol +304 -0
  36. package/contracts/main/contracts/pool/PerformanceFeeLib.sol +81 -0
  37. package/contracts/main/contracts/pool/PoolAdmin.sol +437 -0
  38. package/contracts/main/contracts/pool/PoolFactory.sol +74 -0
  39. package/contracts/main/contracts/pool/PoolWhitelist.sol +70 -0
  40. package/contracts/main/contracts/pool/UFarmPool.sol +959 -0
  41. package/contracts/main/shared/AssetController.sol +194 -0
  42. package/contracts/main/shared/ECDSARecover.sol +91 -0
  43. package/contracts/main/shared/NZGuard.sol +99 -0
  44. package/contracts/main/shared/SafeOPS.sol +128 -0
  45. package/contracts/main/shared/UFarmCoreLink.sol +83 -0
  46. package/contracts/main/shared/UFarmErrors.sol +16 -0
  47. package/contracts/main/shared/UFarmMathLib.sol +80 -0
  48. package/contracts/main/shared/UFarmOwnableUUPS.sol +59 -0
  49. package/contracts/main/shared/UFarmOwnableUUPSBeacon.sol +34 -0
  50. package/contracts/test/Block.sol +15 -0
  51. package/contracts/test/InchSwapTestProxy.sol +292 -0
  52. package/contracts/test/MockPoolAdmin.sol +8 -0
  53. package/contracts/test/MockUFarmPool.sol +8 -0
  54. package/contracts/test/MockV3wstETHstETHAgg.sol +128 -0
  55. package/contracts/test/MockedWETH9.sol +72 -0
  56. package/contracts/test/OneInchToUFarmTestEnv.sol +466 -0
  57. package/contracts/test/StableCoin.sol +25 -0
  58. package/contracts/test/UFarmMockSequencerUptimeFeed.sol +44 -0
  59. package/contracts/test/UFarmMockV3Aggregator.sol +145 -0
  60. package/contracts/test/UUPSBlock.sol +19 -0
  61. package/contracts/test/ufarmLocal/MulticallV3.sol +220 -0
  62. package/contracts/test/ufarmLocal/controllers/UniswapV2ControllerUFarm.sol +27 -0
  63. package/contracts/test/ufarmLocal/controllers/UniswapV3ControllerUFarm.sol +43 -0
  64. package/deploy/100_test_env_setup.ts +483 -0
  65. package/deploy/20_deploy_uniV2.ts +48 -0
  66. package/deploy/21_create_pairs_uniV2.ts +149 -0
  67. package/deploy/22_deploy_mocked_aggregators.ts +123 -0
  68. package/deploy/22_deploy_wsteth_oracle.ts +65 -0
  69. package/deploy/23_deploy_uniV3.ts +80 -0
  70. package/deploy/24_create_pairs_uniV3.ts +140 -0
  71. package/deploy/25_deploy_oneInch.ts +38 -0
  72. package/deploy/2_deploy_multicall.ts +34 -0
  73. package/deploy/30_deploy_price_oracle.ts +33 -0
  74. package/deploy/3_deploy_lido.ts +114 -0
  75. package/deploy/40_deploy_pool_beacon.ts +19 -0
  76. package/deploy/41_deploy_poolAdmin_beacon.ts +19 -0
  77. package/deploy/42_deploy_ufarmcore.ts +29 -0
  78. package/deploy/43_deploy_fund_beacon.ts +19 -0
  79. package/deploy/4_deploy_tokens.ts +76 -0
  80. package/deploy/50_deploy_poolFactory.ts +35 -0
  81. package/deploy/51_deploy_fundFactory.ts +29 -0
  82. package/deploy/60_init_contracts.ts +101 -0
  83. package/deploy/61_whitelist_tokens.ts +18 -0
  84. package/deploy/70_deploy_uniV2Controller.ts +70 -0
  85. package/deploy/71_deploy_uniV3Controller.ts +67 -0
  86. package/deploy/72_deploy_oneInchController.ts +25 -0
  87. package/deploy/79_whitelist_controllers.ts +125 -0
  88. package/deploy/ufarm/arbitrum/1_prepare_env.ts +82 -0
  89. package/deploy/ufarm/arbitrum/2_deploy_ufarm.ts +178 -0
  90. package/deploy/ufarm/arbitrum-sepolia/1000_prepare_arb_sepolia_env.ts +308 -0
  91. package/deploy-config.json +112 -0
  92. package/deploy-data/oracles.csv +32 -0
  93. package/deploy-data/protocols.csv +10 -0
  94. package/deploy-data/tokens.csv +32 -0
  95. package/docker-compose.yml +67 -0
  96. package/hardhat.config.ts +449 -0
  97. package/index.js +93 -0
  98. package/package.json +82 -0
  99. package/scripts/_deploy_helpers.ts +992 -0
  100. package/scripts/_deploy_network_options.ts +49 -0
  101. package/scripts/activatePool.ts +51 -0
  102. package/scripts/createPool.ts +62 -0
  103. package/scripts/deploy_1inch_proxy.ts +98 -0
  104. package/scripts/pool-data.ts +420 -0
  105. package/scripts/post-deploy.sh +24 -0
  106. package/scripts/setUniV2Rate.ts +252 -0
  107. package/scripts/swapOneInchV5.ts +94 -0
  108. package/scripts/swapUniswapV2.ts +65 -0
  109. package/scripts/swapUniswapV3.ts +71 -0
  110. package/scripts/test.ts +61 -0
  111. package/scripts/typings-copy-artifacts.ts +83 -0
  112. package/tasks/boostPool.ts +39 -0
  113. package/tasks/createFund.ts +44 -0
  114. package/tasks/deboostPool.ts +48 -0
  115. package/tasks/grantUFarmPermissions.ts +57 -0
  116. package/tasks/index.ts +7 -0
  117. package/tasks/mintUSDT.ts +62 -0
  118. package/test/Periphery.test.ts +640 -0
  119. package/test/PriceOracle.test.ts +82 -0
  120. package/test/TestCases.MD +109 -0
  121. package/test/UFarmCore.test.ts +331 -0
  122. package/test/UFarmFund.test.ts +406 -0
  123. package/test/UFarmPool.test.ts +4736 -0
  124. package/test/_fixtures.ts +783 -0
  125. package/test/_helpers.ts +2195 -0
  126. package/test/_oneInchTestData.ts +632 -0
  127. package/tsconfig.json +12 -0
@@ -0,0 +1,1018 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+
3
+ pragma solidity ^0.8.0;
4
+
5
+ /// CONTRACTS
6
+ import {UFarmErrors} from '../../shared/UFarmErrors.sol';
7
+ import {NZGuard} from '../../shared/NZGuard.sol';
8
+ import {Controller, IController} from './Controller.sol';
9
+ import {SafeOPS} from '../../shared/SafeOPS.sol';
10
+
11
+ /// INTERFACES
12
+ import {IUFarmPool} from '../pool/IUFarmPool.sol';
13
+ import {IUFarmCore} from '../core/IUFarmCore.sol';
14
+ import {IPriceOracle} from '../oracle/IPriceOracle.sol';
15
+ import {IERC20} from '../../../test/Uniswap/contracts/v2-core/contracts/interfaces/IERC20.sol';
16
+ import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
17
+ import {IPoolWhitelist} from '../pool/PoolWhitelist.sol';
18
+ import {IController, IERC721Controller, IERC20CommonController} from './IController.sol';
19
+ import {ISwapRouter} from '../../../test/UniswapV3/@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
20
+ import {IUniswapV3Pool} from '../../../test/UniswapV3/@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
21
+ import {ReentrancyGuard} from '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
22
+
23
+ /// LIBRARIES
24
+ import {TickMath} from '../../../test/UniswapV3/@uniswap/v3-core/contracts/libraries/TickMath.sol';
25
+ import {LiquidityAmounts} from '../../../test/UniswapV3/@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol';
26
+ import {FixedPoint96} from '../../../test/UniswapV3/@uniswap/v3-core/contracts/libraries/FixedPoint96.sol';
27
+ import {FixedPoint128} from '@uniswap/v3-core/contracts/libraries/FixedPoint128.sol';
28
+ import {PositionValue, INonfungiblePositionManager} from '../../../test/UniswapV3/@uniswap/v3-periphery/contracts/libraries/PositionValue.sol';
29
+
30
+ interface IUnoswapV3Controller is IERC721Controller {
31
+ function delegatedSwapExactInputSingleHop(bytes calldata _data) external;
32
+ }
33
+
34
+ /**
35
+ * @title UnoswapV3Controller contract
36
+ * @author https://ufarm.digital/
37
+ * @notice Controller contract for UniswapV3-like protocols
38
+ */
39
+ abstract contract UnoswapV3Controller is
40
+ IUnoswapV3Controller,
41
+ Controller,
42
+ NZGuard,
43
+ UFarmErrors,
44
+ ReentrancyGuard
45
+ {
46
+ using PositionValue for INonfungiblePositionManager;
47
+
48
+ struct PoolKey {
49
+ address token0;
50
+ address token1;
51
+ uint24 fee;
52
+ }
53
+
54
+ struct FeeParams {
55
+ address token0;
56
+ address token1;
57
+ uint24 fee;
58
+ int24 tickLower;
59
+ int24 tickUpper;
60
+ uint128 liquidity;
61
+ uint256 positionFeeGrowthInside0LastX128;
62
+ uint256 positionFeeGrowthInside1LastX128;
63
+ uint256 tokensOwed0;
64
+ uint256 tokensOwed1;
65
+ }
66
+
67
+ struct UniV3Pos {
68
+ uint96 nonce;
69
+ address operator;
70
+ address token0;
71
+ address token1;
72
+ uint24 fee;
73
+ int24 tickLower;
74
+ int24 tickUpper;
75
+ uint128 liquidity;
76
+ uint256 feeGrowthInside0LastX128;
77
+ uint256 feeGrowthInside1LastX128;
78
+ uint128 tokensOwed0;
79
+ uint128 tokensOwed1;
80
+ }
81
+
82
+ bytes32 public immutable POOL_INIT_CODE_HASH;
83
+ address public immutable swapRouter;
84
+ address public immutable swapFactory;
85
+ address public immutable priceOracle;
86
+ INonfungiblePositionManager public immutable nfpm;
87
+
88
+ /**
89
+ * @notice Emitted when a new position is minted.
90
+ * @param token0 - spent token0 address
91
+ * @param token1 - spent token1 address
92
+ * @param tokenAddr - recieved position token address
93
+ * @param fee - position fee
94
+ * @param tickLower - position lower tick
95
+ * @param tickUpper - position upper tick
96
+ * @param liquidityMinted - liquidity minted
97
+ * @param tokenId - position token id recieved
98
+ * @param amount0 - amount of token0 spent
99
+ * @param amount1 - amount of token1 spent
100
+ * @param protocol - protocol hashed name
101
+ */
102
+ event PositionMintedUnoV3(
103
+ address indexed token0,
104
+ address indexed token1,
105
+ address indexed tokenAddr,
106
+ uint24 fee,
107
+ int24 tickLower,
108
+ int24 tickUpper,
109
+ uint128 liquidityMinted,
110
+ uint256 tokenId,
111
+ uint256 amount0,
112
+ uint256 amount1,
113
+ bytes32 protocol
114
+ );
115
+
116
+ /**
117
+ * @notice Emitted when a position is burned.
118
+ * @param tokenAddr - position token address burned
119
+ * @param tokenId - position token id burned
120
+ * @param protocol - protocol hashed name
121
+ */
122
+ event PositionBurnedUnoV3(address indexed tokenAddr, uint256 tokenId, bytes32 protocol);
123
+
124
+ /**
125
+ * @notice Emitted when liquidity is increased for a position NFT.
126
+ * @param token0 - token0 spent address
127
+ * @param token1 - token1 spent address
128
+ * @param tokenAddr - position token address
129
+ * @param tokenId - position token id
130
+ * @param liquidityIncreased - liquidity increased to
131
+ * @param amount0Increased - amount of token0 increased to
132
+ * @param amount1Increased - amount of token1 increased to
133
+ * @param protocol - protocol hashed name
134
+ */
135
+ event PositionIncreasedUnoV3(
136
+ address indexed token0,
137
+ address indexed token1,
138
+ address indexed tokenAddr,
139
+ uint256 tokenId,
140
+ uint128 liquidityIncreased,
141
+ uint256 amount0Increased,
142
+ uint256 amount1Increased,
143
+ bytes32 protocol
144
+ );
145
+
146
+ /**
147
+ * @notice Emitted when liquidity is decreased for a position NFT.
148
+ * @dev Does not transfer any tokens, tokens must be collected separately.
149
+ * @param token0 - token0 decreased address
150
+ * @param token1 - token1 decreased address
151
+ * @param tokenAddr - position token address
152
+ * @param tokenId - position token id
153
+ * @param liquidityDecreased - liquidity decreased by
154
+ * @param amount0Decreased - amount of token0 decreased by
155
+ * @param amount1Decreased - amount of token1 decreased by
156
+ * @param protocol - protocol hashed name
157
+ */
158
+ event PositionDecreasedUnoV3(
159
+ address indexed token0,
160
+ address indexed token1,
161
+ address indexed tokenAddr,
162
+ uint256 tokenId,
163
+ uint128 liquidityDecreased,
164
+ uint256 amount0Decreased,
165
+ uint256 amount1Decreased,
166
+ bytes32 protocol
167
+ );
168
+
169
+ /**
170
+ * @notice Emitted when tokens been swapped.
171
+ * @param tokenIn - spent token address
172
+ * @param tokenOut - recieved token address
173
+ * @param amountIn - amount of tokenIn spent
174
+ * @param amountOut - amount of tokenOut recieved
175
+ * @param protocol - protocol hashed name
176
+ */
177
+ event SwapUnoV3(
178
+ address indexed tokenIn,
179
+ address indexed tokenOut,
180
+ uint256 amountIn,
181
+ uint256 amountOut,
182
+ bytes32 protocol
183
+ );
184
+
185
+ /**
186
+ * @notice Emitted when fees been collected.
187
+ * @param tokenAddr - position token address
188
+ * @param token0 - collected token0 address
189
+ * @param token1 - collected token1 address
190
+ * @param target - recipient address, may be user or pool
191
+ * @param tokenId - position token id
192
+ * @param amount0 - amount of token0 collected
193
+ * @param amount1 - amount of token1 collected
194
+ * @param protocol - protocol hashed name
195
+ */
196
+ event FeesCollectedUnoV3(
197
+ address indexed tokenAddr,
198
+ address indexed token0,
199
+ address indexed token1,
200
+ address target,
201
+ uint256 tokenId,
202
+ uint256 amount0,
203
+ uint256 amount1,
204
+ bytes32 protocol
205
+ );
206
+
207
+ error INVALID_RECIPIENT();
208
+ error INVALID_POSITION_OWNER();
209
+ error POSITION_NOT_FOUND();
210
+ error DEADLINE_PASSED();
211
+
212
+ /**
213
+ * @notice UnoswapV3Controller constructor
214
+ * @param _swapRouter - address of the Uniswap SwapRouter
215
+ * @param _swapFactory - address of the UniswapV3 factory
216
+ * @param _nfpm - address of the UniswapV3 NonfungiblePositionManager
217
+ * @param _priceOracle - address of the PriceOracle
218
+ * @param _univ3InitCodeHash - init code hash of the UniswapV3 factory
219
+ */
220
+ constructor(
221
+ address _swapRouter,
222
+ address _swapFactory,
223
+ address _nfpm,
224
+ address _priceOracle,
225
+ bytes32 _univ3InitCodeHash
226
+ )
227
+ nonZeroAddress(_swapRouter)
228
+ nonZeroAddress(_swapFactory)
229
+ nonZeroAddress(_nfpm)
230
+ nonZeroAddress(_priceOracle)
231
+ {
232
+ swapRouter = _swapRouter;
233
+ swapFactory = _swapFactory;
234
+ priceOracle = _priceOracle;
235
+ nfpm = INonfungiblePositionManager(_nfpm);
236
+ POOL_INIT_CODE_HASH = _univ3InitCodeHash;
237
+ }
238
+
239
+ /**
240
+ * @dev Hardcoded TWAP period
241
+ */
242
+ function TWAP_PERIOD() public pure virtual returns (uint32);
243
+
244
+ /**
245
+ * @notice Returns TWAP price for the specified period on the specified pool
246
+ *
247
+ * @param _period - TWAP period
248
+ * @param _pool - pool address
249
+ * @return sqrtPriceX96 - TWAP price
250
+ */
251
+ function getTWAPsqrt(uint32 _period, address _pool) public view returns (uint160 sqrtPriceX96) {
252
+ require(_period > 0, 'Period must be greater than 0');
253
+
254
+ uint32[] memory secondsAgos = new uint32[](2);
255
+ // Start of the period
256
+ secondsAgos[0] = _period;
257
+ // Current timestamp
258
+ secondsAgos[1] = 0;
259
+
260
+ (int56[] memory tickCumulatives, ) = IUniswapV3Pool(_pool).observe(secondsAgos);
261
+
262
+ // Calculate the average tick for the specified period
263
+ int56 tickDifference = tickCumulatives[1] - tickCumulatives[0];
264
+ int24 averageTick = int24(tickDifference / int56(int32(_period)));
265
+
266
+ sqrtPriceX96 = TickMath.getSqrtRatioAtTick(averageTick);
267
+ }
268
+
269
+ /**
270
+ * @notice Executes SwapExactInputSingle on Uniswap V3
271
+ * @param _data - encoded SwapExactInputSingleParams struct
272
+ */
273
+ function delegatedSwapExactInputSingleHop(
274
+ bytes calldata _data
275
+ ) external checkDelegateCall nonReentrant {
276
+ ISwapRouter.ExactInputSingleParams memory params = abi.decode(
277
+ _data,
278
+ (ISwapRouter.ExactInputSingleParams)
279
+ );
280
+
281
+ if (params.recipient != address(this)) revert INVALID_RECIPIENT();
282
+
283
+ (address tokenIn, address tokenOut) = (params.tokenIn, params.tokenOut);
284
+ uint256 amountIn = params.amountIn;
285
+
286
+ if (!IPoolWhitelist(address(this)).isTokenAllowed(tokenOut))
287
+ revert IPoolWhitelist.TokenIsNotAllowed(tokenOut);
288
+
289
+ SafeOPS._forceApprove(tokenIn, swapRouter, amountIn);
290
+
291
+ uint256 amountOut = ISwapRouter(swapRouter).exactInputSingle(params);
292
+
293
+ IUFarmPool(address(this)).addERC20(tokenOut, bytes32(0));
294
+ IUFarmPool(address(this)).removeERC20(tokenIn);
295
+
296
+ emit SwapUnoV3(tokenIn, tokenOut, amountIn, amountOut, PROTOCOL());
297
+ }
298
+
299
+ /**
300
+ * @notice Executes SwapExactInput on Uniswap V3
301
+ * @param _data - encoded SwapExactInputParams struct
302
+ */
303
+ function delegatedSwapExactInputMultiHop(
304
+ bytes calldata _data
305
+ ) external checkDelegateCall nonReentrant {
306
+ // Solidity can't decode dynamic arrays from structs, so we have to do it manually
307
+ (
308
+ address _recipient,
309
+ uint256 _deadline,
310
+ uint256 amountIn,
311
+ uint256 amountOut,
312
+ bytes memory _path
313
+ ) = abi.decode(_data, (address, uint256, uint256, uint256, bytes));
314
+
315
+ IPoolWhitelist pool = IPoolWhitelist(address(this));
316
+
317
+ address tokenIn;
318
+ address tokenOut;
319
+
320
+ assembly {
321
+ tokenIn := mload(add(_path, 20)) // gets first token (0x14 == 20 bytes == address size)
322
+ }
323
+
324
+ uint256 pathLength = _path.length;
325
+
326
+ // Start with the second token in the path
327
+ for (uint256 i = 43; i <= pathLength; i += 23) {
328
+ assembly {
329
+ // Calculate the memory pointer by adding the offset to the start of the _path data
330
+ let ptr := add(_path, i)
331
+
332
+ // Load the value from the calculated memory pointer into tokenOut
333
+ tokenOut := mload(ptr)
334
+ }
335
+
336
+ if (!pool.isTokenAllowed(tokenOut)) revert IPoolWhitelist.TokenIsNotAllowed(tokenOut);
337
+ }
338
+
339
+ // prepare arguments
340
+ ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
341
+ path: _path,
342
+ recipient: address(this),
343
+ deadline: _deadline,
344
+ amountIn: amountIn,
345
+ amountOutMinimum: amountOut
346
+ });
347
+
348
+ // swap with multihop
349
+ SafeOPS._forceApprove(tokenIn, swapRouter, amountIn);
350
+ amountOut = ISwapRouter(swapRouter).exactInput(params);
351
+
352
+ IUFarmPool(address(this)).removeERC20(tokenIn);
353
+ IUFarmPool(address(this)).addERC20(tokenOut, bytes32(0));
354
+
355
+ emit SwapUnoV3(tokenIn, tokenOut, amountIn, amountOut, PROTOCOL());
356
+ }
357
+
358
+ /**
359
+ * @notice Executes minting of a new position on Uniswap V3
360
+ * @param _data - encoded MintParams struct
361
+ */
362
+ function delegateMintNewPosition(bytes calldata _data) external checkDelegateCall nonReentrant {
363
+ INonfungiblePositionManager.MintParams memory mintParams = abi.decode(
364
+ _data,
365
+ (INonfungiblePositionManager.MintParams)
366
+ );
367
+
368
+ (address token0, address token1) = (mintParams.token0, mintParams.token1);
369
+
370
+ if (!IPoolWhitelist(address(this)).isTokenAllowed(token0))
371
+ revert IPoolWhitelist.TokenIsNotAllowed(token0);
372
+ if (!IPoolWhitelist(address(this)).isTokenAllowed(token1))
373
+ revert IPoolWhitelist.TokenIsNotAllowed(token1);
374
+
375
+ if (token0 > token1) {
376
+ (token0, token1) = (token1, token0);
377
+
378
+ (mintParams.token0, mintParams.token1) = (mintParams.token1, mintParams.token0);
379
+
380
+ (
381
+ mintParams.amount0Desired,
382
+ mintParams.amount1Desired,
383
+ mintParams.amount0Min,
384
+ mintParams.amount1Min
385
+ ) = (
386
+ mintParams.amount1Desired,
387
+ mintParams.amount0Desired,
388
+ mintParams.amount1Min,
389
+ mintParams.amount0Min
390
+ );
391
+ }
392
+
393
+ if (mintParams.recipient != address(this)) mintParams.recipient = address(this);
394
+
395
+ address _nfpm = address(nfpm);
396
+
397
+ SafeOPS._forceApprove(token0, _nfpm, mintParams.amount0Desired);
398
+ SafeOPS._forceApprove(token1, _nfpm, mintParams.amount1Desired);
399
+
400
+ (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = nfpm.mint(mintParams);
401
+
402
+ if (amount0 > 0) {
403
+ IUFarmPool(address(this)).removeERC20(token0);
404
+ if (amount0 < mintParams.amount0Desired) {
405
+ IERC20(token0).approve(_nfpm, 0);
406
+ }
407
+ }
408
+ if (amount1 > 0) {
409
+ IUFarmPool(address(this)).removeERC20(token1);
410
+ if (amount1 < mintParams.amount1Desired) {
411
+ IERC20(token1).approve(_nfpm, 0);
412
+ }
413
+ }
414
+
415
+ uint256[] memory ids = new uint256[](1);
416
+ ids[0] = tokenId;
417
+
418
+ IUFarmPool(address(this)).addERC721(_nfpm, ids);
419
+
420
+ emit PositionMintedUnoV3(
421
+ token0,
422
+ token1,
423
+ _nfpm,
424
+ mintParams.fee,
425
+ mintParams.tickLower,
426
+ mintParams.tickUpper,
427
+ liquidity,
428
+ tokenId,
429
+ amount0,
430
+ amount1,
431
+ PROTOCOL()
432
+ );
433
+ }
434
+
435
+ /**
436
+ * @notice Executes burning of a position on Uniswap V3
437
+ * @param _data - encoded BurnParams struct
438
+ */
439
+ function delegateBurnPosition(bytes calldata _data) external checkDelegateCall nonReentrant {
440
+ // struct BurnParams {
441
+ // uint256 tokenId;
442
+ // uint128 liquidity;
443
+ // uint256 amount0Min;
444
+ // uint256 amount1Min;
445
+ // uint256 deadline;
446
+ // }
447
+
448
+ INonfungiblePositionManager.DecreaseLiquidityParams memory burnParams = abi.decode(
449
+ _data,
450
+ (INonfungiblePositionManager.DecreaseLiquidityParams)
451
+ );
452
+
453
+ _checkOwnershipOfPosition(burnParams.tokenId);
454
+
455
+ UniV3Pos memory posInfo = _getPositionData(burnParams.tokenId);
456
+
457
+ burnParams.liquidity = posInfo.liquidity;
458
+
459
+ (address _target, bytes32 _withdrawalHash) = _getTarget();
460
+
461
+ if (burnParams.liquidity > 0) {
462
+ _decreaseLiquidity(posInfo, burnParams, _target, _withdrawalHash, true);
463
+ }
464
+
465
+ nfpm.burn(burnParams.tokenId);
466
+
467
+ uint256[] memory ids = new uint256[](1);
468
+ ids[0] = burnParams.tokenId;
469
+
470
+ IUFarmPool(address(this)).removeERC721(address(nfpm), ids);
471
+
472
+ emit PositionBurnedUnoV3(address(nfpm), burnParams.tokenId, PROTOCOL());
473
+ }
474
+
475
+ /**
476
+ * @notice Executes decreasing of liquidity for a position on Uniswap V3
477
+ * @param _data - encoded DecreaseLiquidityParams struct
478
+ */
479
+ function delegatedDecreaseLiquidity(
480
+ bytes calldata _data
481
+ ) external checkDelegateCall nonReentrant {
482
+ INonfungiblePositionManager.DecreaseLiquidityParams memory decLiqParams = abi.decode(
483
+ _data,
484
+ (INonfungiblePositionManager.DecreaseLiquidityParams)
485
+ );
486
+
487
+ UniV3Pos memory posInfo = _getPositionData(decLiqParams.tokenId);
488
+
489
+ (address _target, bytes32 _withdrawalHash) = _getTarget();
490
+
491
+ _decreaseLiquidity(posInfo, decLiqParams, _target, _withdrawalHash, false);
492
+ }
493
+
494
+ /**
495
+ * @notice Executes increasing of liquidity for a position on Uniswap V3
496
+ * @param _data - encoded IncreaseLiquidityParams struct
497
+ */
498
+ function delegateIncreaseLiquidity(bytes calldata _data) external checkDelegateCall nonReentrant {
499
+ // struct IncreaseLiquidityParams {
500
+ // uint256 tokenId;
501
+ // uint128 liquidity;
502
+ // uint256 amount0Desired;
503
+ // uint256 amount1Desired;
504
+ // uint256 amount0Min;
505
+ // uint256 amount1Min;
506
+ // uint256 deadline;
507
+ // }
508
+
509
+ INonfungiblePositionManager.IncreaseLiquidityParams memory incLiqParams = abi.decode(
510
+ _data,
511
+ (INonfungiblePositionManager.IncreaseLiquidityParams)
512
+ );
513
+
514
+ _checkOwnershipOfPosition(incLiqParams.tokenId);
515
+
516
+ // Fetch position data
517
+ UniV3Pos memory posInfo = _getPositionData(incLiqParams.tokenId);
518
+
519
+ (address token0, address token1) = (posInfo.token0, posInfo.token1);
520
+
521
+ if (!IPoolWhitelist(address(this)).isTokenAllowed(token0))
522
+ revert IPoolWhitelist.TokenIsNotAllowed(posInfo.token0);
523
+ if (!IPoolWhitelist(address(this)).isTokenAllowed(token1))
524
+ revert IPoolWhitelist.TokenIsNotAllowed(token1);
525
+
526
+ address _nfpm = address(nfpm);
527
+
528
+ SafeOPS._forceApprove(token0, _nfpm, incLiqParams.amount0Desired);
529
+ SafeOPS._forceApprove(token1, _nfpm, incLiqParams.amount1Desired);
530
+
531
+ (uint128 liquidityIncreased, uint256 amount0Increased, uint256 amount1Increased) = nfpm
532
+ .increaseLiquidity(incLiqParams);
533
+ if (amount0Increased > 0) {
534
+ if (amount0Increased < incLiqParams.amount0Desired) {
535
+ IERC20(token0).approve(_nfpm, 0);
536
+ }
537
+ IUFarmPool(address(this)).removeERC20(token0);
538
+ }
539
+ if (amount1Increased > 0) {
540
+ if (amount1Increased < incLiqParams.amount1Desired) {
541
+ IERC20(token1).approve(_nfpm, 0);
542
+ }
543
+ IUFarmPool(address(this)).removeERC20(token1);
544
+ }
545
+
546
+ emit PositionIncreasedUnoV3(
547
+ token0,
548
+ token1,
549
+ _nfpm,
550
+ incLiqParams.tokenId,
551
+ liquidityIncreased,
552
+ amount0Increased,
553
+ amount1Increased,
554
+ PROTOCOL()
555
+ );
556
+ }
557
+
558
+ /**
559
+ * @inheritdoc IERC721Controller
560
+ */
561
+ function encodePartialWithdrawalERC721(
562
+ address,
563
+ uint256 _tokenId,
564
+ uint256 _numerator,
565
+ uint256 _denominator
566
+ ) external view override returns (bytes[] memory withdrawalTxs) {
567
+ // Fetch the current liquidity from the position
568
+ UniV3Pos memory posInfo = _getPositionData(_tokenId);
569
+
570
+ uint128 liquidityToRemove = uint128((uint256(posInfo.liquidity) * _numerator) / _denominator);
571
+
572
+ (uint256 feeAmount0, uint256 feeAmount1) = _getPendingFeesFromPos(swapFactory, posInfo);
573
+
574
+ bool positionWithFees = feeAmount0 > 0 || feeAmount1 > 0;
575
+ bool needToDecrease = liquidityToRemove < posInfo.liquidity;
576
+ bool needToBurn = liquidityToRemove == posInfo.liquidity && liquidityToRemove > 0;
577
+
578
+ uint8 txCount = positionWithFees ? 1 : 0;
579
+ txCount += needToDecrease || needToBurn ? 1 : 0;
580
+
581
+ if (txCount == 0) return withdrawalTxs;
582
+
583
+ withdrawalTxs = new bytes[](txCount);
584
+ txCount = 0;
585
+
586
+ if (positionWithFees) {
587
+ // Encode the collect fees function call
588
+ withdrawalTxs[txCount] = abi.encodeCall(
589
+ UnoswapV3Controller.delegatedCollectAllFees,
590
+ abi.encode(
591
+ INonfungiblePositionManager.CollectParams({
592
+ tokenId: _tokenId,
593
+ recipient: address(this),
594
+ amount0Max: type(uint128).max,
595
+ amount1Max: type(uint128).max
596
+ })
597
+ )
598
+ );
599
+ ++txCount;
600
+ }
601
+ // Encode the burn position function call
602
+ INonfungiblePositionManager.DecreaseLiquidityParams
603
+ memory decreaseParams = INonfungiblePositionManager.DecreaseLiquidityParams({
604
+ tokenId: _tokenId,
605
+ liquidity: liquidityToRemove,
606
+ amount0Min: 0,
607
+ amount1Min: 0,
608
+ deadline: block.timestamp
609
+ });
610
+ if (liquidityToRemove > 0) {
611
+ (decreaseParams.amount0Min, decreaseParams.amount1Min) = _getAmountsForLiquidity(
612
+ TWAP_PERIOD(),
613
+ _computePoolAddr(
614
+ swapFactory,
615
+ PoolKey({token0: posInfo.token0, token1: posInfo.token1, fee: posInfo.fee})
616
+ ),
617
+ liquidityToRemove,
618
+ posInfo.tickLower,
619
+ posInfo.tickUpper
620
+ );
621
+ // Add slippage tolerance from TWAP price
622
+ (decreaseParams.amount0Min, decreaseParams.amount1Min) = (
623
+ (decreaseParams.amount0Min * 90) / 100,
624
+ (decreaseParams.amount1Min * 90) / 100
625
+ );
626
+
627
+ if (needToDecrease) {
628
+ withdrawalTxs[txCount] = abi.encodeCall(
629
+ UnoswapV3Controller.delegatedDecreaseLiquidity,
630
+ abi.encode(decreaseParams)
631
+ );
632
+ } else {
633
+ withdrawalTxs[txCount] = abi.encodeCall(
634
+ UnoswapV3Controller.delegateBurnPosition,
635
+ abi.encode(decreaseParams)
636
+ );
637
+ }
638
+ } else {
639
+ withdrawalTxs[txCount] = abi.encodeCall(
640
+ UnoswapV3Controller.delegateBurnPosition,
641
+ abi.encode(decreaseParams)
642
+ );
643
+ }
644
+ }
645
+
646
+ function delegatedCollectAllFees(bytes calldata _data) external {
647
+ // struct CollectAllFeesParams {
648
+ // uint256 tokenId;
649
+ // address recipient;
650
+ // uint128 amount0Max;
651
+ // uint128 amount1Max;
652
+ // }
653
+
654
+ INonfungiblePositionManager.CollectParams memory collectParams = abi.decode(
655
+ _data,
656
+ (INonfungiblePositionManager.CollectParams)
657
+ );
658
+
659
+ _checkOwnershipOfPosition(collectParams.tokenId);
660
+
661
+ UniV3Pos memory posInfo = _getPositionData(collectParams.tokenId);
662
+
663
+ // Only UFarmPool can be the recipient of all fees
664
+ collectParams.recipient = address(this);
665
+
666
+ _collectFees(posInfo, collectParams, bytes32(0));
667
+ }
668
+
669
+ /**
670
+ * @inheritdoc IERC721Controller
671
+ */
672
+ function getCostControlledERC721(
673
+ address _lpAddr,
674
+ uint256[] memory _ids,
675
+ address _valueToken
676
+ ) external view override returns (uint256 cost) {
677
+ UniV3Pos memory posInfo;
678
+
679
+ for (uint256 i; i < _ids.length; ++i) {
680
+ posInfo = _getPositionData(_ids[i]);
681
+
682
+ IUniswapV3Pool pool = IUniswapV3Pool(
683
+ _computePoolAddr(
684
+ swapFactory,
685
+ PoolKey({token0: posInfo.token0, token1: posInfo.token1, fee: posInfo.fee})
686
+ )
687
+ );
688
+
689
+ (uint256 amount0, uint256 amount1) = _getAmountsForLiquidity(
690
+ TWAP_PERIOD(),
691
+ address(pool),
692
+ posInfo.liquidity,
693
+ posInfo.tickLower,
694
+ posInfo.tickUpper
695
+ );
696
+
697
+ (uint256 feeAmount0, uint256 feeAmount1) = _getPendingFeesFromPos(swapFactory, posInfo);
698
+ amount0 += feeAmount0;
699
+ amount1 += feeAmount1;
700
+
701
+ if (amount0 > 0) {
702
+ cost += IPriceOracle(priceOracle).getCostERC20(posInfo.token0, amount0, _valueToken);
703
+ }
704
+
705
+ if (amount1 > 0) {
706
+ cost += IPriceOracle(priceOracle).getCostERC20(posInfo.token1, amount1, _valueToken);
707
+ }
708
+ }
709
+ }
710
+
711
+ /**
712
+ * @notice Computes amounts and fees of token0 and token1 in position
713
+ * @param positionId - positionId to get amounts for
714
+ * @return amount0 - amount of token0 in position
715
+ * @return amount1 - amount of token1 in position
716
+ */
717
+ function getAmountsFromPosition(
718
+ uint128 positionId
719
+ )
720
+ external
721
+ view
722
+ returns (uint256 amount0, uint256 amount1, uint256 feeAmount0, uint256 feeAmount1)
723
+ {
724
+ UniV3Pos memory posInfo = _getPositionData(positionId);
725
+
726
+ address pool = _computePoolAddr(
727
+ swapFactory,
728
+ PoolKey({token0: posInfo.token0, token1: posInfo.token1, fee: posInfo.fee})
729
+ );
730
+
731
+ (amount0, amount1) = _getAmountsForLiquidity(
732
+ TWAP_PERIOD(),
733
+ pool,
734
+ posInfo.liquidity,
735
+ posInfo.tickLower,
736
+ posInfo.tickUpper
737
+ );
738
+
739
+ (feeAmount0, feeAmount1) = _getPendingFeesFromPos(swapFactory, posInfo);
740
+ }
741
+
742
+ /**
743
+ * @notice Computes amounts and fees of token0 and token1 in position
744
+ * @param positionId - positionId to get amounts for
745
+ * @return amount0 - amount of token0 in position
746
+ * @return amount1 - amount of token1 in position
747
+ */
748
+ function getPureAmountsFromPosition(
749
+ uint256 positionId
750
+ ) public view returns (uint256 amount0, uint256 amount1, uint256 feeAmount0, uint256 feeAmount1) {
751
+ UniV3Pos memory positionData = _getPositionData(positionId);
752
+
753
+ IUniswapV3Pool pool = IUniswapV3Pool(
754
+ _computePoolAddr(
755
+ swapFactory,
756
+ PoolKey({token0: positionData.token0, token1: positionData.token1, fee: positionData.fee})
757
+ )
758
+ );
759
+
760
+ (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
761
+
762
+ (amount0, amount1) = nfpm.principal(positionId, sqrtPriceX96);
763
+ (feeAmount0, feeAmount1) = nfpm.fees(positionId);
764
+ }
765
+
766
+ /**
767
+ * @notice Returns liquidity amount for specified amounts of tokens
768
+ * @param token0 - token0 address
769
+ * @param token1 - token1 address
770
+ * @param fee - pool fee
771
+ * @param tickLower - lower tick of position
772
+ * @param tickUpper - upper tick of position
773
+ * @param amount0Desired - amount of token0 desired
774
+ * @param amount1Desired - amount of token1 desired
775
+ */
776
+ function getLiquidityForAmounts(
777
+ address token0,
778
+ address token1,
779
+ uint24 fee,
780
+ int24 tickLower,
781
+ int24 tickUpper,
782
+ uint256 amount0Desired,
783
+ uint256 amount1Desired
784
+ ) external view returns (uint128 liquidity) {
785
+ // Ensure the token addresses are ordered correctly for Uniswap V3
786
+ (address tokenA, address tokenB) = token0 < token1 ? (token0, token1) : (token1, token0);
787
+ (uint256 amountA, uint256 amountB) = token0 < token1
788
+ ? (amount0Desired, amount1Desired)
789
+ : (amount1Desired, amount0Desired);
790
+
791
+ // Calculate the pool address
792
+ IUniswapV3Pool pool = IUniswapV3Pool(
793
+ _computePoolAddr(swapFactory, PoolKey({token0: tokenA, token1: tokenB, fee: fee}))
794
+ );
795
+
796
+ // Fetch the current price of the pool
797
+ (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
798
+
799
+ // Calculate the square root prices for the specified tick range
800
+ uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower);
801
+ uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper);
802
+
803
+ // Compute the liquidity amount
804
+ liquidity = LiquidityAmounts.getLiquidityForAmounts(
805
+ sqrtPriceX96,
806
+ sqrtRatioAX96,
807
+ sqrtRatioBX96,
808
+ amountA,
809
+ amountB
810
+ );
811
+ }
812
+
813
+ function _getPositionData(uint256 id) internal view returns (UniV3Pos memory posInfo) {
814
+ (bool success, bytes memory result) = address(nfpm).staticcall(
815
+ abi.encodeCall(nfpm.positions, (id))
816
+ );
817
+
818
+ if (!success) revert POSITION_NOT_FOUND();
819
+
820
+ posInfo = abi.decode(result, (UniV3Pos));
821
+ }
822
+
823
+ function _delegatedThisController() private view returns (address payable thisController) {
824
+ thisController = payable(
825
+ IUFarmCore(IUFarmPool(address(this)).ufarmCore()).controllers(PROTOCOL())
826
+ );
827
+ if (thisController == address(0)) revert FETCHING_CONTROLLER_FAILED();
828
+ }
829
+
830
+ function _getAmountsForLiquidity(
831
+ uint32 _TWAP_PERIOD,
832
+ address _pool,
833
+ uint128 _liquidity,
834
+ int24 _tickLower,
835
+ int24 _tickUpper
836
+ ) public view returns (uint256 amount0, uint256 amount1) {
837
+ uint160 sqrtPriceX96 = getTWAPsqrt(_TWAP_PERIOD, _pool);
838
+
839
+ uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(_tickLower);
840
+ uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(_tickUpper);
841
+
842
+ (amount0, amount1) = LiquidityAmounts.getAmountsForLiquidity(
843
+ sqrtPriceX96,
844
+ sqrtRatioAX96,
845
+ sqrtRatioBX96,
846
+ _liquidity
847
+ );
848
+ }
849
+
850
+ function _decreaseLiquidity(
851
+ UniV3Pos memory posInfo,
852
+ INonfungiblePositionManager.DecreaseLiquidityParams memory decLiqParams,
853
+ address _target,
854
+ bytes32 _withdrawalHash,
855
+ bool addAllFees
856
+ ) internal returns (uint256 amount0Decreased, uint256 amount1Decreased) {
857
+ (amount0Decreased, amount1Decreased) = nfpm.decreaseLiquidity(decLiqParams);
858
+
859
+ emit PositionDecreasedUnoV3(
860
+ posInfo.token0,
861
+ posInfo.token1,
862
+ address(nfpm),
863
+ decLiqParams.tokenId,
864
+ decLiqParams.liquidity,
865
+ amount0Decreased,
866
+ amount1Decreased,
867
+ PROTOCOL()
868
+ );
869
+
870
+ (amount0Decreased, amount1Decreased) = _collectFees(
871
+ posInfo,
872
+ INonfungiblePositionManager.CollectParams({
873
+ tokenId: decLiqParams.tokenId,
874
+ recipient: _target,
875
+ amount0Max: addAllFees ? type(uint128).max : uint128(amount0Decreased),
876
+ amount1Max: addAllFees ? type(uint128).max : uint128(amount1Decreased)
877
+ }),
878
+ _withdrawalHash
879
+ );
880
+ }
881
+
882
+ function _collectFees(
883
+ UniV3Pos memory posInfo,
884
+ INonfungiblePositionManager.CollectParams memory collectParams,
885
+ bytes32 _withdrawalHash
886
+ ) internal returns (uint256 amount0, uint256 amount1) {
887
+ (amount0, amount1) = nfpm.collect(collectParams);
888
+
889
+ (address token0, address token1) = (posInfo.token0, posInfo.token1);
890
+ address target = collectParams.recipient;
891
+
892
+ if (target == address(this)) {
893
+ if (amount0 > 0) {
894
+ IUFarmPool(address(this)).addERC20(token0, bytes32(0));
895
+ }
896
+
897
+ if (amount1 > 0) {
898
+ IUFarmPool(address(this)).addERC20(token1, bytes32(0));
899
+ }
900
+ } else {
901
+ emit IUFarmPool.Withdraw(target, token0, amount0, _withdrawalHash);
902
+ emit IUFarmPool.Withdraw(target, token1, amount1, _withdrawalHash);
903
+ }
904
+
905
+ emit FeesCollectedUnoV3(
906
+ address(nfpm),
907
+ token0,
908
+ token1,
909
+ target,
910
+ collectParams.tokenId,
911
+ amount0,
912
+ amount1,
913
+ PROTOCOL()
914
+ );
915
+ }
916
+
917
+ /**
918
+ * @dev Converts a tick value to a price.
919
+ * @param _tick The tick value to convert.
920
+ * @return price The corresponding price.
921
+ */
922
+ function tickToPrice(int24 _tick) public pure returns (uint256 price) {
923
+ uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(_tick);
924
+ price = (uint256(sqrtPriceX96) * uint256(sqrtPriceX96)) >> (2 * FixedPoint96.RESOLUTION);
925
+ }
926
+
927
+ function _checkOwnershipOfPosition(uint256 tokenId) internal view {
928
+ try nfpm.ownerOf(tokenId) returns (address owner) {
929
+ if (owner != address(this)) revert INVALID_POSITION_OWNER();
930
+ } catch {
931
+ revert POSITION_NOT_FOUND();
932
+ }
933
+ }
934
+
935
+ function _computePoolAddr(
936
+ address factory,
937
+ PoolKey memory key
938
+ ) internal view returns (address pool) {
939
+ require(key.token0 < key.token1);
940
+ pool = address(
941
+ uint160(
942
+ uint256(
943
+ keccak256(
944
+ abi.encodePacked(
945
+ hex'ff',
946
+ factory,
947
+ keccak256(abi.encode(key.token0, key.token1, key.fee)),
948
+ POOL_INIT_CODE_HASH
949
+ )
950
+ )
951
+ )
952
+ )
953
+ );
954
+ }
955
+
956
+ function _getPendingFeesFromPos(
957
+ address uniswapV3Factory,
958
+ UniV3Pos memory feeParams
959
+ ) public view returns (uint256 amount0, uint256 amount1) {
960
+ (
961
+ uint256 poolFeeGrowthInside0LastX128,
962
+ uint256 poolFeeGrowthInside1LastX128
963
+ ) = _getFeeGrowthInside(
964
+ IUniswapV3Pool(
965
+ _computePoolAddr(
966
+ uniswapV3Factory,
967
+ PoolKey({token0: feeParams.token0, token1: feeParams.token1, fee: feeParams.fee})
968
+ )
969
+ ),
970
+ feeParams.tickLower,
971
+ feeParams.tickUpper
972
+ );
973
+
974
+ amount0 =
975
+ ((poolFeeGrowthInside0LastX128 - feeParams.feeGrowthInside0LastX128) * feeParams.liquidity) /
976
+ FixedPoint128.Q128 +
977
+ feeParams.tokensOwed0;
978
+
979
+ amount1 =
980
+ ((poolFeeGrowthInside1LastX128 - feeParams.feeGrowthInside1LastX128) * feeParams.liquidity) /
981
+ FixedPoint128.Q128 +
982
+ feeParams.tokensOwed1;
983
+ }
984
+
985
+ function _getFeeGrowthInside(
986
+ IUniswapV3Pool pool,
987
+ int24 tickLower,
988
+ int24 tickUpper
989
+ ) private view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
990
+ (, int24 tickCurrent, , , , , ) = pool.slot0();
991
+ (, , uint256 lowerFeeGrowthOutside0X128, uint256 lowerFeeGrowthOutside1X128, , , , ) = pool
992
+ .ticks(tickLower);
993
+ (, , uint256 upperFeeGrowthOutside0X128, uint256 upperFeeGrowthOutside1X128, , , , ) = pool
994
+ .ticks(tickUpper);
995
+
996
+ if (tickCurrent < tickLower) {
997
+ feeGrowthInside0X128 = lowerFeeGrowthOutside0X128 - upperFeeGrowthOutside0X128;
998
+ feeGrowthInside1X128 = lowerFeeGrowthOutside1X128 - upperFeeGrowthOutside1X128;
999
+ } else if (tickCurrent < tickUpper) {
1000
+ (uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128) = (
1001
+ pool.feeGrowthGlobal0X128(),
1002
+ pool.feeGrowthGlobal1X128()
1003
+ );
1004
+
1005
+ feeGrowthInside0X128 =
1006
+ feeGrowthGlobal0X128 -
1007
+ lowerFeeGrowthOutside0X128 -
1008
+ upperFeeGrowthOutside0X128;
1009
+ feeGrowthInside1X128 =
1010
+ feeGrowthGlobal1X128 -
1011
+ lowerFeeGrowthOutside1X128 -
1012
+ upperFeeGrowthOutside1X128;
1013
+ } else {
1014
+ feeGrowthInside0X128 = upperFeeGrowthOutside0X128 - lowerFeeGrowthOutside0X128;
1015
+ feeGrowthInside1X128 = upperFeeGrowthOutside1X128 - lowerFeeGrowthOutside1X128;
1016
+ }
1017
+ }
1018
+ }