@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.
- package/.docker/Dockerfile +17 -0
- package/.dockerignore +7 -0
- package/.env.sample +24 -0
- package/.gitlab-ci.yml +51 -0
- package/.gitmodules +15 -0
- package/.prettierrc +10 -0
- package/.solcover.js +4 -0
- package/.vscode/settings.json +23 -0
- package/LICENSE.MD +51 -0
- package/README.md +135 -0
- package/contracts/arbitrum/contracts/controllers/UniswapV2ControllerArbitrum.sol +37 -0
- package/contracts/arbitrum/contracts/controllers/UniswapV3ControllerArbitrum.sol +46 -0
- package/contracts/arbitrum/contracts/oracle/PriceOracleArbitrum.sol +51 -0
- package/contracts/main/contracts/controllers/Controller.sol +61 -0
- package/contracts/main/contracts/controllers/IController.sol +81 -0
- package/contracts/main/contracts/controllers/OneInchV5Controller.sol +332 -0
- package/contracts/main/contracts/controllers/UnoswapV2Controller.sol +789 -0
- package/contracts/main/contracts/controllers/UnoswapV3Controller.sol +1018 -0
- package/contracts/main/contracts/core/CoreWhitelist.sol +192 -0
- package/contracts/main/contracts/core/ICoreWhitelist.sol +92 -0
- package/contracts/main/contracts/core/IUFarmCore.sol +95 -0
- package/contracts/main/contracts/core/UFarmCore.sol +402 -0
- package/contracts/main/contracts/fund/FundFactory.sol +59 -0
- package/contracts/main/contracts/fund/IUFarmFund.sol +68 -0
- package/contracts/main/contracts/fund/UFarmFund.sol +504 -0
- package/contracts/main/contracts/oracle/ChainlinkedOracle.sol +71 -0
- package/contracts/main/contracts/oracle/IChainlinkAggregator.sol +18 -0
- package/contracts/main/contracts/oracle/IPriceOracle.sol +55 -0
- package/contracts/main/contracts/oracle/PriceOracle.sol +20 -0
- package/contracts/main/contracts/oracle/PriceOracleCore.sol +212 -0
- package/contracts/main/contracts/oracle/WstETHOracle.sol +64 -0
- package/contracts/main/contracts/permissions/Permissions.sol +54 -0
- package/contracts/main/contracts/permissions/UFarmPermissionsModel.sol +136 -0
- package/contracts/main/contracts/pool/IPoolAdmin.sol +57 -0
- package/contracts/main/contracts/pool/IUFarmPool.sol +304 -0
- package/contracts/main/contracts/pool/PerformanceFeeLib.sol +81 -0
- package/contracts/main/contracts/pool/PoolAdmin.sol +437 -0
- package/contracts/main/contracts/pool/PoolFactory.sol +74 -0
- package/contracts/main/contracts/pool/PoolWhitelist.sol +70 -0
- package/contracts/main/contracts/pool/UFarmPool.sol +959 -0
- package/contracts/main/shared/AssetController.sol +194 -0
- package/contracts/main/shared/ECDSARecover.sol +91 -0
- package/contracts/main/shared/NZGuard.sol +99 -0
- package/contracts/main/shared/SafeOPS.sol +128 -0
- package/contracts/main/shared/UFarmCoreLink.sol +83 -0
- package/contracts/main/shared/UFarmErrors.sol +16 -0
- package/contracts/main/shared/UFarmMathLib.sol +80 -0
- package/contracts/main/shared/UFarmOwnableUUPS.sol +59 -0
- package/contracts/main/shared/UFarmOwnableUUPSBeacon.sol +34 -0
- package/contracts/test/Block.sol +15 -0
- package/contracts/test/InchSwapTestProxy.sol +292 -0
- package/contracts/test/MockPoolAdmin.sol +8 -0
- package/contracts/test/MockUFarmPool.sol +8 -0
- package/contracts/test/MockV3wstETHstETHAgg.sol +128 -0
- package/contracts/test/MockedWETH9.sol +72 -0
- package/contracts/test/OneInchToUFarmTestEnv.sol +466 -0
- package/contracts/test/StableCoin.sol +25 -0
- package/contracts/test/UFarmMockSequencerUptimeFeed.sol +44 -0
- package/contracts/test/UFarmMockV3Aggregator.sol +145 -0
- package/contracts/test/UUPSBlock.sol +19 -0
- package/contracts/test/ufarmLocal/MulticallV3.sol +220 -0
- package/contracts/test/ufarmLocal/controllers/UniswapV2ControllerUFarm.sol +27 -0
- package/contracts/test/ufarmLocal/controllers/UniswapV3ControllerUFarm.sol +43 -0
- package/deploy/100_test_env_setup.ts +483 -0
- package/deploy/20_deploy_uniV2.ts +48 -0
- package/deploy/21_create_pairs_uniV2.ts +149 -0
- package/deploy/22_deploy_mocked_aggregators.ts +123 -0
- package/deploy/22_deploy_wsteth_oracle.ts +65 -0
- package/deploy/23_deploy_uniV3.ts +80 -0
- package/deploy/24_create_pairs_uniV3.ts +140 -0
- package/deploy/25_deploy_oneInch.ts +38 -0
- package/deploy/2_deploy_multicall.ts +34 -0
- package/deploy/30_deploy_price_oracle.ts +33 -0
- package/deploy/3_deploy_lido.ts +114 -0
- package/deploy/40_deploy_pool_beacon.ts +19 -0
- package/deploy/41_deploy_poolAdmin_beacon.ts +19 -0
- package/deploy/42_deploy_ufarmcore.ts +29 -0
- package/deploy/43_deploy_fund_beacon.ts +19 -0
- package/deploy/4_deploy_tokens.ts +76 -0
- package/deploy/50_deploy_poolFactory.ts +35 -0
- package/deploy/51_deploy_fundFactory.ts +29 -0
- package/deploy/60_init_contracts.ts +101 -0
- package/deploy/61_whitelist_tokens.ts +18 -0
- package/deploy/70_deploy_uniV2Controller.ts +70 -0
- package/deploy/71_deploy_uniV3Controller.ts +67 -0
- package/deploy/72_deploy_oneInchController.ts +25 -0
- package/deploy/79_whitelist_controllers.ts +125 -0
- package/deploy/ufarm/arbitrum/1_prepare_env.ts +82 -0
- package/deploy/ufarm/arbitrum/2_deploy_ufarm.ts +178 -0
- package/deploy/ufarm/arbitrum-sepolia/1000_prepare_arb_sepolia_env.ts +308 -0
- package/deploy-config.json +112 -0
- package/deploy-data/oracles.csv +32 -0
- package/deploy-data/protocols.csv +10 -0
- package/deploy-data/tokens.csv +32 -0
- package/docker-compose.yml +67 -0
- package/hardhat.config.ts +449 -0
- package/index.js +93 -0
- package/package.json +82 -0
- package/scripts/_deploy_helpers.ts +992 -0
- package/scripts/_deploy_network_options.ts +49 -0
- package/scripts/activatePool.ts +51 -0
- package/scripts/createPool.ts +62 -0
- package/scripts/deploy_1inch_proxy.ts +98 -0
- package/scripts/pool-data.ts +420 -0
- package/scripts/post-deploy.sh +24 -0
- package/scripts/setUniV2Rate.ts +252 -0
- package/scripts/swapOneInchV5.ts +94 -0
- package/scripts/swapUniswapV2.ts +65 -0
- package/scripts/swapUniswapV3.ts +71 -0
- package/scripts/test.ts +61 -0
- package/scripts/typings-copy-artifacts.ts +83 -0
- package/tasks/boostPool.ts +39 -0
- package/tasks/createFund.ts +44 -0
- package/tasks/deboostPool.ts +48 -0
- package/tasks/grantUFarmPermissions.ts +57 -0
- package/tasks/index.ts +7 -0
- package/tasks/mintUSDT.ts +62 -0
- package/test/Periphery.test.ts +640 -0
- package/test/PriceOracle.test.ts +82 -0
- package/test/TestCases.MD +109 -0
- package/test/UFarmCore.test.ts +331 -0
- package/test/UFarmFund.test.ts +406 -0
- package/test/UFarmPool.test.ts +4736 -0
- package/test/_fixtures.ts +783 -0
- package/test/_helpers.ts +2195 -0
- package/test/_oneInchTestData.ts +632 -0
- 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
|
+
}
|