@@ -3,13 +3,20 @@ const {
3
3
ZERO_ADDRESS ,
4
4
to1e18,
5
5
lastBlockTime,
6
+ increaseTime,
7
+ pastEvents,
6
8
} = require ( "../helpers/contract-test-helpers" )
7
9
8
10
describe ( "TokenGrant" , ( ) => {
11
+ let token
9
12
let grantee
10
13
11
14
beforeEach ( async ( ) => {
12
- ; [ grantee ] = await ethers . getSigners ( )
15
+ ; [ deployer , thirdParty , grantee ] = await ethers . getSigners ( )
16
+
17
+ const T = await ethers . getContractFactory ( "T" )
18
+ token = await T . deploy ( )
19
+ await token . deployed ( )
13
20
} )
14
21
15
22
describe ( "unlockedAmount" , ( ) => {
@@ -27,7 +34,7 @@ describe("TokenGrant", () => {
27
34
context ( "before the schedule start" , ( ) => {
28
35
it ( "should return no tokens unlocked" , async ( ) => {
29
36
const start = now + 60
30
- const cliff = now
37
+ const cliff = now + 60
31
38
const grant = await createGrant ( false , amount , duration , start , cliff )
32
39
expect ( await grant . unlockedAmount ( ) ) . to . equal ( 0 )
33
40
} )
@@ -101,22 +108,219 @@ describe("TokenGrant", () => {
101
108
)
102
109
} )
103
110
} )
111
+
112
+ context ( "when some tokens are staked" , ( ) => {
113
+ it ( "should return token amount unlocked so far" , async ( ) => {
114
+ const start = now - 3600 // one hour earlier
115
+ const cliff = now - 3600
116
+ const grant = await createGrant ( false , amount , duration , start , cliff )
117
+ await grant . connect ( grantee ) . withdraw ( )
118
+ expect ( await grant . unlockedAmount ( ) ) . is . closeTo (
119
+ to1e18 ( 23 ) , // 3600 / 15552000 * 100k = ~23 tokens
120
+ assertionPrecision
121
+ )
122
+ } )
123
+ } )
124
+
125
+ context ( "when some tokens were withdrawn" , ( ) => {
126
+ it ( "should return token amount unlocked so far" , async ( ) => {
127
+ const start = now - 3600 // one hour earlier
128
+ const cliff = now - 3600
129
+ const grant = await createGrant ( false , amount , duration , start , cliff )
130
+ await grant . connect ( grantee ) . stake ( to1e18 ( 20 ) )
131
+ expect ( await grant . unlockedAmount ( ) ) . is . closeTo (
132
+ to1e18 ( 23 ) , // 3600 / 15552000 * 100k = ~23 tokens
133
+ assertionPrecision
134
+ )
135
+ } )
136
+ } )
137
+ } )
138
+
139
+ describe ( "withdrawableAmount" , ( ) => {
140
+ const assertionPrecision = to1e18 ( 1 ) // +- 1 token
141
+
142
+ const amount = to1e18 ( 100000 ) // 100k tokens
143
+ const duration = 15552000 // 180 days
144
+
145
+ let grant
146
+
147
+ beforeEach ( async ( ) => {
148
+ const now = await lastBlockTime ( )
149
+ const start = now - 7200 // two hours earlier
150
+ const cliff = now - 7200
151
+ grant = await createGrant ( false , amount , duration , start , cliff )
152
+ } )
153
+
154
+ context ( "when no tokens were staked or withdrawn" , ( ) => {
155
+ it ( "should return tokens unlocked so far" , async ( ) => {
156
+ expect ( await grant . withdrawableAmount ( ) ) . is . closeTo (
157
+ to1e18 ( 46 ) , // 7200 / 15552000 * 100k = ~46 tokens
158
+ assertionPrecision
159
+ )
160
+ } )
161
+ } )
162
+
163
+ context ( "when some tokens were staked" , ( ) => {
164
+ it ( "should return tokens unlocked so far minus staked tokens" , async ( ) => {
165
+ await grant . connect ( grantee ) . stake ( to1e18 ( 5 ) )
166
+ expect ( await grant . withdrawableAmount ( ) ) . is . closeTo (
167
+ to1e18 ( 41 ) , // 7200 / 15552000 * 100k - 5 = ~41 tokens
168
+ assertionPrecision
169
+ )
170
+ } )
171
+ } )
172
+
173
+ context ( "when some tokens were withdrawn" , ( ) => {
174
+ it ( "should return tokens unlocked so far minus withdrawn tokens" , async ( ) => {
175
+ await grant . connect ( grantee ) . withdraw ( )
176
+ await increaseTime ( 3600 )
177
+ expect ( await grant . withdrawableAmount ( ) ) . is . closeTo (
178
+ to1e18 ( 23 ) , // 3600 / 15552000 * 100k = ~23 tokens
179
+ assertionPrecision
180
+ )
181
+ } )
182
+ } )
183
+
184
+ context ( "when tokens were withdrawn multiple times" , ( ) => {
185
+ it ( "should return tokens unlocked so far minus withdrawn tokens" , async ( ) => {
186
+ await grant . connect ( grantee ) . withdraw ( )
187
+ await increaseTime ( 7200 )
188
+ await grant . connect ( grantee ) . withdraw ( )
189
+ await increaseTime ( 7200 )
190
+
191
+ expect ( await grant . withdrawableAmount ( ) ) . is . closeTo (
192
+ to1e18 ( 46 ) , // 7200 / 15552000 * 100k = ~46 tokens
193
+ assertionPrecision
194
+ )
195
+ } )
196
+ } )
197
+
198
+ context ( "when tokens were staked and withdrawn" , ( ) => {
199
+ it ( "should return tokens unlocked so far minus withdrawn and staked tokens" , async ( ) => {
200
+ await grant . connect ( grantee ) . withdraw ( )
201
+ await increaseTime ( 7200 )
202
+ await grant . connect ( grantee ) . stake ( to1e18 ( 20 ) )
203
+ expect ( await grant . withdrawableAmount ( ) ) . is . closeTo (
204
+ to1e18 ( 26 ) , // 7200 / 15552000 * 100k - 20 = ~26 tokens
205
+ assertionPrecision
206
+ )
207
+ } )
208
+ } )
209
+
210
+ context ( "when tokens were staked and withdrawn multiple times" , ( ) => {
211
+ it ( "should return tokens unlocked so far minus withdrawn and staked tokens" , async ( ) => {
212
+ await grant . connect ( grantee ) . withdraw ( )
213
+ await increaseTime ( 7200 )
214
+ await grant . connect ( grantee ) . withdraw ( )
215
+ await grant . connect ( grantee ) . stake ( to1e18 ( 10 ) )
216
+ await increaseTime ( 7200 )
217
+ expect ( await grant . withdrawableAmount ( ) ) . is . closeTo (
218
+ to1e18 ( 36 ) , // 7200 / 15552000 * 100k - 10 = ~36 tokens
219
+ assertionPrecision
220
+ )
221
+ } )
222
+ } )
223
+ } )
224
+
225
+ describe ( "withdraw" , ( ) => {
226
+ const assertionPrecision = to1e18 ( 1 ) // +- 1 token
227
+
228
+ const amount = to1e18 ( 200000 ) // 200k tokens
229
+ const duration = 7776000 // 90 days
230
+
231
+ let grant
232
+
233
+ beforeEach ( async ( ) => {
234
+ const now = await lastBlockTime ( )
235
+ const start = now - 3888000 // 45 days earlier
236
+ const cliff = now - 3888000
237
+ grant = await createGrant ( false , amount , duration , start , cliff )
238
+ } )
239
+
240
+ context ( "when called by a third party" , ( ) => {
241
+ it ( "should revert" , async ( ) => {
242
+ await expect ( grant . connect ( thirdParty ) . withdraw ( ) ) . to . be . revertedWith (
243
+ "Not authorized"
244
+ )
245
+ } )
246
+ } )
247
+
248
+ context ( "when called by a grant creator" , ( ) => {
249
+ it ( "should revert" , async ( ) => {
250
+ await expect ( grant . connect ( deployer ) . withdraw ( ) ) . to . be . revertedWith (
251
+ "Not authorized"
252
+ )
253
+ } )
254
+ } )
255
+
256
+ context ( "when called by grantee" , ( ) => {
257
+ context ( "when there are no withdrawable tokens" , ( ) => {
258
+ it ( "should revert" , async ( ) => {
259
+ await grant . connect ( grantee ) . stake ( amount )
260
+ await expect ( grant . connect ( grantee ) . withdraw ( ) ) . to . be . revertedWith (
261
+ "There is nothing to withdraw"
262
+ )
263
+ } )
264
+ } )
265
+
266
+ context ( "when there are withdrawable tokens" , ( ) => {
267
+ let tx
268
+
269
+ beforeEach ( async ( ) => {
270
+ // 3888000/7776000 * 200k = 100k
271
+ tx = await grant . connect ( grantee ) . withdraw ( )
272
+ } )
273
+
274
+ it ( "should increase withdrawn amount" , async ( ) => {
275
+ expect ( await grant . withdrawn ( ) ) . to . be . closeTo (
276
+ to1e18 ( 100000 ) ,
277
+ assertionPrecision
278
+ )
279
+ } )
280
+
281
+ it ( "should transfer tokens to grantee" , async ( ) => {
282
+ expect ( await token . balanceOf ( grantee . address ) ) . to . be . closeTo (
283
+ to1e18 ( 100000 ) ,
284
+ assertionPrecision
285
+ )
286
+ expect ( await token . balanceOf ( grant . address ) ) . to . be . closeTo (
287
+ to1e18 ( 100000 ) ,
288
+ assertionPrecision
289
+ )
290
+ } )
291
+
292
+ it ( "should emit Withdrawn event" , async ( ) => {
293
+ const events = pastEvents ( await tx . wait ( ) , grant , "Withdrawn" )
294
+ expect ( events . length ) . to . equal ( 1 )
295
+ expect ( events [ 0 ] . args [ "amount" ] ) . to . be . closeTo (
296
+ to1e18 ( 100000 ) ,
297
+ assertionPrecision
298
+ )
299
+ } )
300
+ } )
301
+ } )
104
302
} )
105
303
106
304
async function createGrant ( revocable , amount , duration , start , cliff ) {
107
305
const TokenGrant = await ethers . getContractFactory ( "TokenGrant" )
108
306
const tokenGrant = await TokenGrant . deploy ( )
109
307
await tokenGrant . deployed ( )
110
308
111
- await tokenGrant . initialize (
112
- grantee . address ,
113
- revocable ,
114
- amount ,
115
- duration ,
116
- start ,
117
- cliff ,
118
- ZERO_ADDRESS
119
- )
309
+ await token . connect ( deployer ) . mint ( deployer . address , amount )
310
+ await token . connect ( deployer ) . approve ( tokenGrant . address , amount )
311
+
312
+ await tokenGrant
313
+ . connect ( deployer )
314
+ . initialize (
315
+ token . address ,
316
+ grantee . address ,
317
+ revocable ,
318
+ amount ,
319
+ duration ,
320
+ start ,
321
+ cliff ,
322
+ ZERO_ADDRESS
323
+ )
120
324
121
325
return tokenGrant
122
326
}
0 commit comments