@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,406 @@
|
|
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 } from '@nomicfoundation/hardhat-network-helpers'
|
7
|
+
import { UFarmFund } from '../typechain-types/'
|
8
|
+
import { BigNumber } from 'ethers'
|
9
|
+
import { UFarmCoreFixture, UFarmFundFixture, fundWithPoolFixture } from './_fixtures'
|
10
|
+
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
|
11
|
+
|
12
|
+
import {
|
13
|
+
deployPool,
|
14
|
+
constants,
|
15
|
+
bitsToBigNumber,
|
16
|
+
_prepareInvite,
|
17
|
+
_signWithdrawRequest,
|
18
|
+
WithdrawRequestStruct,
|
19
|
+
protocolToBytes32,
|
20
|
+
} from './_helpers'
|
21
|
+
|
22
|
+
describe('UFarmFund', function () {
|
23
|
+
describe('Basic tests', function () {
|
24
|
+
it('Should activate fund', async function () {
|
25
|
+
const { UFarmFund_instance } = await loadFixture(UFarmFundFixture)
|
26
|
+
|
27
|
+
expect(await UFarmFund_instance.status()).to.equal(0) // 0 = Approved
|
28
|
+
|
29
|
+
await expect(UFarmFund_instance.changeStatus(1))
|
30
|
+
.to.emit(UFarmFund_instance, 'FundStatusChanged')
|
31
|
+
.withArgs(1)
|
32
|
+
})
|
33
|
+
it('Addresses should be correct', async function () {
|
34
|
+
const { UFarmCore_instance, deployer, UFarmFund_instance } = await loadFixture(
|
35
|
+
UFarmFundFixture,
|
36
|
+
)
|
37
|
+
|
38
|
+
expect(await UFarmFund_instance.ufarmCore()).to.equal(UFarmCore_instance.address)
|
39
|
+
// TODO: to be continued...
|
40
|
+
})
|
41
|
+
})
|
42
|
+
describe('Pools tests', function () {
|
43
|
+
const tempSalt = () => {
|
44
|
+
return ethers.utils.randomBytes(32)
|
45
|
+
}
|
46
|
+
it('Initial pool count should be 0', async function () {
|
47
|
+
const { UFarmFund_instance } = await loadFixture(UFarmFundFixture)
|
48
|
+
|
49
|
+
expect(await UFarmFund_instance.poolsCount()).to.equal(0)
|
50
|
+
})
|
51
|
+
it('Should create pool with event', async function () {
|
52
|
+
const { alice, UFarmFund_instance, deployer, UFarmCore_instance, poolArgs } =
|
53
|
+
await loadFixture(UFarmFundFixture)
|
54
|
+
|
55
|
+
const salt = tempSalt()
|
56
|
+
|
57
|
+
const [poolAddr, poolAdmin] = await UFarmFund_instance.connect(alice).callStatic.createPool(
|
58
|
+
poolArgs,
|
59
|
+
salt,
|
60
|
+
)
|
61
|
+
|
62
|
+
await expect(UFarmFund_instance.connect(alice).createPool(poolArgs, salt))
|
63
|
+
.to.emit(UFarmFund_instance, 'PoolCreated')
|
64
|
+
.withArgs(
|
65
|
+
'UFarm-'.concat(await poolArgs.name),
|
66
|
+
'UF-'.concat(await poolArgs.symbol),
|
67
|
+
poolArgs.minInvestment,
|
68
|
+
poolArgs.maxInvestment,
|
69
|
+
poolArgs.managementCommission,
|
70
|
+
poolArgs.packedPerformanceCommission,
|
71
|
+
poolArgs.withdrawalLockupPeriod,
|
72
|
+
0,
|
73
|
+
poolAddr,
|
74
|
+
poolAdmin,
|
75
|
+
)
|
76
|
+
})
|
77
|
+
it('Should create pool with precalculated address', async function () {
|
78
|
+
const { alice, UFarmFund_instance, deployer, PoolFactory_instance, poolArgs } =
|
79
|
+
await loadFixture(UFarmFundFixture)
|
80
|
+
|
81
|
+
const salt1 = '0x759688d05783d51759ded786fb469c3fa8cb44f910f4ec3020d7c2dbf95baaaa'
|
82
|
+
const salt2 = '0x759688d05783d51759ded786fb469c3fa8cb44f910f4ec3020d7c2dbf95baaab'
|
83
|
+
|
84
|
+
const nextPoolId = await UFarmFund_instance.poolsCount()
|
85
|
+
|
86
|
+
const [poolAddr2_response, poolAdmin2_response] = await PoolFactory_instance.getPoolBySalt(salt2)
|
87
|
+
const [poolAddr_response, poolAdmin_response] = await PoolFactory_instance.getPoolBySalt(salt1)
|
88
|
+
|
89
|
+
await UFarmFund_instance.connect(alice).createPool(poolArgs, salt1)
|
90
|
+
await UFarmFund_instance.connect(alice).createPool(poolArgs, salt2)
|
91
|
+
|
92
|
+
const [poolAddr_actual, poolAdmin_actual] = await UFarmFund_instance.getPool(nextPoolId)
|
93
|
+
const [poolAddr2_actual, poolAdmin2_actual] = await UFarmFund_instance.getPool(nextPoolId.add(1))
|
94
|
+
|
95
|
+
expect(poolAddr_actual).to.eq(poolAddr_response, 'Response pool address should be correct')
|
96
|
+
expect(poolAdmin_actual).to.eq(poolAdmin_response, 'Response pool admin should be correct')
|
97
|
+
|
98
|
+
expect(poolAddr2_actual).to.eq(poolAddr2_response, 'Response pool address should be correct')
|
99
|
+
expect(poolAdmin2_actual).to.eq(poolAdmin2_response, 'Response pool admin should be correct')
|
100
|
+
})
|
101
|
+
it('Should return all pools addresses', async function () {
|
102
|
+
const { alice, UFarmFund_instance, deployer, UFarmCore_instance, poolArgs } =
|
103
|
+
await loadFixture(UFarmFundFixture)
|
104
|
+
|
105
|
+
const count = 10
|
106
|
+
|
107
|
+
for (let i = 0; i < count; i++) {
|
108
|
+
const tempSalt = ethers.utils.randomBytes(32)
|
109
|
+
await UFarmFund_instance.connect(alice).createPool(poolArgs, tempSalt)
|
110
|
+
}
|
111
|
+
|
112
|
+
expect(await UFarmFund_instance.poolsCount()).to.equal(count)
|
113
|
+
|
114
|
+
const poolsAddresses = await UFarmFund_instance.getPools()
|
115
|
+
|
116
|
+
expect(poolsAddresses.length).to.equal(count)
|
117
|
+
|
118
|
+
for (let i = 0; i < count; i++) {
|
119
|
+
expect(poolsAddresses[i]).to.deep.eq(await UFarmFund_instance.getPool(i))
|
120
|
+
}
|
121
|
+
})
|
122
|
+
it('Fund can create pools when its approved', async function () {
|
123
|
+
const { UFarmFund_instance, poolArgs } = await loadFixture(UFarmFundFixture)
|
124
|
+
|
125
|
+
expect(await UFarmFund_instance.status()).to.equal(0) // 0 = Approved
|
126
|
+
|
127
|
+
await expect(UFarmFund_instance.createPool(poolArgs, tempSalt())).to.be.not.reverted
|
128
|
+
})
|
129
|
+
it('Fund can create pools when its active', async function () {
|
130
|
+
const { UFarmFund_instance, poolArgs } = await loadFixture(UFarmFundFixture)
|
131
|
+
|
132
|
+
await UFarmFund_instance.changeStatus(1) // 1 = Active
|
133
|
+
|
134
|
+
expect(await UFarmFund_instance.status()).to.equal(1) // 1 = Active
|
135
|
+
|
136
|
+
await expect(UFarmFund_instance.createPool(poolArgs, tempSalt())).to.be.not.reverted
|
137
|
+
})
|
138
|
+
it('Fund can deposit and withdraw from pools when its approved', async function () {
|
139
|
+
const { UFarmFund_instance, poolArgs, tokens, alice } = await loadFixture(UFarmFundFixture)
|
140
|
+
|
141
|
+
expect(await UFarmFund_instance.status()).to.equal(0) // 0 = Approved
|
142
|
+
|
143
|
+
const newPool = await deployPool(poolArgs, UFarmFund_instance.connect(alice))
|
144
|
+
|
145
|
+
await tokens.USDT.mint(UFarmFund_instance.address, constants.ONE_HUNDRED_BUCKS.mul(2))
|
146
|
+
|
147
|
+
await expect(() =>
|
148
|
+
UFarmFund_instance.depositToPool(newPool.pool.address, constants.ONE_HUNDRED_BUCKS),
|
149
|
+
).to.changeTokenBalance(tokens.USDT, UFarmFund_instance, constants.ONE_HUNDRED_BUCKS.mul(-1))
|
150
|
+
|
151
|
+
const signedWithdrawalRequest = await _signWithdrawRequest(newPool.pool, alice, {
|
152
|
+
sharesToBurn: constants.ONE_HUNDRED_BUCKS,
|
153
|
+
salt: protocolToBytes32('anySalt'),
|
154
|
+
poolAddr: newPool.pool.address,
|
155
|
+
} as WithdrawRequestStruct)
|
156
|
+
|
157
|
+
await expect(() =>
|
158
|
+
UFarmFund_instance.withdrawFromPool({
|
159
|
+
body: signedWithdrawalRequest.msg,
|
160
|
+
signature: signedWithdrawalRequest.sig,
|
161
|
+
}),
|
162
|
+
).to.changeTokenBalance(tokens.USDT, UFarmFund_instance, constants.ONE_HUNDRED_BUCKS)
|
163
|
+
})
|
164
|
+
it('Fund can deposit and withdraw from pools when its active', async function () {
|
165
|
+
const { UFarmFund_instance, poolArgs, tokens, alice } = await loadFixture(UFarmFundFixture)
|
166
|
+
|
167
|
+
await UFarmFund_instance.changeStatus(1) // 1 = Active
|
168
|
+
|
169
|
+
expect(await UFarmFund_instance.status()).to.equal(1)
|
170
|
+
|
171
|
+
const newPool = await deployPool(poolArgs, UFarmFund_instance.connect(alice))
|
172
|
+
|
173
|
+
await tokens.USDT.mint(UFarmFund_instance.address, constants.ONE_HUNDRED_BUCKS.mul(2))
|
174
|
+
|
175
|
+
await expect(() =>
|
176
|
+
UFarmFund_instance.depositToPool(newPool.pool.address, constants.ONE_HUNDRED_BUCKS),
|
177
|
+
).to.changeTokenBalance(tokens.USDT, UFarmFund_instance, constants.ONE_HUNDRED_BUCKS.mul(-1))
|
178
|
+
|
179
|
+
const signedWithdrawalRequest = await _signWithdrawRequest(newPool.pool, alice, {
|
180
|
+
sharesToBurn: constants.ONE_HUNDRED_BUCKS,
|
181
|
+
salt: protocolToBytes32('anySalt'),
|
182
|
+
poolAddr: newPool.pool.address,
|
183
|
+
} as WithdrawRequestStruct)
|
184
|
+
|
185
|
+
await expect(() =>
|
186
|
+
UFarmFund_instance.withdrawFromPool({
|
187
|
+
body: signedWithdrawalRequest.msg,
|
188
|
+
signature: signedWithdrawalRequest.sig,
|
189
|
+
}),
|
190
|
+
).to.changeTokenBalance(tokens.USDT, UFarmFund_instance, constants.ONE_HUNDRED_BUCKS)
|
191
|
+
})
|
192
|
+
})
|
193
|
+
describe('Fund Permission tests', function () {
|
194
|
+
it('Fund member can receive new permissions via invitation', async function () {
|
195
|
+
const { UFarmFund_instance, alice, bob, wallet, carol } = await loadFixture(
|
196
|
+
fundWithPoolFixture,
|
197
|
+
)
|
198
|
+
|
199
|
+
const inviteeMask = bitsToBigNumber(
|
200
|
+
constants.Fund.Roles.MemberRole.concat(
|
201
|
+
constants.Fund.Roles.FundEditorRole,
|
202
|
+
constants.Fund.Roles.AllPoolsFinanceManagerRole,
|
203
|
+
constants.Fund.Roles.FundFinanceManagerRole,
|
204
|
+
),
|
205
|
+
)
|
206
|
+
|
207
|
+
const invite = await _prepareInvite(UFarmFund_instance, alice, {
|
208
|
+
invitee: bob.address,
|
209
|
+
permissionsMask: inviteeMask,
|
210
|
+
})
|
211
|
+
|
212
|
+
type FundInvite = {
|
213
|
+
invitation: {
|
214
|
+
deadline: number
|
215
|
+
invitee: string
|
216
|
+
permissionsMask: BigNumber
|
217
|
+
}
|
218
|
+
signature: string
|
219
|
+
}
|
220
|
+
|
221
|
+
const contractInvite: FundInvite = {
|
222
|
+
invitation: {
|
223
|
+
deadline: invite.msg.deadline,
|
224
|
+
invitee: invite.msg.invitee,
|
225
|
+
permissionsMask: invite.msg.permissionsMask,
|
226
|
+
},
|
227
|
+
signature: invite.sig,
|
228
|
+
}
|
229
|
+
|
230
|
+
expect(
|
231
|
+
await UFarmFund_instance.verifyInvitation(
|
232
|
+
contractInvite.invitation,
|
233
|
+
contractInvite.signature,
|
234
|
+
),
|
235
|
+
).to.be.not.reverted
|
236
|
+
|
237
|
+
const invitationResponse = await UFarmFund_instance.verifyInvitation(invite.msg, invite.sig)
|
238
|
+
|
239
|
+
expect(invitationResponse.inviter).to.eq(alice.address, 'Inviter should be correct')
|
240
|
+
expect(invitationResponse.msgHash).to.eq(invite.hash, 'MsgHash should be correct')
|
241
|
+
|
242
|
+
await expect(UFarmFund_instance.connect(bob).acceptInvitation(invite.msg, invite.sig))
|
243
|
+
.to.emit(UFarmFund_instance, 'InvitationAccepted')
|
244
|
+
.withArgs(alice.address, bob.address, invite.hash)
|
245
|
+
.to.emit(UFarmFund_instance, 'PermissionsUpdated')
|
246
|
+
.withArgs(bob.address, inviteeMask)
|
247
|
+
})
|
248
|
+
it(`Can't accept invitation with deadline in the past`, async function () {
|
249
|
+
const { UFarmFund_instance, alice, bob, wallet, carol } = await loadFixture(
|
250
|
+
fundWithPoolFixture,
|
251
|
+
)
|
252
|
+
|
253
|
+
const inviteeMask = bitsToBigNumber(
|
254
|
+
constants.Fund.Roles.MemberRole.concat(
|
255
|
+
constants.Fund.Roles.FundEditorRole,
|
256
|
+
constants.Fund.Roles.AllPoolsFinanceManagerRole,
|
257
|
+
),
|
258
|
+
)
|
259
|
+
|
260
|
+
const overdueDeadline = (await time.latest()) - time.duration.days(1)
|
261
|
+
|
262
|
+
const invite = await _prepareInvite(UFarmFund_instance, alice, {
|
263
|
+
invitee: bob.address,
|
264
|
+
permissionsMask: inviteeMask,
|
265
|
+
deadline: overdueDeadline,
|
266
|
+
})
|
267
|
+
|
268
|
+
await expect(UFarmFund_instance.connect(bob).acceptInvitation(invite.msg, invite.sig))
|
269
|
+
.to.be.revertedWithCustomError(UFarmFund_instance, 'InvitationExpired')
|
270
|
+
.withArgs(overdueDeadline, (await time.latest()) + 1)
|
271
|
+
})
|
272
|
+
it(`Current member can't accept invitation`, async function () {
|
273
|
+
const { UFarmFund_instance, alice, bob, wallet, carol } = await loadFixture(
|
274
|
+
fundWithPoolFixture,
|
275
|
+
)
|
276
|
+
|
277
|
+
const mask1 = bitsToBigNumber(
|
278
|
+
constants.Fund.Roles.MemberRole.concat(
|
279
|
+
constants.Fund.Roles.FundEditorRole,
|
280
|
+
constants.Fund.Roles.AllPoolsFinanceManagerRole,
|
281
|
+
),
|
282
|
+
)
|
283
|
+
|
284
|
+
const mask2 = bitsToBigNumber(
|
285
|
+
constants.Fund.Roles.MemberRole.concat(
|
286
|
+
constants.Fund.Roles.FundEditorRole,
|
287
|
+
constants.Fund.Roles.FundFinanceManagerRole,
|
288
|
+
),
|
289
|
+
)
|
290
|
+
|
291
|
+
const invite1 = await _prepareInvite(UFarmFund_instance, alice, {
|
292
|
+
invitee: carol.address,
|
293
|
+
permissionsMask: mask1,
|
294
|
+
})
|
295
|
+
|
296
|
+
const invite2 = await _prepareInvite(UFarmFund_instance, alice, {
|
297
|
+
invitee: carol.address,
|
298
|
+
permissionsMask: mask2,
|
299
|
+
})
|
300
|
+
|
301
|
+
await UFarmFund_instance.connect(carol).acceptInvitation(invite1.msg, invite1.sig)
|
302
|
+
|
303
|
+
await expect(
|
304
|
+
UFarmFund_instance.connect(carol).acceptInvitation(invite2.msg, invite2.sig),
|
305
|
+
).to.be.revertedWithCustomError(UFarmFund_instance, 'AlreadyMember')
|
306
|
+
})
|
307
|
+
|
308
|
+
it(`Invite can't be used twice`, async function () {
|
309
|
+
const { UFarmFund_instance, alice, bob, wallet, carol } = await loadFixture(
|
310
|
+
fundWithPoolFixture,
|
311
|
+
)
|
312
|
+
|
313
|
+
const inviteeMask = bitsToBigNumber(
|
314
|
+
constants.Fund.Roles.MemberRole.concat(
|
315
|
+
constants.Fund.Roles.FundEditorRole,
|
316
|
+
constants.Fund.Roles.AllPoolsFinanceManagerRole,
|
317
|
+
),
|
318
|
+
)
|
319
|
+
|
320
|
+
const invite = await _prepareInvite(UFarmFund_instance, alice, {
|
321
|
+
invitee: bob.address,
|
322
|
+
permissionsMask: inviteeMask,
|
323
|
+
})
|
324
|
+
|
325
|
+
// submit invitation
|
326
|
+
await UFarmFund_instance.connect(bob).acceptInvitation(invite.msg, invite.sig)
|
327
|
+
|
328
|
+
// revert permission update
|
329
|
+
await UFarmFund_instance.connect(alice).updatePermissions(bob.address, BigNumber.from(0))
|
330
|
+
|
331
|
+
// try to submit invitation again
|
332
|
+
await expect(
|
333
|
+
UFarmFund_instance.connect(bob).acceptInvitation(invite.msg, invite.sig),
|
334
|
+
).to.be.revertedWithCustomError(UFarmFund_instance, 'ActionAlreadyDone')
|
335
|
+
})
|
336
|
+
it("Many users can be owners, last owner can't be removed", async function () {
|
337
|
+
const { UFarmFund_instance, alice, bob, wallet, carol } = await loadFixture(
|
338
|
+
fundWithPoolFixture,
|
339
|
+
)
|
340
|
+
|
341
|
+
const ownerMask = bitsToBigNumber([constants.Fund.Permissions.Owner])
|
342
|
+
|
343
|
+
await expect(UFarmFund_instance.updatePermissions(bob.address, ownerMask))
|
344
|
+
.to.emit(UFarmFund_instance, 'PermissionsUpdated')
|
345
|
+
.withArgs(bob.address, ownerMask)
|
346
|
+
|
347
|
+
await expect(UFarmFund_instance.updatePermissions(wallet.address, ownerMask))
|
348
|
+
.to.emit(UFarmFund_instance, 'PermissionsUpdated')
|
349
|
+
.withArgs(wallet.address, ownerMask)
|
350
|
+
|
351
|
+
const permissionsArray = Array.from(Object.values(constants.Fund.Permissions))
|
352
|
+
const emptyPermissionsArray = Array.from({ length: permissionsArray.length }, () => 0)
|
353
|
+
const emptyPermissionsMask = bitsToBigNumber(emptyPermissionsArray)
|
354
|
+
|
355
|
+
await expect(
|
356
|
+
UFarmFund_instance.connect(carol).updatePermissions(carol.address, emptyPermissionsMask),
|
357
|
+
).to.be.revertedWithCustomError(UFarmFund_instance, 'NonAuthorized')
|
358
|
+
|
359
|
+
await UFarmFund_instance.updatePermissions(carol.address, ownerMask)
|
360
|
+
|
361
|
+
await expect(UFarmFund_instance.updatePermissions(wallet.address, emptyPermissionsMask))
|
362
|
+
.to.emit(UFarmFund_instance, 'PermissionsUpdated')
|
363
|
+
.withArgs(wallet.address, emptyPermissionsMask)
|
364
|
+
|
365
|
+
await expect(UFarmFund_instance.updatePermissions(bob.address, emptyPermissionsMask))
|
366
|
+
.to.emit(UFarmFund_instance, 'PermissionsUpdated')
|
367
|
+
.withArgs(bob.address, emptyPermissionsMask)
|
368
|
+
|
369
|
+
await expect(
|
370
|
+
UFarmFund_instance.updatePermissions(alice.address, emptyPermissionsMask),
|
371
|
+
).to.be.revertedWithCustomError(UFarmFund_instance, 'NonAuthorized')
|
372
|
+
|
373
|
+
await UFarmFund_instance.connect(carol).updatePermissions(alice.address, emptyPermissionsMask)
|
374
|
+
|
375
|
+
await expect(
|
376
|
+
UFarmFund_instance.connect(carol).updatePermissions(carol.address, emptyPermissionsMask),
|
377
|
+
).to.be.revertedWithCustomError(UFarmFund_instance, 'NonAuthorized')
|
378
|
+
})
|
379
|
+
})
|
380
|
+
|
381
|
+
describe("Fund assets balance operations' tests", function () {
|
382
|
+
it('Should receive and transfer ERC20 tokens', async function () {
|
383
|
+
const { UFarmFund_instance, deployer, alice, tokens } = await loadFixture(UFarmFundFixture)
|
384
|
+
|
385
|
+
const token = tokens.DAI
|
386
|
+
|
387
|
+
const transferableAmount = ethers.utils.parseEther('100')
|
388
|
+
|
389
|
+
await token.mint(deployer.address, transferableAmount.mul(2))
|
390
|
+
await token.connect(deployer).transfer(UFarmFund_instance.address, transferableAmount)
|
391
|
+
|
392
|
+
expect(await token.balanceOf(UFarmFund_instance.address)).to.eq(
|
393
|
+
transferableAmount,
|
394
|
+
"Fund should keep tokens in it's balance",
|
395
|
+
)
|
396
|
+
|
397
|
+
await expect(() =>
|
398
|
+
UFarmFund_instance.connect(alice).withdrawAsset(
|
399
|
+
token.address,
|
400
|
+
deployer.address,
|
401
|
+
transferableAmount,
|
402
|
+
),
|
403
|
+
).to.changeTokenBalance(token, deployer, transferableAmount)
|
404
|
+
})
|
405
|
+
})
|
406
|
+
})
|