@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,82 @@
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
2
|
+
|
3
|
+
import { expect } from 'chai'
|
4
|
+
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'
|
5
|
+
import {
|
6
|
+
constants,
|
7
|
+
_signDepositRequest,
|
8
|
+
_signWithdrawRequest,
|
9
|
+
convertDecimals,
|
10
|
+
} from './_helpers'
|
11
|
+
import {
|
12
|
+
UFarmFundFixture,
|
13
|
+
_poolSwapUniV2,
|
14
|
+
MockedAggregatorFixture,
|
15
|
+
} from './_fixtures'
|
16
|
+
import {
|
17
|
+
_BNsqrt,
|
18
|
+
} from './_helpers'
|
19
|
+
|
20
|
+
describe('PriceOracle', async () => {
|
21
|
+
it('Mocked aggregator test', async () => {
|
22
|
+
const { feedInstancesTokenToUSDT } = await loadFixture(MockedAggregatorFixture)
|
23
|
+
|
24
|
+
const daiPrice = await feedInstancesTokenToUSDT.DAI.latestAnswer()
|
25
|
+
const daiFeedDecimals = await feedInstancesTokenToUSDT.DAI.decimals()
|
26
|
+
expect(daiPrice).to.be.lessThanOrEqual(
|
27
|
+
(10n ** BigInt(daiFeedDecimals) * 105n) / 100n,
|
28
|
+
'DAI price is too high',
|
29
|
+
)
|
30
|
+
expect(daiPrice).to.be.greaterThanOrEqual(
|
31
|
+
(10n ** BigInt(daiFeedDecimals) * 95n) / 100n,
|
32
|
+
'DAI price is too low',
|
33
|
+
)
|
34
|
+
|
35
|
+
const wethPrice = await feedInstancesTokenToUSDT.WETH.latestAnswer()
|
36
|
+
const wethFeedDecimals = await feedInstancesTokenToUSDT.WETH.decimals()
|
37
|
+
expect(wethPrice).to.be.lessThanOrEqual(
|
38
|
+
(1800n * 10n ** BigInt(wethFeedDecimals) * 105n) / 100n,
|
39
|
+
'WETH price is too high',
|
40
|
+
)
|
41
|
+
expect(wethPrice).to.be.greaterThanOrEqual(
|
42
|
+
(1800n * 10n ** BigInt(wethFeedDecimals) * 95n) / 100n,
|
43
|
+
'WETH price is too low',
|
44
|
+
)
|
45
|
+
})
|
46
|
+
it('Price oracle getCostERC20() function test', async () => {
|
47
|
+
const { feedInstancesTokenToUSDT, tokens, PriceOracle_instance } = await loadFixture(
|
48
|
+
UFarmFundFixture,
|
49
|
+
)
|
50
|
+
const [daiPrice, daiFeedDecimals, usdtDecimals] = await Promise.all([
|
51
|
+
feedInstancesTokenToUSDT.DAI.latestAnswer(),
|
52
|
+
feedInstancesTokenToUSDT.DAI.decimals(),
|
53
|
+
tokens.USDT.decimals(),
|
54
|
+
])
|
55
|
+
const daiCost = await PriceOracle_instance.getCostERC20(
|
56
|
+
tokens.DAI.address,
|
57
|
+
constants.ONE,
|
58
|
+
tokens.USDT.address,
|
59
|
+
)
|
60
|
+
expect(daiCost).to.eq(
|
61
|
+
convertDecimals(daiPrice, daiFeedDecimals, usdtDecimals),
|
62
|
+
'DAI cost should be equal to price for 1 DAI in USDT',
|
63
|
+
)
|
64
|
+
|
65
|
+
const [wethPrice, wethFeedDecimals] = await Promise.all([
|
66
|
+
feedInstancesTokenToUSDT.WETH.latestAnswer(),
|
67
|
+
feedInstancesTokenToUSDT.WETH.decimals(),
|
68
|
+
])
|
69
|
+
const wethCost = await PriceOracle_instance.getCostERC20(
|
70
|
+
tokens.WETH.address,
|
71
|
+
constants.ONE,
|
72
|
+
tokens.USDT.address,
|
73
|
+
)
|
74
|
+
expect(wethCost).to.eq(
|
75
|
+
convertDecimals(wethPrice, wethFeedDecimals, usdtDecimals),
|
76
|
+
'WETH cost should be equal to price for 1 WETH in USDT',
|
77
|
+
)
|
78
|
+
})
|
79
|
+
it("Should check wsteth/eth oracle calculations", async () => {
|
80
|
+
|
81
|
+
})
|
82
|
+
})
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Another tests of Fees
|
2
|
+
|
3
|
+
## Test Case: All Fees are Calculated Correctly During Withdrawal, Final HWM is zero
|
4
|
+
|
5
|
+
1. Bob deposits 2000 USDT to the pool.
|
6
|
+
2. Manager exchanges 1000 USDT to ETH.
|
7
|
+
3. 30 days passed.
|
8
|
+
4. The exchange rate of ETH bumps by 1/3.
|
9
|
+
5. The pool calculates fees as:
|
10
|
+
1. Total cost should be equal to 1000 USDT + UniswapCostOfETHinPool() = 1000 USDT + 1000 USDT \* 4/3 = 2333.33 USDT
|
11
|
+
2. Initial HWM should be equal to 2000 USDT
|
12
|
+
3. Performance fee = Profit \* Performance commission rate
|
13
|
+
(~2300 - 2000) \* 10% = 30 USDT at the time of withdrawal.
|
14
|
+
4. Management fee = Annual Fee Period \* Management Commission \* Assets Under Management = (30 days / 365 days) \* 2% \* 2000 USDT = 12 USDT
|
15
|
+
5. Protocol fee = Annual Fee Period \* Protocol Commission \* Assets Under Management = (30 days / 365 days) \* 3% \* 2000 USDT = 18 USDT
|
16
|
+
6. HWM should be equal to 0
|
17
|
+
|
18
|
+
## Test Case: All Fees are Calculated Correctly During Deposit and HWM Decreases after Fee Calculation
|
19
|
+
|
20
|
+
1. Bob deposits 2000 USDT to the pool.
|
21
|
+
2. Manager exchanges 1000 USDT to ETH.
|
22
|
+
3. The exchange rate of ETH bumps by 1/3.
|
23
|
+
4. 12 hours passed.
|
24
|
+
4. The pool calculates fees as:
|
25
|
+
1. Management Fee = Total cost after changing rate * Management Commission * Fee Period
|
26
|
+
2. Protocol Fee = Total cost after changing rate * Protocol Commission * Fee Period
|
27
|
+
3. Performance Fee = Profit * Performance commission rate
|
28
|
+
|
29
|
+
## Test Case: All Fees are Calculated Correctly During Pool Initialization
|
30
|
+
|
31
|
+
1. Fund deposits 4000 USDT to the pool.
|
32
|
+
2. Manager exchanges 2000 USDT to ETH.
|
33
|
+
3. The exchange rate of ETH bumps by 100%.
|
34
|
+
4. 1 min passed.
|
35
|
+
5. The pool calculates fees as:
|
36
|
+
1. Management Fee = Total cost after changing rate \* Management Commission \* Fee Period
|
37
|
+
2. Protocol Fee = Total cost after changing rate \* Protocol Commission \* Fee Period
|
38
|
+
3. Performance Fee = Profit \* Performance commission rate
|
39
|
+
|
40
|
+
## Test Case: Total Fee is Distributed Correctly Between the Fund and the UFarm
|
41
|
+
|
42
|
+
1. Bob deposits 2000 USDT to the pool.
|
43
|
+
2. Manager exchanges 1000 USDT to ETH.
|
44
|
+
3. The exchange rate of ETH bumps by 1/3, 30 days passed.
|
45
|
+
4. The pool calculates the total fees as:
|
46
|
+
1. Protocol Fee = Fee Period \* Protocol Commission \* Assets Under Management
|
47
|
+
2. Management Fee = Fee Period \* Management Commission \* Assets Under Management
|
48
|
+
3. Performance Fee = Management Commission \* Profit Above HWM
|
49
|
+
5. UFarm Fee should be equal to the sum of the Protocol Fee and 20% of the sum of the Management Fee and Performance Fee:
|
50
|
+
1. Total UFarm Fee = Protocol Fee + 20% \* (Management Fee + Performance Fee)
|
51
|
+
6. Fund Fee should be equal to 80% of the sum of the Management Fee and Performance Fee:
|
52
|
+
1. Total Fund Fee = 80% \* (Management Fee + Performance Fee)
|
53
|
+
|
54
|
+
## Test Case: Performance Fee is Not Calculated When There is No Profit
|
55
|
+
|
56
|
+
1. Bob deposits 10000 USDT to the pool.
|
57
|
+
2. Manager exchanges 5000 USDT to ETH.
|
58
|
+
3. The exchange rate of ETH dumps to 1/2.
|
59
|
+
4. Alice deposits 2500 USDT to the pool and no performance fee is calculated.
|
60
|
+
|
61
|
+
## Test Case: Protocol Fee and Management Fee are Calculated When There is No Change in the Pool Exchange Rate
|
62
|
+
|
63
|
+
1. Bob deposits 1000 USDT to the pool.
|
64
|
+
2. The exchange rate of ETH does not change. 180 days passed.
|
65
|
+
3. The pool calculates the protocol fee as:
|
66
|
+
1. Protocol Fee = Fee Period \* Protocol Commission \* Assets Under Management
|
67
|
+
2. 180/365 \* 0.03 \* 1000 ~= 14.79 USDT
|
68
|
+
4. The pool calculates the management fee as:
|
69
|
+
1. Management Fee = Fee Period \* Management Commission \* Assets Under Management
|
70
|
+
2. 180/365 \* 0.02 \* 1000 ~= 10.96 USDT
|
71
|
+
|
72
|
+
## Test Case: Management and Protocol Fees Can't Be More Than Pool Balance
|
73
|
+
|
74
|
+
1. Bob deposits 1000 USDT to the pool.
|
75
|
+
2. Manager exchanges 500 USDT to ETH.
|
76
|
+
3. The exchange rate of ETH changes to x10.
|
77
|
+
4. Protocol fee is always 0.3% of the pool balance per year. Management fee is 4.7% of the pool balance per year. Performance fee is 10% of the profit.
|
78
|
+
5. 21 years passed. Total annual rate is 5%, so it is 100% for 20 years.
|
79
|
+
1. Total cost ~= 500 + 500 \* 10 = 5500 USDT
|
80
|
+
2. Performance fee ~= 5500 \* 10% = 550 USDT
|
81
|
+
6. The pool calculates the management fee as:
|
82
|
+
1. Management Fee = Fee Period _ Management Commission _ Assets Under Management
|
83
|
+
2. 21 _ 0.047 _ 5500 = 5428.5 USDT
|
84
|
+
7. The pool calculates the protocol fee as:
|
85
|
+
1. Protocol Fee = Fee Period _ Protocol Commission _ Assets Under Management
|
86
|
+
2. 21 _ 0.003 _ 5500 = 346.5 USDT
|
87
|
+
|
88
|
+
## Test Case: Full Withdrawal After 1 Year of Pool's Existence
|
89
|
+
|
90
|
+
1. Bob deposits 1000 USDT to the pool.
|
91
|
+
2. Manager exchanges 500 USDT to ETH.
|
92
|
+
3. 1 year passed.
|
93
|
+
4. Bob withdraws all his funds. Fees are calculated as:
|
94
|
+
1. Protocol Fee = Fee Period _ Protocol Commission _ Assets Under Management
|
95
|
+
2. Management Fee = Fee Period _ Management Commission _ Assets Under Management
|
96
|
+
3. Performance Fee = Management Commission \* Profit Above HWM
|
97
|
+
4. Total UFarm Fee = Protocol Fee + 20% \* (Management Fee + Performance Fee)
|
98
|
+
5. Total Fund Fee = 80% \* (Management Fee + Performance Fee)
|
99
|
+
6. Total Fee = Total UFarm Fee + Total Fund Fee
|
100
|
+
7. Bob receives 1000 - feesValue USDT
|
101
|
+
|
102
|
+
## Test Case: Performance Fee is Calculated Correctly During Deposit
|
103
|
+
|
104
|
+
1. Bob deposits 1000 USDT to the pool that splits 50% to ETH and 50% to USDT.
|
105
|
+
2. Manager exchanges 500 USDT to ETH.
|
106
|
+
3. The exchange rate of ETH bumps by 1/3.
|
107
|
+
4. The pool calculates the performance fee as:
|
108
|
+
1. Performance fee = Profit \* Performance commission rate
|
109
|
+
2. (1300 - 1000) \* 10% = 30 USDT at the time of deposit.
|
@@ -0,0 +1,331 @@
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
2
|
+
|
3
|
+
import { ethers } from 'hardhat'
|
4
|
+
import { expect } from 'chai'
|
5
|
+
import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'
|
6
|
+
import { time, loadFixture, takeSnapshot } from '@nomicfoundation/hardhat-network-helpers'
|
7
|
+
import { UFarmFund } from '../typechain-types/'
|
8
|
+
|
9
|
+
import { UFarmCoreFixture } from './_fixtures'
|
10
|
+
|
11
|
+
import {
|
12
|
+
protocolToBytes32,
|
13
|
+
constants,
|
14
|
+
getEventFromTx,
|
15
|
+
AssetWithPriceFeed,
|
16
|
+
FeedWithDecimal,
|
17
|
+
} from './_helpers'
|
18
|
+
import { bitsToBigNumber } from './_helpers'
|
19
|
+
|
20
|
+
describe('UFarmCore test', function () {
|
21
|
+
describe('Basic tests', function () {
|
22
|
+
it('Should create fund', async function () {
|
23
|
+
const { UFarmCore_instance, deployer, alice, bob } = await loadFixture(UFarmCoreFixture)
|
24
|
+
|
25
|
+
const firstFundId = 0
|
26
|
+
const applicationId = ethers.utils.randomBytes(32)
|
27
|
+
|
28
|
+
expect(await UFarmCore_instance.connect(deployer).createFund(deployer.address, applicationId))
|
29
|
+
.to.emit(UFarmCore_instance, 'FundCreated')
|
30
|
+
.withArgs(applicationId, firstFundId, anyValue)
|
31
|
+
|
32
|
+
const address_UFarmFund = await UFarmCore_instance.getFund(firstFundId)
|
33
|
+
|
34
|
+
const UFarmFund_instance: UFarmFund = await ethers.getContractAt(
|
35
|
+
'UFarmFund',
|
36
|
+
address_UFarmFund,
|
37
|
+
alice,
|
38
|
+
)
|
39
|
+
|
40
|
+
expect(await UFarmFund_instance.name()).to.equal('UFarmFund')
|
41
|
+
})
|
42
|
+
|
43
|
+
it('Should create the same fund with same applicationId', async function () {
|
44
|
+
const { UFarmCore_instance, FundFactory_instance, deployer, alice, bob } = await loadFixture(
|
45
|
+
UFarmCoreFixture,
|
46
|
+
)
|
47
|
+
|
48
|
+
const applications = [
|
49
|
+
{
|
50
|
+
id: ethers.utils.randomBytes(32),
|
51
|
+
admin: alice.address,
|
52
|
+
},
|
53
|
+
{
|
54
|
+
id: ethers.utils.randomBytes(32),
|
55
|
+
admin: deployer.address,
|
56
|
+
},
|
57
|
+
{
|
58
|
+
id: ethers.utils.randomBytes(32),
|
59
|
+
admin: bob.address,
|
60
|
+
},
|
61
|
+
]
|
62
|
+
const reversedApplications = applications.reverse()
|
63
|
+
|
64
|
+
const cleanSnapshot = await takeSnapshot()
|
65
|
+
|
66
|
+
const funds1 = []
|
67
|
+
|
68
|
+
for (const application of applications) {
|
69
|
+
const fundAddr = await FundFactory_instance.getFundBySalt(application.admin, application.id)
|
70
|
+
|
71
|
+
expect(
|
72
|
+
await UFarmCore_instance.connect(deployer).createFund(application.admin, application.id),
|
73
|
+
)
|
74
|
+
.to.emit(UFarmCore_instance, 'FundCreated')
|
75
|
+
.withArgs(application.id, anyValue, fundAddr)
|
76
|
+
|
77
|
+
funds1.push(fundAddr)
|
78
|
+
}
|
79
|
+
|
80
|
+
await cleanSnapshot.restore()
|
81
|
+
|
82
|
+
const funds2 = []
|
83
|
+
|
84
|
+
for (const application of reversedApplications) {
|
85
|
+
const fundAddr = await FundFactory_instance.getFundBySalt(application.admin, application.id)
|
86
|
+
|
87
|
+
expect(
|
88
|
+
await UFarmCore_instance.connect(deployer).createFund(application.admin, application.id),
|
89
|
+
)
|
90
|
+
.to.emit(UFarmCore_instance, 'FundCreated')
|
91
|
+
.withArgs(application.id, anyValue, fundAddr)
|
92
|
+
|
93
|
+
funds2.push(fundAddr)
|
94
|
+
}
|
95
|
+
expect(funds1).to.deep.equal(funds2, 'Funds are not the same')
|
96
|
+
})
|
97
|
+
})
|
98
|
+
describe('UFarm Permission tests', function () {
|
99
|
+
it('Should deploy contract and grant permissions to the admin', async function () {
|
100
|
+
const { UFarmCore_instance, deployer, alice, bob } = await loadFixture(UFarmCoreFixture)
|
101
|
+
|
102
|
+
for (const [key, value] of Object.entries(constants.UFarm.Permissions)) {
|
103
|
+
expect(await UFarmCore_instance.hasPermission(deployer.address, value)).to.equal(
|
104
|
+
true,
|
105
|
+
`Deployer doesn't have permission ${key}`,
|
106
|
+
)
|
107
|
+
}
|
108
|
+
})
|
109
|
+
it('Should grant UFarm owner', async function () {
|
110
|
+
const { UFarmCore_instance, deployer, alice, bob } = await loadFixture(UFarmCoreFixture)
|
111
|
+
|
112
|
+
expect(await UFarmCore_instance.hasPermission(alice.address, 2)).to.equal(
|
113
|
+
false,
|
114
|
+
'Alice is already UFarm owner',
|
115
|
+
)
|
116
|
+
|
117
|
+
const ownerMask = bitsToBigNumber([constants.UFarm.Permissions.Owner])
|
118
|
+
|
119
|
+
await expect(UFarmCore_instance.connect(deployer).updatePermissions(alice.address, ownerMask))
|
120
|
+
.to.emit(UFarmCore_instance, 'PermissionsUpdated')
|
121
|
+
.withArgs(alice.address, ownerMask)
|
122
|
+
|
123
|
+
expect(
|
124
|
+
await UFarmCore_instance.hasPermission(alice.address, constants.UFarm.Permissions.Owner),
|
125
|
+
).to.equal(true, 'Alice is not UFarm owner')
|
126
|
+
})
|
127
|
+
it('Should grant and remove UFarm permission', async function () {
|
128
|
+
const { UFarmCore_instance, deployer, alice, bob } = await loadFixture(UFarmCoreFixture)
|
129
|
+
|
130
|
+
expect(
|
131
|
+
await UFarmCore_instance.hasPermission(alice.address, constants.UFarm.Permissions.Member),
|
132
|
+
).to.equal(false, 'Alice is already UFarm member')
|
133
|
+
expect(
|
134
|
+
await UFarmCore_instance.hasPermission(
|
135
|
+
alice.address,
|
136
|
+
constants.UFarm.Permissions.UpdatePermissions,
|
137
|
+
),
|
138
|
+
).to.equal(false, 'Alice already has UpdatePermissions permission')
|
139
|
+
|
140
|
+
const memberBlockFundMask = bitsToBigNumber([
|
141
|
+
constants.UFarm.Permissions.Member,
|
142
|
+
constants.UFarm.Permissions.BlockFund,
|
143
|
+
])
|
144
|
+
|
145
|
+
await expect(
|
146
|
+
UFarmCore_instance.connect(deployer).updatePermissions(alice.address, memberBlockFundMask),
|
147
|
+
)
|
148
|
+
.to.emit(UFarmCore_instance, 'PermissionsUpdated')
|
149
|
+
.withArgs(alice.address, memberBlockFundMask)
|
150
|
+
|
151
|
+
expect(
|
152
|
+
await UFarmCore_instance.hasPermission(alice.address, constants.UFarm.Permissions.Member),
|
153
|
+
).to.equal(true, 'Alice is not UFarm member')
|
154
|
+
expect(
|
155
|
+
await UFarmCore_instance.hasPermission(
|
156
|
+
alice.address,
|
157
|
+
constants.UFarm.Permissions.BlockFund,
|
158
|
+
),
|
159
|
+
).to.equal(true, 'Alice does not have BlockFund permission')
|
160
|
+
|
161
|
+
const multiMask = bitsToBigNumber([
|
162
|
+
constants.UFarm.Permissions.Member,
|
163
|
+
constants.UFarm.Permissions.UpdateUFarmMember,
|
164
|
+
constants.UFarm.Permissions.TurnPauseOn,
|
165
|
+
])
|
166
|
+
|
167
|
+
await expect(UFarmCore_instance.connect(deployer).updatePermissions(alice.address, multiMask))
|
168
|
+
.to.emit(UFarmCore_instance, 'PermissionsUpdated')
|
169
|
+
.withArgs(alice.address, multiMask)
|
170
|
+
|
171
|
+
expect(
|
172
|
+
await UFarmCore_instance.hasPermission(alice.address, constants.UFarm.Permissions.Member),
|
173
|
+
).to.equal(true)
|
174
|
+
expect(
|
175
|
+
await UFarmCore_instance.hasPermission(
|
176
|
+
alice.address,
|
177
|
+
constants.UFarm.Permissions.BlockFund,
|
178
|
+
),
|
179
|
+
).to.equal(false)
|
180
|
+
expect(
|
181
|
+
await UFarmCore_instance.hasPermission(
|
182
|
+
alice.address,
|
183
|
+
constants.UFarm.Permissions.TurnPauseOn,
|
184
|
+
),
|
185
|
+
).to.equal(true)
|
186
|
+
})
|
187
|
+
it('Should deploy with one owner', async function () {
|
188
|
+
const { UFarmCore_instance, deployer, alice, bob } = await loadFixture(UFarmCoreFixture)
|
189
|
+
|
190
|
+
expect(
|
191
|
+
await UFarmCore_instance.hasPermission(deployer.address, constants.UFarm.Permissions.Owner),
|
192
|
+
).to.equal(true)
|
193
|
+
expect(
|
194
|
+
await UFarmCore_instance.hasPermission(bob.address, constants.UFarm.Permissions.Owner),
|
195
|
+
).to.equal(false)
|
196
|
+
})
|
197
|
+
it('Only owner can grant ownership', async function () {
|
198
|
+
const { UFarmCore_instance, deployer, alice, bob } = await loadFixture(UFarmCoreFixture)
|
199
|
+
|
200
|
+
// Bob is not member
|
201
|
+
expect(
|
202
|
+
await UFarmCore_instance.hasPermission(bob.address, constants.UFarm.Permissions.Owner),
|
203
|
+
).to.equal(false, 'Bob is not UFarm owner')
|
204
|
+
|
205
|
+
// Bob can't grant ownership to anyone
|
206
|
+
const ownerMask = bitsToBigNumber([constants.UFarm.Permissions.Owner])
|
207
|
+
|
208
|
+
await expect(
|
209
|
+
UFarmCore_instance.connect(bob).updatePermissions(alice.address, ownerMask),
|
210
|
+
).to.be.revertedWithCustomError(UFarmCore_instance, 'NonAuthorized')
|
211
|
+
await expect(
|
212
|
+
UFarmCore_instance.connect(bob).updatePermissions(bob.address, ownerMask),
|
213
|
+
).to.be.revertedWithCustomError(UFarmCore_instance, 'NonAuthorized')
|
214
|
+
|
215
|
+
// Grant member role to Bob
|
216
|
+
await UFarmCore_instance.updatePermissions(
|
217
|
+
bob.address,
|
218
|
+
bitsToBigNumber([constants.UFarm.Permissions.Member]),
|
219
|
+
)
|
220
|
+
|
221
|
+
// Bob is member
|
222
|
+
await expect(
|
223
|
+
UFarmCore_instance.connect(bob).updatePermissions(alice.address, ownerMask),
|
224
|
+
).to.be.revertedWithCustomError(UFarmCore_instance, 'NonAuthorized')
|
225
|
+
await expect(
|
226
|
+
UFarmCore_instance.connect(bob).updatePermissions(bob.address, ownerMask),
|
227
|
+
).to.be.revertedWithCustomError(UFarmCore_instance, 'NonAuthorized')
|
228
|
+
|
229
|
+
// Grant owner role to Bob
|
230
|
+
await UFarmCore_instance.updatePermissions(bob.address, ownerMask)
|
231
|
+
|
232
|
+
await expect(UFarmCore_instance.connect(bob).updatePermissions(alice.address, ownerMask)).to
|
233
|
+
.be.not.reverted
|
234
|
+
})
|
235
|
+
})
|
236
|
+
|
237
|
+
describe('Token whitelist tests', function () {
|
238
|
+
function convertToEventLogFormat(
|
239
|
+
asset: AssetWithPriceFeed,
|
240
|
+
): (string | number | FeedWithDecimal)[] & { assetInfo: AssetWithPriceFeed } {
|
241
|
+
// Construct the priceFeed array with both indexed and named properties
|
242
|
+
const priceFeedArray = [asset.priceFeed.feedAddr, asset.priceFeed.feedDec] as (
|
243
|
+
| string
|
244
|
+
| number
|
245
|
+
)[] &
|
246
|
+
FeedWithDecimal
|
247
|
+
priceFeedArray.feedAddr = asset.priceFeed.feedAddr
|
248
|
+
priceFeedArray.feedDec = asset.priceFeed.feedDec
|
249
|
+
|
250
|
+
// Construct the result array with both indexed and named properties
|
251
|
+
const resultArray = [asset.assetAddr, asset.assetDec, priceFeedArray] as (
|
252
|
+
| string
|
253
|
+
| number
|
254
|
+
| typeof priceFeedArray
|
255
|
+
)[] &
|
256
|
+
AssetWithPriceFeed
|
257
|
+
resultArray.assetAddr = asset.assetAddr
|
258
|
+
resultArray.assetDec = asset.assetDec
|
259
|
+
resultArray.priceFeed = priceFeedArray
|
260
|
+
|
261
|
+
const finalOutput = [resultArray] as unknown as (string | number | FeedWithDecimal)[] & {
|
262
|
+
assetInfo: AssetWithPriceFeed
|
263
|
+
}
|
264
|
+
finalOutput.assetInfo = resultArray
|
265
|
+
|
266
|
+
return finalOutput
|
267
|
+
}
|
268
|
+
|
269
|
+
it('Should add tokens to whitelist with event', async function () {
|
270
|
+
const { UFarmCore_instance, tokenFeeds } = await loadFixture(UFarmCoreFixture)
|
271
|
+
|
272
|
+
const feed0 = tokenFeeds[0]
|
273
|
+
const feed1 = tokenFeeds[1]
|
274
|
+
const feed2 = tokenFeeds[2]
|
275
|
+
|
276
|
+
const thisBlock = await ethers.provider.getBlock('latest')
|
277
|
+
|
278
|
+
await UFarmCore_instance.whitelistTokens([feed0])
|
279
|
+
await UFarmCore_instance.whitelistTokens([feed1, feed2])
|
280
|
+
|
281
|
+
const tokenAddedFilter = UFarmCore_instance.filters.TokenAdded()
|
282
|
+
const events = await UFarmCore_instance.queryFilter(tokenAddedFilter, thisBlock.number)
|
283
|
+
|
284
|
+
for (let i = 0; i < events.length; i++) {
|
285
|
+
expect(events[i].args).to.deep.equal(convertToEventLogFormat(tokenFeeds[i]))
|
286
|
+
}
|
287
|
+
})
|
288
|
+
it('Should remove token from whitelist', async function () {
|
289
|
+
const { UFarmCore_instance, tokenFeeds } = await loadFixture(UFarmCoreFixture)
|
290
|
+
|
291
|
+
const feed0 = tokenFeeds[0]
|
292
|
+
|
293
|
+
await UFarmCore_instance.whitelistTokens([feed0])
|
294
|
+
|
295
|
+
await expect(UFarmCore_instance.blacklistTokens([feed0.assetAddr]))
|
296
|
+
.to.emit(UFarmCore_instance, 'TokenRemoved')
|
297
|
+
.withArgs(feed0.assetAddr)
|
298
|
+
})
|
299
|
+
})
|
300
|
+
describe('Protocol Whitelist tests', () => {
|
301
|
+
it('Should add protocol to whitelist', async () => {
|
302
|
+
const { UFarmCore_instance, deployer, alice, bob } = await loadFixture(UFarmCoreFixture)
|
303
|
+
|
304
|
+
const protocolName = protocolToBytes32('protocolName')
|
305
|
+
|
306
|
+
expect(await UFarmCore_instance.isProtocolWhitelisted(protocolName)).to.equal(false)
|
307
|
+
|
308
|
+
await expect(
|
309
|
+
UFarmCore_instance.whitelistProtocolsWithControllers([protocolName], [deployer.address]),
|
310
|
+
)
|
311
|
+
.to.emit(UFarmCore_instance, 'ProtocolAdded')
|
312
|
+
.withArgs(protocolName, deployer.address)
|
313
|
+
|
314
|
+
expect(await UFarmCore_instance.isProtocolWhitelisted(protocolName)).to.equal(true)
|
315
|
+
})
|
316
|
+
})
|
317
|
+
|
318
|
+
describe("Parameters' tests", function () {
|
319
|
+
it('Should change minimumFundDeposit', async function () {
|
320
|
+
const { UFarmCore_instance } = await loadFixture(UFarmCoreFixture)
|
321
|
+
|
322
|
+
expect(await UFarmCore_instance.minimumFundDeposit()).to.equal(0)
|
323
|
+
|
324
|
+
await expect(UFarmCore_instance.setMinimumFundDeposit(100))
|
325
|
+
.to.emit(UFarmCore_instance, 'MinimumFundDepositChanged')
|
326
|
+
.withArgs(100)
|
327
|
+
|
328
|
+
expect(await UFarmCore_instance.minimumFundDeposit()).to.equal(100)
|
329
|
+
})
|
330
|
+
})
|
331
|
+
})
|