@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,789 @@
|
|
1
|
+
// SPDX-License-Identifier: GPL-2.0-or-later
|
2
|
+
|
3
|
+
pragma solidity ^0.8.0;
|
4
|
+
|
5
|
+
/// CONTRACTS
|
6
|
+
import {Controller} from './Controller.sol';
|
7
|
+
import {NZGuard} from '../../shared/NZGuard.sol';
|
8
|
+
import {ReentrancyGuard} from '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
|
9
|
+
import {UFarmErrors} from '../../shared/UFarmErrors.sol';
|
10
|
+
|
11
|
+
/// INTERFACES
|
12
|
+
import {IPriceOracle} from '../oracle/IPriceOracle.sol';
|
13
|
+
import {IUFarmPool} from '../pool/IUFarmPool.sol';
|
14
|
+
import {IUFarmCore} from '../core/IUFarmCore.sol';
|
15
|
+
import {IUniswapV2Router02} from '../../../test/Uniswap/contracts/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol';
|
16
|
+
import {IUniswapV2Factory} from '../../../test/Uniswap/contracts/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
|
17
|
+
import {IUniswapV2Pair} from '../../../test/Uniswap/contracts/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
|
18
|
+
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
19
|
+
import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
|
20
|
+
import {IPoolWhitelist} from '../pool/PoolWhitelist.sol';
|
21
|
+
import {IController, IERC20CommonController, IERC20SynthController} from './IController.sol';
|
22
|
+
|
23
|
+
/// LIBRARIES
|
24
|
+
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
|
25
|
+
import {UFarmMathLib} from '../../shared/UFarmMathLib.sol';
|
26
|
+
|
27
|
+
interface IUnoswapV2Controller is IERC20CommonController, IERC20SynthController {
|
28
|
+
/**
|
29
|
+
* @notice Returns exact amount of tokens that will be spent to provide maximum lp tokens
|
30
|
+
* @param tokenA - address of the first token in the pair
|
31
|
+
* @param tokenB - address of the second token in the pair
|
32
|
+
* @param amountADesired - desired amount of tokenA to be provided
|
33
|
+
* @param amountBDesired - desired amount of tokenB to be provided
|
34
|
+
* @param amountAMin - minimum amount of tokenA to be spent
|
35
|
+
* @param amountBMin - minimum amount of tokenB to be spent
|
36
|
+
* @param deadline - deadline for the operation in UNIX time
|
37
|
+
* @return amountA - amount of tokenA to be spent
|
38
|
+
* @return amountB - amount of tokenB to be spent
|
39
|
+
* @return pair - address of the pair that was used for adding liquidity
|
40
|
+
*/
|
41
|
+
function quoteExactLiquidityAmounts(
|
42
|
+
address tokenA,
|
43
|
+
address tokenB,
|
44
|
+
uint256 amountADesired,
|
45
|
+
uint256 amountBDesired,
|
46
|
+
uint256 amountAMin,
|
47
|
+
uint256 amountBMin,
|
48
|
+
uint256 deadline
|
49
|
+
) external view returns (uint256 amountA, uint256 amountB, address pair);
|
50
|
+
|
51
|
+
function PROTOCOL() external view returns (bytes32);
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* @title UnoswapV2Controller contract
|
56
|
+
* @author https://ufarm.digital/
|
57
|
+
* @notice Manages all swaps and liquidity operations for UniswapV2-like protocols
|
58
|
+
*/
|
59
|
+
abstract contract UnoswapV2Controller is
|
60
|
+
IUnoswapV2Controller,
|
61
|
+
Controller,
|
62
|
+
NZGuard,
|
63
|
+
UFarmErrors,
|
64
|
+
ReentrancyGuard
|
65
|
+
{
|
66
|
+
using SafeERC20 for IERC20;
|
67
|
+
bytes32 public immutable FACTORY_INIT_CODE_HASH; // Needs to be hardcoded in the contract
|
68
|
+
|
69
|
+
IUniswapV2Factory public immutable factory;
|
70
|
+
IUniswapV2Router02 public immutable router;
|
71
|
+
address public immutable priceOracle;
|
72
|
+
|
73
|
+
/**
|
74
|
+
* @notice Emitted when swap is executed
|
75
|
+
* @param tokenIn - token to swap from
|
76
|
+
* @param tokenOut - token to swap to
|
77
|
+
* @param amountIn - amount of tokens to swap
|
78
|
+
* @param amountOut - minimum amount of tokens to receive
|
79
|
+
* @param protocol - protocol hashed name
|
80
|
+
*/
|
81
|
+
event SwapUnoV2(
|
82
|
+
address indexed tokenIn,
|
83
|
+
address indexed tokenOut,
|
84
|
+
uint256 amountIn,
|
85
|
+
uint256 amountOut,
|
86
|
+
bytes32 protocol
|
87
|
+
);
|
88
|
+
|
89
|
+
/**
|
90
|
+
* @notice Emitted when liquidity is added to the pool
|
91
|
+
* @param tokenA - address of the first token spent for liquidity
|
92
|
+
* @param tokenB - address of the second token spent for liquidity
|
93
|
+
* @param amountA - amount of tokenA spent for liquidity
|
94
|
+
* @param amountB - amount of tokenB spent for liquidity
|
95
|
+
* @param liquidity - amount of liquidity tokens received
|
96
|
+
* @param pair - liquidity pair address
|
97
|
+
* @param protocol - protocol hashed name
|
98
|
+
*/
|
99
|
+
event LiquidityAddedUnoV2(
|
100
|
+
address indexed tokenA,
|
101
|
+
address indexed tokenB,
|
102
|
+
uint256 amountA,
|
103
|
+
uint256 amountB,
|
104
|
+
uint256 liquidity,
|
105
|
+
address indexed pair,
|
106
|
+
bytes32 protocol
|
107
|
+
);
|
108
|
+
|
109
|
+
/**
|
110
|
+
* @notice Emitted when liquidity is removed from the pool
|
111
|
+
* @param tokenA - address of the first token received for liquidity
|
112
|
+
* @param tokenB - address of the second token received for liquidity
|
113
|
+
* @param amountA - amount of tokenA received for liquidity
|
114
|
+
* @param amountB - amount of tokenB received for liquidity
|
115
|
+
* @param liquidity - amount of liquidity tokens spent
|
116
|
+
* @param pair - liquidity pair address
|
117
|
+
* @param target - address of the target for the removed liquidity
|
118
|
+
* @param protocol - protocol hashed name
|
119
|
+
*/
|
120
|
+
event LiquidityRemovedUnoV2(
|
121
|
+
address indexed tokenA,
|
122
|
+
address indexed tokenB,
|
123
|
+
uint256 amountA,
|
124
|
+
uint256 amountB,
|
125
|
+
uint256 liquidity,
|
126
|
+
address indexed pair,
|
127
|
+
address target,
|
128
|
+
bytes32 protocol
|
129
|
+
);
|
130
|
+
|
131
|
+
error INSUFFICIENT_A_AMOUNT();
|
132
|
+
error INSUFFICIENT_B_AMOUNT();
|
133
|
+
error DEADLINE_PASSED();
|
134
|
+
error IDENTICAL_ADDRESSES();
|
135
|
+
error INSUFFICIENT_AMOUNT();
|
136
|
+
error INSUFFICIENT_LIQUIDITY();
|
137
|
+
error INSUFFICIENT_INPUT_AMOUNT();
|
138
|
+
error INSUFFICIENT_OUTPUT_AMOUNT();
|
139
|
+
error INVALID_PATH();
|
140
|
+
error INVALID_PAIR();
|
141
|
+
|
142
|
+
/**
|
143
|
+
* @notice UnoswapV2Controller constructor
|
144
|
+
* @param _factory - address of the UnoswapV2 factory
|
145
|
+
* @param _router - address of the UnoswapV2 router
|
146
|
+
* @param _priceOracle - address of the PriceOracle
|
147
|
+
* @param _factoryInitCodeHash - init code hash of the UnoswapV2 factory
|
148
|
+
*/
|
149
|
+
constructor(
|
150
|
+
address _factory,
|
151
|
+
address _router,
|
152
|
+
address _priceOracle,
|
153
|
+
bytes32 _factoryInitCodeHash
|
154
|
+
)
|
155
|
+
nonZeroAddress(_factory)
|
156
|
+
nonZeroAddress(_router)
|
157
|
+
nonZeroAddress(_priceOracle)
|
158
|
+
nonZeroBytes32(_factoryInitCodeHash)
|
159
|
+
{
|
160
|
+
factory = IUniswapV2Factory(_factory);
|
161
|
+
router = IUniswapV2Router02(_router);
|
162
|
+
priceOracle = _priceOracle;
|
163
|
+
FACTORY_INIT_CODE_HASH = _factoryInitCodeHash;
|
164
|
+
}
|
165
|
+
|
166
|
+
/**
|
167
|
+
* @inheritdoc IController
|
168
|
+
*/
|
169
|
+
function PROTOCOL()
|
170
|
+
public
|
171
|
+
pure
|
172
|
+
virtual
|
173
|
+
override(Controller, IUnoswapV2Controller)
|
174
|
+
returns (bytes32);
|
175
|
+
|
176
|
+
/**
|
177
|
+
* @notice Returns amount of tokens that will be received for given amountIn and path
|
178
|
+
* @param amountIn - amount of tokens to swap
|
179
|
+
* @param path - path of tokens to swap
|
180
|
+
*/
|
181
|
+
function getAmountOut(uint256 amountIn, address[] memory path) public view returns (uint256) {
|
182
|
+
if (path.length < 2) revert INVALID_PATH();
|
183
|
+
|
184
|
+
// Using router:
|
185
|
+
uint256[] memory amounts = router.getAmountsOut(amountIn, path);
|
186
|
+
return amounts[path.length - 1];
|
187
|
+
}
|
188
|
+
|
189
|
+
/**
|
190
|
+
* @notice Returns amount of tokens that will be spent for given amountOut and path
|
191
|
+
* @param amountOut - amount of tokens to receive
|
192
|
+
* @param path - path of tokens to swap
|
193
|
+
*/
|
194
|
+
function getAmountIn(uint256 amountOut, address[] memory path) public view returns (uint256) {
|
195
|
+
if (path.length < 2) revert INVALID_PATH();
|
196
|
+
|
197
|
+
// Using router:
|
198
|
+
uint256[] memory amounts = router.getAmountsIn(amountOut, path);
|
199
|
+
return amounts[0];
|
200
|
+
}
|
201
|
+
|
202
|
+
struct UniV2SwapExactTokensForTokensArgs {
|
203
|
+
uint256 amountIn;
|
204
|
+
uint256 amountOutMin;
|
205
|
+
uint256 deadline;
|
206
|
+
}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* @notice Swaps pool assets via delegate call using UniswapV2-like protocol
|
210
|
+
* @param _data - encoded data for protocol controller
|
211
|
+
*/
|
212
|
+
function delegateSwapExactTokensForTokens(
|
213
|
+
bytes calldata _data
|
214
|
+
) external checkDelegateCall nonReentrant {
|
215
|
+
// (address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut)
|
216
|
+
(UniV2SwapExactTokensForTokensArgs memory swArgs, address[] memory path) = abi.decode(
|
217
|
+
_data,
|
218
|
+
(UniV2SwapExactTokensForTokensArgs, address[])
|
219
|
+
);
|
220
|
+
|
221
|
+
_checkDeadline(swArgs.deadline);
|
222
|
+
|
223
|
+
// UnoswapV2Controller thisController = _delegatedThisController();
|
224
|
+
|
225
|
+
(uint256 pathLength, uint256 amountIn) = (path.length, swArgs.amountIn);
|
226
|
+
|
227
|
+
if (pathLength < 2) revert INVALID_PATH();
|
228
|
+
|
229
|
+
// FIRST SWAP
|
230
|
+
(address tokenIn, address tokenOut) = (path[0], path[1]);
|
231
|
+
|
232
|
+
IPoolWhitelist pool = IPoolWhitelist(address(this));
|
233
|
+
|
234
|
+
if (!pool.isTokenAllowed(tokenOut)) revert IPoolWhitelist.TokenIsNotAllowed(tokenOut);
|
235
|
+
|
236
|
+
bool reversed = tokenIn > tokenOut;
|
237
|
+
|
238
|
+
(address tokenA, address tokenB) = (
|
239
|
+
reversed ? tokenOut : tokenIn,
|
240
|
+
reversed ? tokenIn : tokenOut
|
241
|
+
);
|
242
|
+
|
243
|
+
IUniswapV2Pair pair = IUniswapV2Pair(pairFor(tokenA, tokenB));
|
244
|
+
|
245
|
+
IERC20(tokenIn).safeTransfer(address(pair), amountIn);
|
246
|
+
|
247
|
+
address swapTarget = pathLength > 2 ? pairFor(tokenOut, path[2]) : address(this);
|
248
|
+
|
249
|
+
(uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
|
250
|
+
|
251
|
+
uint256 amountOut = getAmountOutReserves(
|
252
|
+
amountIn,
|
253
|
+
reversed ? reserve1 : reserve0,
|
254
|
+
reversed ? reserve0 : reserve1
|
255
|
+
);
|
256
|
+
|
257
|
+
_checkOutAmount(amountOut, 1);
|
258
|
+
|
259
|
+
pair.swap(reversed ? amountOut : 0, reversed ? 0 : amountOut, swapTarget, new bytes(0));
|
260
|
+
// next swap if pathLength > 2
|
261
|
+
for (uint256 i; i < pathLength - 2; ++i) {
|
262
|
+
(tokenIn, tokenOut) = (path[i + 1], path[i + 2]);
|
263
|
+
|
264
|
+
if (!pool.isTokenAllowed(tokenOut)) revert IPoolWhitelist.TokenIsNotAllowed(tokenOut);
|
265
|
+
|
266
|
+
reversed = tokenIn > tokenOut;
|
267
|
+
(tokenA, tokenB) = (reversed ? tokenOut : tokenIn, reversed ? tokenIn : tokenOut);
|
268
|
+
|
269
|
+
// pair addres was destination of previous swap
|
270
|
+
pair = IUniswapV2Pair(swapTarget);
|
271
|
+
|
272
|
+
// if there is next swap, then transfer destionation is the next pair
|
273
|
+
swapTarget = i < pathLength - 3 ? pairFor(tokenOut, path[i + 3]) : address(this);
|
274
|
+
|
275
|
+
(reserve0, reserve1, ) = pair.getReserves();
|
276
|
+
|
277
|
+
amountOut = getAmountOutReserves(
|
278
|
+
amountOut,
|
279
|
+
reversed ? reserve1 : reserve0,
|
280
|
+
reversed ? reserve0 : reserve1
|
281
|
+
);
|
282
|
+
|
283
|
+
_checkOutAmount(amountOut, 1);
|
284
|
+
|
285
|
+
pair.swap(reversed ? amountOut : 0, reversed ? 0 : amountOut, swapTarget, new bytes(0));
|
286
|
+
}
|
287
|
+
|
288
|
+
_checkOutAmount(amountOut, swArgs.amountOutMin);
|
289
|
+
|
290
|
+
tokenIn = path[0];
|
291
|
+
|
292
|
+
IUFarmPool(address(this)).removeERC20(tokenIn);
|
293
|
+
IUFarmPool(address(this)).addERC20(tokenOut, bytes32(0));
|
294
|
+
|
295
|
+
emit SwapUnoV2(tokenIn, tokenOut, amountIn, amountOut, PROTOCOL());
|
296
|
+
}
|
297
|
+
|
298
|
+
struct UniV2AddLiquidityArgs {
|
299
|
+
address tokenA;
|
300
|
+
address tokenB;
|
301
|
+
uint256 amountADesired;
|
302
|
+
uint256 amountBDesired;
|
303
|
+
uint256 amountAMin;
|
304
|
+
uint256 amountBMin;
|
305
|
+
uint256 deadline;
|
306
|
+
}
|
307
|
+
|
308
|
+
/**
|
309
|
+
* @notice Adds liquidity to the pool via delegate call using UniswapV2-like protocol
|
310
|
+
* @param _data - encoded data for protocol controller
|
311
|
+
*/
|
312
|
+
function delegatedAddLiquidity(bytes calldata _data) external checkDelegateCall nonReentrant {
|
313
|
+
UniV2AddLiquidityArgs memory alArgs = abi.decode(_data, (UniV2AddLiquidityArgs));
|
314
|
+
|
315
|
+
_checkDeadline(alArgs.deadline);
|
316
|
+
|
317
|
+
(address tokenA, address tokenB) = (alArgs.tokenA, alArgs.tokenB);
|
318
|
+
|
319
|
+
IPoolWhitelist pool = IPoolWhitelist(address(this));
|
320
|
+
|
321
|
+
if (!pool.isTokenAllowed(tokenA)) revert IPoolWhitelist.TokenIsNotAllowed(tokenA);
|
322
|
+
if (!pool.isTokenAllowed(tokenB)) revert IPoolWhitelist.TokenIsNotAllowed(tokenB);
|
323
|
+
|
324
|
+
UnoswapV2Controller thisController = _delegatedThisController();
|
325
|
+
(uint256 amountA, uint256 amountB, address pair) = thisController.quoteExactLiquidityAmounts(
|
326
|
+
tokenA,
|
327
|
+
tokenB,
|
328
|
+
alArgs.amountADesired,
|
329
|
+
alArgs.amountBDesired,
|
330
|
+
alArgs.amountAMin,
|
331
|
+
alArgs.amountBMin,
|
332
|
+
alArgs.deadline
|
333
|
+
);
|
334
|
+
|
335
|
+
bool reversed = tokenA > tokenB;
|
336
|
+
|
337
|
+
IERC20(tokenA).safeTransfer(pair, reversed ? amountB : amountA);
|
338
|
+
IERC20(tokenB).safeTransfer(pair, reversed ? amountA : amountB);
|
339
|
+
|
340
|
+
// pair should check for 0 liquidity
|
341
|
+
uint256 liquidity = IUniswapV2Pair(pair).mint(address(this));
|
342
|
+
|
343
|
+
if (liquidity == 0) revert INSUFFICIENT_LIQUIDITY();
|
344
|
+
|
345
|
+
IUFarmPool(address(this)).removeERC20(tokenA);
|
346
|
+
IUFarmPool(address(this)).removeERC20(tokenB);
|
347
|
+
IUFarmPool(address(this)).addERC20(pair, PROTOCOL());
|
348
|
+
|
349
|
+
emit LiquidityAddedUnoV2(tokenA, tokenB, amountA, amountB, liquidity, pair, PROTOCOL());
|
350
|
+
}
|
351
|
+
|
352
|
+
struct UniV2RemoveLiquidityArgs {
|
353
|
+
address tokenA;
|
354
|
+
address tokenB;
|
355
|
+
uint256 liquidity;
|
356
|
+
uint256 amountAMin;
|
357
|
+
uint256 amountBMin;
|
358
|
+
uint256 deadline;
|
359
|
+
}
|
360
|
+
|
361
|
+
/**
|
362
|
+
* @notice Removes liquidity from the pool via delegate call using UniswapV2-like protocol
|
363
|
+
* @param _data - encoded data for protocol controller
|
364
|
+
*/
|
365
|
+
function delegatedRemoveLiquidity(bytes calldata _data) external checkDelegateCall nonReentrant {
|
366
|
+
UniV2RemoveLiquidityArgs memory rlArgs = abi.decode(_data, (UniV2RemoveLiquidityArgs));
|
367
|
+
_checkDeadline(rlArgs.deadline);
|
368
|
+
|
369
|
+
(address tokenA, address tokenB) = (rlArgs.tokenA, rlArgs.tokenB);
|
370
|
+
|
371
|
+
if (tokenA > tokenB) {
|
372
|
+
(tokenA, tokenB) = (tokenB, tokenA);
|
373
|
+
(rlArgs.amountAMin, rlArgs.amountBMin) = (rlArgs.amountBMin, rlArgs.amountAMin);
|
374
|
+
}
|
375
|
+
|
376
|
+
address pair = pairForSorted(tokenA, tokenB);
|
377
|
+
IERC20(pair).safeTransfer(pair, rlArgs.liquidity);
|
378
|
+
|
379
|
+
IUFarmPool pool = IUFarmPool(address(this));
|
380
|
+
(address _target, bytes32 _withdrawalHash) = _getTarget();
|
381
|
+
|
382
|
+
(uint256 amountA, uint256 amountB) = IUniswapV2Pair(pair).burn(_target);
|
383
|
+
|
384
|
+
if (amountA < rlArgs.amountAMin) revert INSUFFICIENT_A_AMOUNT();
|
385
|
+
if (amountB < rlArgs.amountBMin) revert INSUFFICIENT_B_AMOUNT();
|
386
|
+
|
387
|
+
pool.removeERC20(pair);
|
388
|
+
|
389
|
+
if (_target == address(this)) {
|
390
|
+
pool.addERC20(tokenA, bytes32(0));
|
391
|
+
pool.addERC20(tokenB, bytes32(0));
|
392
|
+
} else {
|
393
|
+
emit IUFarmPool.Withdraw(_target, tokenA, amountA, _withdrawalHash);
|
394
|
+
emit IUFarmPool.Withdraw(_target, tokenB, amountB, _withdrawalHash);
|
395
|
+
}
|
396
|
+
|
397
|
+
emit LiquidityRemovedUnoV2(
|
398
|
+
tokenA,
|
399
|
+
tokenB,
|
400
|
+
amountA,
|
401
|
+
amountB,
|
402
|
+
rlArgs.liquidity,
|
403
|
+
pair,
|
404
|
+
_target,
|
405
|
+
PROTOCOL()
|
406
|
+
);
|
407
|
+
}
|
408
|
+
|
409
|
+
/**
|
410
|
+
* @inheritdoc IERC20SynthController
|
411
|
+
*/
|
412
|
+
function encodePartialWithdrawalERC20(
|
413
|
+
address _token,
|
414
|
+
uint256 _numerator,
|
415
|
+
uint256 _denominator
|
416
|
+
) external view override returns (bytes[] memory encodedTxs) {
|
417
|
+
encodedTxs = new bytes[](1);
|
418
|
+
IUniswapV2Pair pair = IUniswapV2Pair(_token);
|
419
|
+
(address token0, address token1) = (pair.token0(), pair.token1());
|
420
|
+
|
421
|
+
uint256 liquidity = pair.balanceOf(msg.sender);
|
422
|
+
uint256 amountToRemove = (liquidity * _numerator) / _denominator;
|
423
|
+
|
424
|
+
UniV2RemoveLiquidityArgs memory rlArgs = UniV2RemoveLiquidityArgs({
|
425
|
+
tokenA: token0,
|
426
|
+
tokenB: token1,
|
427
|
+
liquidity: amountToRemove,
|
428
|
+
amountAMin: 0,
|
429
|
+
amountBMin: 0,
|
430
|
+
deadline: block.timestamp
|
431
|
+
});
|
432
|
+
|
433
|
+
encodedTxs[0] = abi.encodeCall(
|
434
|
+
UnoswapV2Controller.delegatedRemoveLiquidity,
|
435
|
+
abi.encode(rlArgs)
|
436
|
+
);
|
437
|
+
}
|
438
|
+
|
439
|
+
/**
|
440
|
+
* @notice Returns optimal amount of tokens that will be provided for given amountIn and path
|
441
|
+
* @param tokenA - address of the first token in the pair
|
442
|
+
* @param tokenB - address of the second token in the pair
|
443
|
+
* @param amountADesired - desired amount of tokenA to be provided
|
444
|
+
* @return amountB - optimal amount of tokenA that will be provided
|
445
|
+
* @return desiredLiquidity - amount of liquidity tokens that will be received
|
446
|
+
* @return totalLiquidity - total amount of liquidity tokens in the pair
|
447
|
+
* @return pair - address of the pair that was used for adding liquidity
|
448
|
+
*/
|
449
|
+
function quoteOptimalLiquidityAmount(
|
450
|
+
address tokenA,
|
451
|
+
address tokenB,
|
452
|
+
uint256 amountADesired
|
453
|
+
)
|
454
|
+
external
|
455
|
+
view
|
456
|
+
nonZeroAddress(tokenA)
|
457
|
+
nonZeroAddress(tokenB)
|
458
|
+
returns (uint256 amountB, uint256 desiredLiquidity, uint256 totalLiquidity, address pair)
|
459
|
+
{
|
460
|
+
if (amountADesired == 0) {
|
461
|
+
revert INSUFFICIENT_AMOUNT();
|
462
|
+
}
|
463
|
+
|
464
|
+
bool reversed = tokenA > tokenB;
|
465
|
+
|
466
|
+
pair = pairFor(tokenA, tokenB);
|
467
|
+
|
468
|
+
try IUniswapV2Pair(pair).getReserves() returns (uint112 reserveA, uint112 reserveB, uint32) {
|
469
|
+
if (reserveA == 0 || reserveB == 0) {
|
470
|
+
revert INSUFFICIENT_LIQUIDITY();
|
471
|
+
}
|
472
|
+
|
473
|
+
amountB = _quote(
|
474
|
+
amountADesired,
|
475
|
+
reversed ? reserveB : reserveA,
|
476
|
+
reversed ? reserveA : reserveB
|
477
|
+
);
|
478
|
+
|
479
|
+
desiredLiquidity = UFarmMathLib.sqrt(amountADesired * amountB);
|
480
|
+
totalLiquidity = IUniswapV2Pair(pair).totalSupply() + desiredLiquidity;
|
481
|
+
} catch {
|
482
|
+
revert INVALID_PAIR();
|
483
|
+
}
|
484
|
+
}
|
485
|
+
|
486
|
+
/**
|
487
|
+
* @inheritdoc IUnoswapV2Controller
|
488
|
+
*/
|
489
|
+
function quoteExactLiquidityAmounts(
|
490
|
+
address tokenA,
|
491
|
+
address tokenB,
|
492
|
+
uint256 amountADesired,
|
493
|
+
uint256 amountBDesired,
|
494
|
+
uint256 amountAMin,
|
495
|
+
uint256 amountBMin,
|
496
|
+
uint256 deadline
|
497
|
+
) external view override returns (uint256 amountA, uint256 amountB, address pair) {
|
498
|
+
return
|
499
|
+
_quoteExactLiquidityAmounts(
|
500
|
+
tokenA,
|
501
|
+
tokenB,
|
502
|
+
amountADesired,
|
503
|
+
amountBDesired,
|
504
|
+
amountAMin,
|
505
|
+
amountBMin,
|
506
|
+
deadline
|
507
|
+
);
|
508
|
+
}
|
509
|
+
|
510
|
+
/**
|
511
|
+
* @notice Returns token addresses and amounts that will be received for given lp token amount
|
512
|
+
* @param lpToken - address of the liquidity pool token
|
513
|
+
* @param balance - amount of lp tokens
|
514
|
+
* @return tokenA - address of the first token in the pair
|
515
|
+
* @return tokenB - address of the second token in the pair
|
516
|
+
* @return amountA - amount of tokenA to be received
|
517
|
+
* @return amountB - amount of tokenB to be received
|
518
|
+
* @return totalLiquidity - total amount of liquidity tokens in the pair
|
519
|
+
*/
|
520
|
+
function quoteExactTokenAmounts(
|
521
|
+
address lpToken,
|
522
|
+
uint256 balance
|
523
|
+
)
|
524
|
+
external
|
525
|
+
view
|
526
|
+
returns (
|
527
|
+
address tokenA,
|
528
|
+
address tokenB,
|
529
|
+
uint256 amountA,
|
530
|
+
uint256 amountB,
|
531
|
+
uint256 totalLiquidity
|
532
|
+
)
|
533
|
+
{
|
534
|
+
IUniswapV2Pair _pair = IUniswapV2Pair(lpToken);
|
535
|
+
try _pair.getReserves() returns (uint112 reserveA, uint112 reserveB, uint32) {
|
536
|
+
(tokenA, tokenB) = (_pair.token0(), _pair.token1());
|
537
|
+
uint256 klast;
|
538
|
+
(totalLiquidity, klast) = (_pair.totalSupply(), _pair.kLast());
|
539
|
+
|
540
|
+
(amountA, amountB) = computeLiquidityValueWithFee(
|
541
|
+
reserveA,
|
542
|
+
reserveB,
|
543
|
+
totalLiquidity,
|
544
|
+
balance,
|
545
|
+
true,
|
546
|
+
klast
|
547
|
+
);
|
548
|
+
} catch {
|
549
|
+
revert INVALID_PAIR();
|
550
|
+
}
|
551
|
+
}
|
552
|
+
|
553
|
+
/**
|
554
|
+
* @inheritdoc IERC20SynthController
|
555
|
+
* @dev Thanks for the idea to Alpha Homora V2 team
|
556
|
+
* https://github.com/AlphaFinanceLab/alpha-homora-v2-contract/blob/f74fc460bd614ad15bbef57c88f6b470e5efd1fd/contracts/oracle/UniswapV2Oracle.sol#L20
|
557
|
+
*/
|
558
|
+
function getCostControlledERC20(
|
559
|
+
address lpAsset,
|
560
|
+
uint256 lpAmount,
|
561
|
+
address valueToken
|
562
|
+
) external view returns (uint256 cost) {
|
563
|
+
IUniswapV2Pair pair = IUniswapV2Pair(lpAsset);
|
564
|
+
uint256 totalSupply = pair.totalSupply();
|
565
|
+
(address token0, address token1) = (pair.token0(), pair.token1());
|
566
|
+
(uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
|
567
|
+
|
568
|
+
(uint256 fairPrice, uint8 numenator0, uint8 numenator1) = _getFairPrice(
|
569
|
+
token0,
|
570
|
+
token1,
|
571
|
+
valueToken
|
572
|
+
);
|
573
|
+
uint256 sqrtK = UFarmMathLib.sqrt(reserve0) * UFarmMathLib.sqrt(reserve1);
|
574
|
+
// Calculate price
|
575
|
+
cost = (2 * sqrtK * fairPrice) / totalSupply;
|
576
|
+
// Calculate cost in USD
|
577
|
+
cost = (lpAmount * cost) / UFarmMathLib.sqrt(10 ** (numenator0 + numenator1));
|
578
|
+
return cost;
|
579
|
+
}
|
580
|
+
|
581
|
+
function computeLiquidityValueWithFee(
|
582
|
+
uint112 reservesA,
|
583
|
+
uint112 reservesB,
|
584
|
+
uint256 totalSupply,
|
585
|
+
uint256 liquidityAmount,
|
586
|
+
bool feeOn,
|
587
|
+
uint256 kLast
|
588
|
+
) internal pure returns (uint256 tokenAAmount, uint256 tokenBAmount) {
|
589
|
+
if (feeOn && kLast > 0) {
|
590
|
+
uint256 rootK = UFarmMathLib.sqrt(reservesA * (reservesB));
|
591
|
+
uint256 rootKLast = UFarmMathLib.sqrt(kLast);
|
592
|
+
if (rootK > rootKLast) {
|
593
|
+
uint numerator1 = totalSupply;
|
594
|
+
uint numerator2 = rootK - rootKLast;
|
595
|
+
uint denominator = rootK * (5) + (rootKLast);
|
596
|
+
uint feeLiquidity = (numerator1 * numerator2) / denominator;
|
597
|
+
totalSupply = totalSupply + feeLiquidity;
|
598
|
+
}
|
599
|
+
}
|
600
|
+
return (
|
601
|
+
(reservesA * (liquidityAmount)) / totalSupply,
|
602
|
+
(reservesB * (liquidityAmount)) / totalSupply
|
603
|
+
);
|
604
|
+
}
|
605
|
+
|
606
|
+
function _quoteExactLiquidityAmounts(
|
607
|
+
address tokenA,
|
608
|
+
address tokenB,
|
609
|
+
uint256 amountADesired,
|
610
|
+
uint256 amountBDesired,
|
611
|
+
uint256 amountAMin,
|
612
|
+
uint256 amountBMin,
|
613
|
+
uint256 deadline
|
614
|
+
) internal view returns (uint256 amountA, uint256 amountB, address pair) {
|
615
|
+
if (block.timestamp > deadline) revert DEADLINE_PASSED();
|
616
|
+
if (amountADesired == 0 || amountBDesired == 0) {
|
617
|
+
revert INSUFFICIENT_AMOUNT();
|
618
|
+
}
|
619
|
+
|
620
|
+
if (tokenA > tokenB) {
|
621
|
+
(tokenA, tokenB) = (tokenB, tokenA);
|
622
|
+
(amountADesired, amountBDesired) = (amountBDesired, amountADesired);
|
623
|
+
(amountAMin, amountBMin) = (amountBMin, amountAMin);
|
624
|
+
}
|
625
|
+
|
626
|
+
pair = pairForSorted(tokenA, tokenB);
|
627
|
+
|
628
|
+
try IUniswapV2Pair(pair).getReserves() returns (uint112 reserveA, uint112 reserveB, uint32) {
|
629
|
+
if (reserveA == 0 || reserveB == 0) {
|
630
|
+
revert INSUFFICIENT_LIQUIDITY();
|
631
|
+
}
|
632
|
+
|
633
|
+
uint256 amountBOptimal = _quote(amountADesired, reserveA, reserveB);
|
634
|
+
|
635
|
+
if (amountBOptimal <= amountBDesired) {
|
636
|
+
if (amountBOptimal < amountBMin) revert INSUFFICIENT_B_AMOUNT();
|
637
|
+
return (amountADesired, amountBOptimal, pair);
|
638
|
+
} else {
|
639
|
+
uint256 amountAOptimal = _quote(amountBDesired, reserveB, reserveA);
|
640
|
+
|
641
|
+
if (amountAOptimal < amountAMin || amountAOptimal > amountADesired)
|
642
|
+
revert INSUFFICIENT_A_AMOUNT();
|
643
|
+
|
644
|
+
return (amountAOptimal, amountBDesired, pair);
|
645
|
+
}
|
646
|
+
} catch {
|
647
|
+
revert INVALID_PAIR();
|
648
|
+
}
|
649
|
+
}
|
650
|
+
|
651
|
+
function _getFairPrice(
|
652
|
+
address tokenA,
|
653
|
+
address tokenB,
|
654
|
+
address valueToken
|
655
|
+
) internal view returns (uint256 fairPrice, uint8 decimals0, uint8 decimals1) {
|
656
|
+
(decimals0, decimals1) = (IERC20Metadata(tokenA).decimals(), IERC20Metadata(tokenB).decimals());
|
657
|
+
|
658
|
+
(uint256 fairPrice0, uint256 fairPrice1) = (
|
659
|
+
IPriceOracle(priceOracle).getCostERC20(tokenA, 10 ** (decimals0), valueToken),
|
660
|
+
IPriceOracle(priceOracle).getCostERC20(tokenB, 10 ** (decimals1), valueToken)
|
661
|
+
);
|
662
|
+
|
663
|
+
fairPrice = UFarmMathLib.sqrt(fairPrice0 * fairPrice1);
|
664
|
+
}
|
665
|
+
|
666
|
+
// LIB
|
667
|
+
|
668
|
+
function sortTokens(
|
669
|
+
address tokenA,
|
670
|
+
address tokenB
|
671
|
+
) internal pure returns (address token0, address token1) {
|
672
|
+
if (tokenA == tokenB) revert IDENTICAL_ADDRESSES();
|
673
|
+
if (tokenA == address(0) || tokenB == address(0)) revert ZeroAddress();
|
674
|
+
if (tokenA < tokenB) return (tokenA, tokenB);
|
675
|
+
else return (tokenB, tokenA);
|
676
|
+
}
|
677
|
+
|
678
|
+
/**
|
679
|
+
* @notice Returns address of the pair for given tokens
|
680
|
+
* @dev Checks for address(0) and identical tokens addresses
|
681
|
+
* @param tokenA - address of the first token
|
682
|
+
* @param tokenB - address of the second token
|
683
|
+
* @return pair - address of the pair
|
684
|
+
*/
|
685
|
+
function pairFor(address tokenA, address tokenB) public view returns (address pair) {
|
686
|
+
(address token0, address token1) = sortTokens(tokenA, tokenB);
|
687
|
+
return pairForSorted(token0, token1);
|
688
|
+
}
|
689
|
+
|
690
|
+
/**
|
691
|
+
* @notice Returns address of the pair for given tokens
|
692
|
+
* @dev Doesn't check for address(0) and identical tokens addresses
|
693
|
+
* @param tokenA - address of the first token
|
694
|
+
* @param tokenB - address of the second token
|
695
|
+
* @return pair - address of the pair
|
696
|
+
*/
|
697
|
+
function pairForSorted(address tokenA, address tokenB) public view returns (address pair) {
|
698
|
+
pair = address(
|
699
|
+
uint160(
|
700
|
+
uint256(
|
701
|
+
keccak256(
|
702
|
+
abi.encodePacked(
|
703
|
+
hex'ff',
|
704
|
+
factory,
|
705
|
+
keccak256(abi.encodePacked(tokenA, tokenB)),
|
706
|
+
FACTORY_INIT_CODE_HASH
|
707
|
+
)
|
708
|
+
)
|
709
|
+
)
|
710
|
+
)
|
711
|
+
);
|
712
|
+
}
|
713
|
+
|
714
|
+
function _checkOutAmount(uint256 amountOut, uint256 amountOutMin) internal pure {
|
715
|
+
if (amountOut < amountOutMin) revert INSUFFICIENT_OUTPUT_AMOUNT();
|
716
|
+
}
|
717
|
+
|
718
|
+
// fetches and sorts the reserves for a pair
|
719
|
+
function _getReserves(
|
720
|
+
address tokenA,
|
721
|
+
address tokenB
|
722
|
+
) private view returns (uint256 reserveA, uint256 reserveB) {
|
723
|
+
(address token0, ) = sortTokens(tokenA, tokenB);
|
724
|
+
(uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pairForSorted(tokenA, tokenB))
|
725
|
+
.getReserves();
|
726
|
+
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
|
727
|
+
}
|
728
|
+
|
729
|
+
// given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
|
730
|
+
function _quote(
|
731
|
+
uint256 amountA,
|
732
|
+
uint256 reserveA,
|
733
|
+
uint256 reserveB
|
734
|
+
) private pure returns (uint256 amountB) {
|
735
|
+
amountB = (amountA * (reserveB)) / reserveA;
|
736
|
+
}
|
737
|
+
|
738
|
+
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
|
739
|
+
function getAmountOutReserves(
|
740
|
+
uint256 amountIn,
|
741
|
+
uint256 reserveIn,
|
742
|
+
uint256 reserveOut
|
743
|
+
) private pure returns (uint256 amountOut) {
|
744
|
+
if (amountIn == 0) revert INSUFFICIENT_INPUT_AMOUNT();
|
745
|
+
if (reserveIn == 0 || reserveOut == 0) revert INSUFFICIENT_LIQUIDITY();
|
746
|
+
uint256 amountInWithFee = amountIn * (997);
|
747
|
+
uint256 numerator = amountInWithFee * (reserveOut);
|
748
|
+
uint256 denominator = reserveIn * (1000) + (amountInWithFee);
|
749
|
+
amountOut = numerator / denominator;
|
750
|
+
}
|
751
|
+
|
752
|
+
// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
|
753
|
+
function getAmountInRes(
|
754
|
+
uint256 amountOut,
|
755
|
+
uint256 reserveIn,
|
756
|
+
uint256 reserveOut
|
757
|
+
) private pure returns (uint256 amountIn) {
|
758
|
+
if (amountOut == 0) revert INSUFFICIENT_OUTPUT_AMOUNT();
|
759
|
+
if (reserveIn == 0 || reserveOut == 0) revert INSUFFICIENT_LIQUIDITY();
|
760
|
+
uint256 numerator = reserveIn * (amountOut) * (1000);
|
761
|
+
uint256 denominator = (reserveOut - amountOut) * (997);
|
762
|
+
amountIn = (numerator / denominator) + (1);
|
763
|
+
}
|
764
|
+
|
765
|
+
// performs chained getAmountOut calculations on any number of pairs
|
766
|
+
function getAmountsOut(
|
767
|
+
uint256 amountIn,
|
768
|
+
address[] memory path
|
769
|
+
) private view returns (uint256[] memory amounts) {
|
770
|
+
uint256 length = path.length;
|
771
|
+
if (length < 2) revert INVALID_PATH();
|
772
|
+
amounts = new uint256[](length);
|
773
|
+
amounts[0] = amountIn;
|
774
|
+
for (uint256 i; i < length - 1; ++i) {
|
775
|
+
(uint256 reserveIn, uint256 reserveOut) = _getReserves(path[i], path[i + 1]);
|
776
|
+
amounts[i + 1] = getAmountOutReserves(amounts[i], reserveIn, reserveOut);
|
777
|
+
}
|
778
|
+
}
|
779
|
+
|
780
|
+
function _checkDeadline(uint256 deadline) private view {
|
781
|
+
if (block.timestamp > deadline) revert DEADLINE_PASSED();
|
782
|
+
}
|
783
|
+
|
784
|
+
function _delegatedThisController() private view returns (UnoswapV2Controller controller) {
|
785
|
+
IUFarmPool pool = IUFarmPool(address(this));
|
786
|
+
controller = UnoswapV2Controller(payable(IUFarmCore(pool.ufarmCore()).controllers(PROTOCOL())));
|
787
|
+
if (address(controller) == address(0)) revert FETCHING_CONTROLLER_FAILED();
|
788
|
+
}
|
789
|
+
}
|