@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,504 @@
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
2
|
+
|
3
|
+
pragma solidity ^0.8.0;
|
4
|
+
|
5
|
+
/// INTERFACES
|
6
|
+
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
7
|
+
import {IUFarmCore} from '../core/IUFarmCore.sol';
|
8
|
+
import {IUFarmFund} from './IUFarmFund.sol';
|
9
|
+
import {IUFarmPool} from '../pool/IUFarmPool.sol';
|
10
|
+
import {IPoolFactory} from '../pool/PoolFactory.sol';
|
11
|
+
import {Permissions} from '../permissions/Permissions.sol';
|
12
|
+
|
13
|
+
/// CONTRACTS
|
14
|
+
import {ECDSARecover} from '../../shared/ECDSARecover.sol';
|
15
|
+
import {NZGuard} from '../../shared/NZGuard.sol';
|
16
|
+
import {ReentrancyGuardUpgradeable as ReentrancyGuard} from '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol';
|
17
|
+
import {UFarmErrors} from '../../shared/UFarmErrors.sol';
|
18
|
+
import {UFarmPermissionsModel} from '../permissions/UFarmPermissionsModel.sol';
|
19
|
+
import {UFarmOwnableUUPSBeacon} from '../../shared/UFarmOwnableUUPSBeacon.sol';
|
20
|
+
|
21
|
+
/// LIBRARIES
|
22
|
+
import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
|
23
|
+
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
|
24
|
+
import {SafeOPS} from '../../shared/SafeOPS.sol';
|
25
|
+
|
26
|
+
/**
|
27
|
+
* @title UFarmFund contract
|
28
|
+
* @author https://ufarm.digital/
|
29
|
+
* @notice Fund contract for the UFarm protocol for managing pools and fund employees permissions
|
30
|
+
*/
|
31
|
+
contract UFarmFund is
|
32
|
+
IUFarmFund,
|
33
|
+
UFarmPermissionsModel,
|
34
|
+
Permissions,
|
35
|
+
ReentrancyGuard,
|
36
|
+
NZGuard,
|
37
|
+
UFarmErrors,
|
38
|
+
ECDSARecover,
|
39
|
+
UFarmOwnableUUPSBeacon
|
40
|
+
{
|
41
|
+
using SafeERC20 for IERC20;
|
42
|
+
using EnumerableSet for EnumerableSet.AddressSet;
|
43
|
+
|
44
|
+
/// @notice Struct for the pool and pool admin address
|
45
|
+
struct PoolContracts {
|
46
|
+
address poolAddr;
|
47
|
+
address poolAdmin;
|
48
|
+
}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* @notice Native asset address constant
|
52
|
+
*/
|
53
|
+
address public constant NATIVE_ASSET = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
54
|
+
|
55
|
+
/**
|
56
|
+
* @inheritdoc IUFarmFund
|
57
|
+
*/
|
58
|
+
FundStatus public status;
|
59
|
+
|
60
|
+
/**
|
61
|
+
* @inheritdoc IUFarmFund
|
62
|
+
*/
|
63
|
+
address public ufarmCore;
|
64
|
+
|
65
|
+
/**
|
66
|
+
* @notice Check if the pool is in the fund
|
67
|
+
*
|
68
|
+
* @return _isPool True if the pool is in the fund
|
69
|
+
*/
|
70
|
+
mapping(address => bool) public isPool;
|
71
|
+
|
72
|
+
PoolContracts[] private __poolContracts;
|
73
|
+
|
74
|
+
mapping(bytes32 => bool) private __acceptedInvites;
|
75
|
+
|
76
|
+
/// EVENTS
|
77
|
+
/**
|
78
|
+
* @notice Emitted when the pool is created
|
79
|
+
* @param name - pool name
|
80
|
+
* @param symbol - pool symbol
|
81
|
+
* @param minInvestment - minimum investment in the pool
|
82
|
+
* @param maxInvestment - maximum investment in the pool
|
83
|
+
* @param managementCommission - management commission
|
84
|
+
* @param packedPerformanceComission - packed performance commission [(step:uint16, fee:uint16)...]
|
85
|
+
* @param withdrawalLockupPeriod - withdrawal lockup period
|
86
|
+
* @param poolId - pool id in the fund
|
87
|
+
* @param pool - pool address
|
88
|
+
* @param poolAdmin - pool admin address
|
89
|
+
*/
|
90
|
+
event PoolCreated(
|
91
|
+
string name,
|
92
|
+
string symbol,
|
93
|
+
uint256 minInvestment,
|
94
|
+
uint256 maxInvestment,
|
95
|
+
uint256 managementCommission,
|
96
|
+
uint256 packedPerformanceComission,
|
97
|
+
uint256 withdrawalLockupPeriod,
|
98
|
+
uint256 poolId,
|
99
|
+
address pool,
|
100
|
+
address poolAdmin
|
101
|
+
);
|
102
|
+
|
103
|
+
/**
|
104
|
+
* @notice Emitted when the fund status is changed
|
105
|
+
* @param status - new status of the fund
|
106
|
+
*/
|
107
|
+
event FundStatusChanged(FundStatus indexed status);
|
108
|
+
|
109
|
+
/**
|
110
|
+
* @notice Emitted when the invitation is accepted
|
111
|
+
* @param inviter - inviter fund employee
|
112
|
+
* @param invitee - invitee fund employee
|
113
|
+
* @param msgHash - hash of the invitation that was accepted
|
114
|
+
*/
|
115
|
+
event InvitationAccepted(
|
116
|
+
address indexed inviter,
|
117
|
+
address indexed invitee,
|
118
|
+
bytes32 indexed msgHash
|
119
|
+
);
|
120
|
+
|
121
|
+
/// ERRORS
|
122
|
+
error StatusAlreadySet(FundStatus status);
|
123
|
+
error NotAPool(address pool);
|
124
|
+
error WrongAsset();
|
125
|
+
error InvitationExpired(uint256 deadline, uint256 timeNow);
|
126
|
+
error AlreadyMember();
|
127
|
+
error EmptyPermissions();
|
128
|
+
|
129
|
+
/// MODIFIERS
|
130
|
+
/**
|
131
|
+
* @notice Reverts if the fund is not in the required status
|
132
|
+
* @param _status - required status of the fund
|
133
|
+
*/
|
134
|
+
modifier requiredStatus(FundStatus _status) {
|
135
|
+
_requiredStatus(_status);
|
136
|
+
_;
|
137
|
+
}
|
138
|
+
|
139
|
+
/**
|
140
|
+
* @notice Reverts if the fund can't manage pools
|
141
|
+
*/
|
142
|
+
modifier poolManagementAllowed() {
|
143
|
+
_poolManagementAllowed();
|
144
|
+
_;
|
145
|
+
}
|
146
|
+
|
147
|
+
/**
|
148
|
+
* @notice Reverts if the UFarm platform is paused
|
149
|
+
*/
|
150
|
+
modifier ufarmIsNotPaused() {
|
151
|
+
_ufarmIsNotPaused();
|
152
|
+
_;
|
153
|
+
}
|
154
|
+
|
155
|
+
/**
|
156
|
+
* @notice Reverts if the caller is not the fund owner or have two permissions
|
157
|
+
* @param permission1 - first permission (often membership)
|
158
|
+
* @param permission2 - second permission (often some kind of editor)
|
159
|
+
*/
|
160
|
+
modifier ownerOrHaveTwoPermissions(uint8 permission1, uint8 permission2) {
|
161
|
+
if (!_hasPermission(msg.sender, uint8(Permissions.Fund.Owner))) {
|
162
|
+
_checkForPermissions(msg.sender, _twoPermissionsToMask(permission1, permission2));
|
163
|
+
}
|
164
|
+
_;
|
165
|
+
}
|
166
|
+
|
167
|
+
function version() public pure override returns (string memory) {
|
168
|
+
return '1.0';
|
169
|
+
}
|
170
|
+
|
171
|
+
function name() public pure override(ECDSARecover) returns (string memory) {
|
172
|
+
return 'UFarmFund';
|
173
|
+
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* @notice Get the pool address by the pool id
|
177
|
+
*
|
178
|
+
* @return poolContracts struct {poolAddr: address, poolAdmin: address}
|
179
|
+
*/
|
180
|
+
function getPool(uint256 index) external view returns (PoolContracts memory) {
|
181
|
+
return __poolContracts[index];
|
182
|
+
}
|
183
|
+
|
184
|
+
/**
|
185
|
+
* @notice Get the list of the pools
|
186
|
+
*
|
187
|
+
* @return _pools List of the pools
|
188
|
+
*/
|
189
|
+
function getPools() external view returns (PoolContracts[] memory) {
|
190
|
+
return __poolContracts;
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* @notice Get the count of the pools
|
195
|
+
*
|
196
|
+
* @return _pools Count of the pools
|
197
|
+
*/
|
198
|
+
function poolsCount() external view returns (uint256) {
|
199
|
+
return __poolContracts.length;
|
200
|
+
}
|
201
|
+
|
202
|
+
/**
|
203
|
+
* @notice Verifies the invitation to the fund
|
204
|
+
* @param invitation - invitation struct
|
205
|
+
* @param signature - signature of the invitation
|
206
|
+
* @return inviter - address of the inviter or reverts if check failed
|
207
|
+
*/
|
208
|
+
function verifyInvitation(
|
209
|
+
FundMemberInvitation memory invitation,
|
210
|
+
bytes memory signature
|
211
|
+
) public view returns (address inviter, bytes32 msgHash) {
|
212
|
+
msgHash = ECDSARecover.toEIP712MessageHash(DOMAIN_SEPARATOR(), _hashInvitation(invitation));
|
213
|
+
inviter = ECDSARecover.recoverAddress(msgHash, signature);
|
214
|
+
|
215
|
+
if (inviter == address(0)) revert ECDSARecover.WrongSignature();
|
216
|
+
|
217
|
+
if (__acceptedInvites[msgHash]) revert UFarmErrors.ActionAlreadyDone();
|
218
|
+
|
219
|
+
if (_hasPermission(invitation.invitee, uint8(Permissions.Fund.Member))) revert AlreadyMember();
|
220
|
+
|
221
|
+
if (invitation.deadline < block.timestamp)
|
222
|
+
revert InvitationExpired(invitation.deadline, block.timestamp);
|
223
|
+
|
224
|
+
if (invitation.permissionsMask > 0) {
|
225
|
+
_canUpdatePermissions(inviter, invitation.invitee, invitation.permissionsMask);
|
226
|
+
// returns inviter with hash
|
227
|
+
} else revert EmptyPermissions();
|
228
|
+
}
|
229
|
+
|
230
|
+
/**
|
231
|
+
* @notice Accepts the invitation
|
232
|
+
* @param invitation - invitation struct
|
233
|
+
* @param signature - signature of the invitation
|
234
|
+
*/
|
235
|
+
function acceptInvitation(
|
236
|
+
FundMemberInvitation memory invitation,
|
237
|
+
bytes memory signature
|
238
|
+
) external ufarmIsNotPaused {
|
239
|
+
(address inviter, bytes32 msgHash) = verifyInvitation(invitation, signature);
|
240
|
+
if (invitation.invitee == msg.sender) {
|
241
|
+
__acceptedInvites[msgHash] = true;
|
242
|
+
emit InvitationAccepted(inviter, msg.sender, msgHash);
|
243
|
+
_updatePermissions(msg.sender, invitation.permissionsMask);
|
244
|
+
} else revert NonAuthorized();
|
245
|
+
}
|
246
|
+
|
247
|
+
/**
|
248
|
+
* @notice Updates the permissions as mask for the user, replaces the old mask
|
249
|
+
*
|
250
|
+
* @param _user - user address
|
251
|
+
*
|
252
|
+
* @param _newPermissionsMask - new permissions mask
|
253
|
+
*/
|
254
|
+
function updatePermissions(address _user, uint256 _newPermissionsMask) external ufarmIsNotPaused {
|
255
|
+
_canUpdatePermissions(msg.sender, _user, _newPermissionsMask);
|
256
|
+
_updatePermissions(_user, _newPermissionsMask);
|
257
|
+
}
|
258
|
+
|
259
|
+
/**
|
260
|
+
* @notice Creates a new pool in the fund
|
261
|
+
*
|
262
|
+
* @param _settings - pool creation settings
|
263
|
+
* @param salt - salt for the pool
|
264
|
+
*
|
265
|
+
* @return pool - Pool address
|
266
|
+
*/
|
267
|
+
function createPool(
|
268
|
+
IUFarmPool.CreationSettings memory _settings,
|
269
|
+
bytes32 salt
|
270
|
+
)
|
271
|
+
external
|
272
|
+
ufarmIsNotPaused
|
273
|
+
ownerOrHaveTwoPermissions(uint8(Permissions.Fund.Member), uint8(Permissions.Fund.CreatePool))
|
274
|
+
poolManagementAllowed
|
275
|
+
returns (address pool, address poolAdmin)
|
276
|
+
{
|
277
|
+
(_settings.name, _settings.symbol) = (
|
278
|
+
string(abi.encodePacked('UFarm-', _settings.name)),
|
279
|
+
string(abi.encodePacked('UF-', _settings.symbol))
|
280
|
+
);
|
281
|
+
IUFarmPool.CreationSettingsWithLinks memory fullSettings = IUFarmPool
|
282
|
+
.CreationSettingsWithLinks({
|
283
|
+
params: _settings,
|
284
|
+
ufarmCore: ufarmCore,
|
285
|
+
ufarmFund: address(this)
|
286
|
+
});
|
287
|
+
(pool, poolAdmin) = IPoolFactory(IUFarmCore(ufarmCore).poolFactory()).createPool(
|
288
|
+
fullSettings,
|
289
|
+
salt
|
290
|
+
);
|
291
|
+
|
292
|
+
uint256 poolId = __poolContracts.length;
|
293
|
+
__poolContracts.push(PoolContracts({poolAddr: pool, poolAdmin: poolAdmin}));
|
294
|
+
isPool[pool] = true;
|
295
|
+
|
296
|
+
emit PoolCreated(
|
297
|
+
_settings.name,
|
298
|
+
_settings.symbol,
|
299
|
+
_settings.minInvestment,
|
300
|
+
_settings.maxInvestment,
|
301
|
+
_settings.managementCommission,
|
302
|
+
_settings.packedPerformanceCommission,
|
303
|
+
_settings.withdrawalLockupPeriod,
|
304
|
+
poolId,
|
305
|
+
pool,
|
306
|
+
poolAdmin
|
307
|
+
);
|
308
|
+
}
|
309
|
+
|
310
|
+
/**
|
311
|
+
* @notice Changes the status of the fund
|
312
|
+
* @param newStatus New status of the fund
|
313
|
+
*/
|
314
|
+
function changeStatus(FundStatus newStatus) external ufarmIsNotPaused {
|
315
|
+
if (
|
316
|
+
msg.sender == address(ufarmCore) ||
|
317
|
+
((newStatus == FundStatus.Active || newStatus == FundStatus.Terminated) &&
|
318
|
+
_hasPermission(msg.sender, uint8(Permissions.Fund.Owner)))
|
319
|
+
) {
|
320
|
+
_changeStatus(newStatus);
|
321
|
+
} else revert UFarmErrors.NonAuthorized();
|
322
|
+
}
|
323
|
+
|
324
|
+
/**
|
325
|
+
* @notice Deposits to the pool
|
326
|
+
*
|
327
|
+
* @param _pool - pool address
|
328
|
+
* @param _amount - amount of the deposit token that will be deposited
|
329
|
+
*/
|
330
|
+
function depositToPool(
|
331
|
+
address _pool,
|
332
|
+
uint256 _amount
|
333
|
+
)
|
334
|
+
external
|
335
|
+
ufarmIsNotPaused
|
336
|
+
ownerOrHaveTwoPermissions(uint8(Permissions.Fund.Member), uint8(Permissions.Fund.ManageFund))
|
337
|
+
poolManagementAllowed
|
338
|
+
{
|
339
|
+
_checkPool(_pool);
|
340
|
+
SafeOPS._forceApprove(IUFarmPool(_pool).valueToken(), _pool, _amount);
|
341
|
+
IUFarmPool(_pool).deposit(_amount);
|
342
|
+
}
|
343
|
+
|
344
|
+
/**
|
345
|
+
* @notice Withdraws from the pool
|
346
|
+
*
|
347
|
+
* @param _request - withdrawal request
|
348
|
+
*/
|
349
|
+
function withdrawFromPool(
|
350
|
+
IUFarmPool.SignedWithdrawalRequest calldata _request
|
351
|
+
)
|
352
|
+
external
|
353
|
+
ufarmIsNotPaused
|
354
|
+
ownerOrHaveTwoPermissions(uint8(Permissions.Fund.Member), uint8(Permissions.Fund.ManageFund))
|
355
|
+
poolManagementAllowed
|
356
|
+
{
|
357
|
+
_checkPool(_request.body.poolAddr);
|
358
|
+
IUFarmPool(_request.body.poolAddr).withdraw(_request);
|
359
|
+
}
|
360
|
+
|
361
|
+
//// ASSETS CONTROL
|
362
|
+
/**
|
363
|
+
* @notice Withdraws assets from the fund
|
364
|
+
*
|
365
|
+
* @param _token - address of the asset to withdraw
|
366
|
+
* @param _to - address of the recipient
|
367
|
+
* @param _amount - amount of the asset to withdraw
|
368
|
+
*/
|
369
|
+
function withdrawAsset(
|
370
|
+
address _token,
|
371
|
+
address _to,
|
372
|
+
uint256 _amount
|
373
|
+
)
|
374
|
+
external
|
375
|
+
ufarmIsNotPaused
|
376
|
+
ownerOrHaveTwoPermissions(uint8(Permissions.Fund.Member), uint8(Permissions.Fund.ManageFund))
|
377
|
+
{
|
378
|
+
if (_token == address(0) || isPool[_token]) revert WrongAsset();
|
379
|
+
IERC20(_token).safeTransfer(_to, _amount);
|
380
|
+
}
|
381
|
+
|
382
|
+
/**
|
383
|
+
* @notice Approves assets to the recipient
|
384
|
+
*
|
385
|
+
* @param _token - address of the asset to approve
|
386
|
+
* @param _recipient - allowed recipient
|
387
|
+
* @param _amount - new allowance
|
388
|
+
*/
|
389
|
+
function approveAssetTo(
|
390
|
+
address _token,
|
391
|
+
address _recipient,
|
392
|
+
uint256 _amount
|
393
|
+
)
|
394
|
+
external
|
395
|
+
ufarmIsNotPaused
|
396
|
+
ownerOrHaveTwoPermissions(uint8(Permissions.Fund.Member), uint8(Permissions.Fund.ManageFund))
|
397
|
+
{
|
398
|
+
if (_token == NATIVE_ASSET || _token == address(0) || isPool[_token]) revert WrongAsset();
|
399
|
+
SafeOPS._forceApprove(_token, _recipient, _amount);
|
400
|
+
}
|
401
|
+
|
402
|
+
/**
|
403
|
+
* @inheritdoc IUFarmFund
|
404
|
+
*/
|
405
|
+
function __init_UFarmFund(address _owner, address _ufarmCore) external checkDelegateCall initializer {
|
406
|
+
__UUPSUpgradeable_init();
|
407
|
+
__ReentrancyGuard_init();
|
408
|
+
__init_UFarmFund_unchained(_owner, _ufarmCore);
|
409
|
+
}
|
410
|
+
|
411
|
+
function __init_UFarmFund_unchained(address _owner, address _ufarmCore) internal {
|
412
|
+
ufarmCore = _ufarmCore;
|
413
|
+
|
414
|
+
_nonZeroAddress(_owner);
|
415
|
+
_updatePermissions(_owner, _FULL_PERMISSIONS_MASK);
|
416
|
+
}
|
417
|
+
|
418
|
+
function _changeStatus(FundStatus _status) private {
|
419
|
+
if (status == _status) {
|
420
|
+
revert StatusAlreadySet(status);
|
421
|
+
}
|
422
|
+
|
423
|
+
status = _status;
|
424
|
+
emit FundStatusChanged(status);
|
425
|
+
}
|
426
|
+
|
427
|
+
function _requiredStatus(FundStatus _status) private view {
|
428
|
+
if (status != _status) {
|
429
|
+
revert WrongFundStatus(_status, status);
|
430
|
+
}
|
431
|
+
}
|
432
|
+
|
433
|
+
function _checkPool(address _pool) private view {
|
434
|
+
if (!isPool[_pool]) revert NotAPool(_pool);
|
435
|
+
}
|
436
|
+
|
437
|
+
function _poolManagementAllowed() private view {
|
438
|
+
if (status != FundStatus.Approved && status != FundStatus.Active)
|
439
|
+
revert WrongFundStatus(FundStatus.Approved, status);
|
440
|
+
}
|
441
|
+
|
442
|
+
function _ufarmIsNotPaused() private view {
|
443
|
+
if (IUFarmCore(ufarmCore).isPaused()) revert UFarmIsPaused();
|
444
|
+
}
|
445
|
+
|
446
|
+
function _canUpdatePermissions(
|
447
|
+
address _updater,
|
448
|
+
address _updatee,
|
449
|
+
uint256 _newPermissionsMask
|
450
|
+
) private view {
|
451
|
+
uint256 currentUpdateeMask = _accountMask[_updatee];
|
452
|
+
// if update owner permissions
|
453
|
+
if (_isPermissionDiff(uint8(Permissions.Fund.Owner), currentUpdateeMask, _newPermissionsMask)) {
|
454
|
+
// only owner can grant or revoke owner permissions
|
455
|
+
_checkForPermissions(_updater, _permissionToMask(uint8(Permissions.Fund.Owner))); // reverts if caller is not owner
|
456
|
+
// owner cant update his own permissions
|
457
|
+
if (_updater == _updatee) revert NonAuthorized();
|
458
|
+
} else {
|
459
|
+
// if not owner
|
460
|
+
if (!_hasPermission(_updater, uint8(Permissions.Fund.Owner))) {
|
461
|
+
// should be able to grant or revoke permissions
|
462
|
+
_checkForPermissions(
|
463
|
+
_updater,
|
464
|
+
_twoPermissionsToMask(
|
465
|
+
uint8(Permissions.Fund.Member),
|
466
|
+
uint8(Permissions.Fund.UpdateFundPermissions)
|
467
|
+
)
|
468
|
+
);
|
469
|
+
// revert if want to grant UpdateFundPermissions
|
470
|
+
if (
|
471
|
+
_isPermissionDiff(
|
472
|
+
uint8(Permissions.Fund.UpdateFundPermissions),
|
473
|
+
currentUpdateeMask,
|
474
|
+
_newPermissionsMask
|
475
|
+
)
|
476
|
+
) revert NonAuthorized();
|
477
|
+
|
478
|
+
// revert if want to grant permissions that caller doesn't have
|
479
|
+
uint256 currentUpdaterMask = _accountMask[_updater];
|
480
|
+
uint256 hasBitsNotInCommon = _newPermissionsMask & (~currentUpdaterMask);
|
481
|
+
|
482
|
+
if (hasBitsNotInCommon > 0) {
|
483
|
+
revert NonAuthorized();
|
484
|
+
}
|
485
|
+
}
|
486
|
+
}
|
487
|
+
}
|
488
|
+
|
489
|
+
function _hashInvitation(FundMemberInvitation memory invitation) private pure returns (bytes32) {
|
490
|
+
return
|
491
|
+
keccak256(
|
492
|
+
abi.encode(
|
493
|
+
keccak256(
|
494
|
+
bytes('FundMemberInvitation(address invitee,uint256 permissionsMask,uint256 deadline)')
|
495
|
+
),
|
496
|
+
invitation.invitee,
|
497
|
+
invitation.permissionsMask,
|
498
|
+
invitation.deadline
|
499
|
+
)
|
500
|
+
);
|
501
|
+
}
|
502
|
+
|
503
|
+
uint256[50] private __gap;
|
504
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
2
|
+
|
3
|
+
pragma solidity ^0.8.0;
|
4
|
+
|
5
|
+
import {IChainlinkAggregator} from '../oracle/IChainlinkAggregator.sol';
|
6
|
+
import {Initializable} from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* @title ChainlinkedOracle contract
|
10
|
+
* @author https://ufarm.digital/
|
11
|
+
* @notice Calls Chainlink Aggregator contracts and checks for stale data
|
12
|
+
*/
|
13
|
+
abstract contract ChainlinkedOracle is Initializable {
|
14
|
+
struct ChainlinkAnswer {
|
15
|
+
uint80 roundId;
|
16
|
+
int256 answer;
|
17
|
+
uint256 startedAt;
|
18
|
+
uint256 updatedAt;
|
19
|
+
uint80 answeredInRound;
|
20
|
+
}
|
21
|
+
uint256 constant HOUR = 3600;
|
22
|
+
|
23
|
+
uint256 public chainlinkTimeout;
|
24
|
+
|
25
|
+
event ChainlinkTimeoutSet(uint256 timeout);
|
26
|
+
|
27
|
+
error WrongChainlinkPrice(address oracle);
|
28
|
+
error IncompleteChainlinkRound(address oracle);
|
29
|
+
error StaleChainlinkPrice(address oracle);
|
30
|
+
error ChainlinkOracleOutdated(address oracle);
|
31
|
+
error ChainlinkOracleNotSet(address asset);
|
32
|
+
error ChainlinkOracleIsDown(address oracle);
|
33
|
+
|
34
|
+
function __init__ChainlinkedOracle(uint256 _chainlinkTimeout) internal virtual onlyInitializing {
|
35
|
+
__init__ChainlinkedOracle_unchained(_chainlinkTimeout);
|
36
|
+
}
|
37
|
+
|
38
|
+
function __init__ChainlinkedOracle_unchained(
|
39
|
+
uint256 _chainlinkTimeout
|
40
|
+
) internal virtual onlyInitializing {
|
41
|
+
chainlinkTimeout = _chainlinkTimeout;
|
42
|
+
emit ChainlinkTimeoutSet(_chainlinkTimeout);
|
43
|
+
}
|
44
|
+
|
45
|
+
function _chainlinkLatestRoundData(
|
46
|
+
address _oracle
|
47
|
+
) internal view returns (ChainlinkAnswer memory latestRoundData) {
|
48
|
+
try IChainlinkAggregator(_oracle).latestRoundData() returns (
|
49
|
+
uint80 roundId,
|
50
|
+
int256 answer,
|
51
|
+
uint256 startedAt,
|
52
|
+
uint256 updatedAt,
|
53
|
+
uint80 answeredInRound
|
54
|
+
) {
|
55
|
+
if (answer < 1) revert WrongChainlinkPrice(_oracle);
|
56
|
+
if (updatedAt == 0) revert IncompleteChainlinkRound(_oracle);
|
57
|
+
if (roundId < answeredInRound) revert StaleChainlinkPrice(_oracle);
|
58
|
+
if ((updatedAt + chainlinkTimeout) < block.timestamp) revert ChainlinkOracleOutdated(_oracle);
|
59
|
+
|
60
|
+
return ChainlinkAnswer(roundId, answer, startedAt, updatedAt, answeredInRound);
|
61
|
+
} catch {
|
62
|
+
revert ChainlinkOracleIsDown(_oracle);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
function _chainlinkLatestAnswer(address _oracle) internal view returns (int256 answer) {
|
67
|
+
return _chainlinkLatestRoundData(_oracle).answer;
|
68
|
+
}
|
69
|
+
|
70
|
+
uint256[50] private __gap;
|
71
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
2
|
+
|
3
|
+
pragma solidity ^0.8.0;
|
4
|
+
|
5
|
+
interface IChainlinkAggregator {
|
6
|
+
function latestAnswer() external view returns (int256);
|
7
|
+
function latestRoundData()
|
8
|
+
external
|
9
|
+
view
|
10
|
+
returns (
|
11
|
+
uint80 roundId,
|
12
|
+
int256 answer,
|
13
|
+
uint256 startedAt,
|
14
|
+
uint256 updatedAt,
|
15
|
+
uint80 answeredInRound
|
16
|
+
);
|
17
|
+
function decimals() external view returns (uint8);
|
18
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
2
|
+
|
3
|
+
pragma solidity ^0.8.0;
|
4
|
+
|
5
|
+
interface IPriceOracle {
|
6
|
+
/**
|
7
|
+
* @notice Returns total cost of the pool in terms of value token
|
8
|
+
* @param ufarmPool - address of the pool
|
9
|
+
* @param valueToken - address of the token in which the cost is calculated
|
10
|
+
*/
|
11
|
+
function getTotalCostOfPool(
|
12
|
+
address ufarmPool,
|
13
|
+
address valueToken
|
14
|
+
) external view returns (uint256 totalCost);
|
15
|
+
|
16
|
+
/**
|
17
|
+
* @notice Returns cost of the token in terms of value token
|
18
|
+
* @param tokenIn - address of the token which cost will be calculated
|
19
|
+
* @param amountIn - amount of the token which cost will be calculated
|
20
|
+
* @param tokenOut - address of the token in which the cost is calculated
|
21
|
+
*/
|
22
|
+
function getCostERC20(
|
23
|
+
address tokenIn,
|
24
|
+
uint256 amountIn,
|
25
|
+
address tokenOut
|
26
|
+
) external view returns (uint256 cost);
|
27
|
+
|
28
|
+
/**
|
29
|
+
* @notice Returns cost of the token in terms of value token
|
30
|
+
* @param tokenIn - address of the token which cost will be calculated
|
31
|
+
* @param amountIn - amount of the token which cost will be calculated
|
32
|
+
* @param tokenOut - address of the token in which the cost is calculated
|
33
|
+
* @param controller - address of the controller, which will be used to calculate the cost
|
34
|
+
*/
|
35
|
+
function getCostControlledERC20(
|
36
|
+
address tokenIn,
|
37
|
+
uint256 amountIn,
|
38
|
+
address tokenOut,
|
39
|
+
address controller
|
40
|
+
) external view returns (uint256 cost);
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @notice Returns cost of the token in terms of value token
|
44
|
+
* @param tokenIn - address of the token which cost will be calculated
|
45
|
+
* @param ids - ids of the token which cost will be calculated
|
46
|
+
* @param tokenOut - address of the token in which the cost is calculated
|
47
|
+
* @param controller - address of the controller, which will be used to calculate the cost
|
48
|
+
*/
|
49
|
+
function getCostERC721(
|
50
|
+
address tokenIn,
|
51
|
+
uint256[] memory ids,
|
52
|
+
address tokenOut,
|
53
|
+
address controller
|
54
|
+
) external view returns (uint256 cost);
|
55
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
2
|
+
|
3
|
+
pragma solidity ^0.8.0;
|
4
|
+
|
5
|
+
import {PriceOracleCore} from './PriceOracleCore.sol';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* @title PriceOracle
|
9
|
+
* @author https://ufarm.digital/
|
10
|
+
* @notice Calculates the cost of the pool and tokens
|
11
|
+
*/
|
12
|
+
contract PriceOracle is PriceOracleCore {
|
13
|
+
/**
|
14
|
+
* @notice Initializes the contract from deployer. Sets link to UFarmCore
|
15
|
+
* @param ufarmCoreLink - address of the core contract
|
16
|
+
*/
|
17
|
+
function __init__PriceOracle(address ufarmCoreLink) external onlyDeployer initializer {
|
18
|
+
__init__PriceOracleCore(ufarmCoreLink);
|
19
|
+
}
|
20
|
+
}
|