@@ -3,13 +3,20 @@ const {
33 ZERO_ADDRESS ,
44 to1e18,
55 lastBlockTime,
6+ increaseTime,
7+ pastEvents,
68} = require ( "../helpers/contract-test-helpers" )
79
810describe ( "TokenGrant" , ( ) => {
11+ let token
912 let grantee
1013
1114 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 ( )
1320 } )
1421
1522 describe ( "unlockedAmount" , ( ) => {
@@ -27,7 +34,7 @@ describe("TokenGrant", () => {
2734 context ( "before the schedule start" , ( ) => {
2835 it ( "should return no tokens unlocked" , async ( ) => {
2936 const start = now + 60
30- const cliff = now
37+ const cliff = now + 60
3138 const grant = await createGrant ( false , amount , duration , start , cliff )
3239 expect ( await grant . unlockedAmount ( ) ) . to . equal ( 0 )
3340 } )
@@ -101,22 +108,219 @@ describe("TokenGrant", () => {
101108 )
102109 } )
103110 } )
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+ } )
104302 } )
105303
106304 async function createGrant ( revocable , amount , duration , start , cliff ) {
107305 const TokenGrant = await ethers . getContractFactory ( "TokenGrant" )
108306 const tokenGrant = await TokenGrant . deploy ( )
109307 await tokenGrant . deployed ( )
110308
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+ )
120324
121325 return tokenGrant
122326 }
0 commit comments