@@ -236,6 +236,168 @@ describe("UTT", function () {
236236 } ) ;
237237 } ) ;
238238
239+ describe ( "Stake withdrawal" , function ( ) {
240+ async function deployWithUserStake ( stakeAmount = 1000 ) {
241+ const fixture = await loadFixture ( deployUTT ) ;
242+ const { utt, mockOperator, user1, service1, connector } = fixture ;
243+
244+ await addConnection ( utt , connector , user1 . address ) ;
245+ await endorse (
246+ utt ,
247+ mockOperator ,
248+ user1 ,
249+ service1 . address ,
250+ stakeAmount ,
251+ mockTransactionId ,
252+ [ ] ,
253+ [ ]
254+ ) ;
255+
256+ return fixture ;
257+ }
258+
259+ async function deployWithClaimableRewards ( ) {
260+ const fixture = await loadFixture ( deployUTT ) ;
261+ const { utt, mockOperator, admin, user1, service1, connector } =
262+ fixture ;
263+
264+ await addConnection ( utt , connector , user1 . address ) ;
265+ await endorse (
266+ utt ,
267+ mockOperator ,
268+ user1 ,
269+ service1 . address ,
270+ 200 ,
271+ mockTransactionId ,
272+ [ ] ,
273+ [ ]
274+ ) ;
275+ await endorse (
276+ utt ,
277+ mockOperator ,
278+ admin ,
279+ service1 . address ,
280+ 200 ,
281+ mockTransactionId ,
282+ [ user1 . address ] ,
283+ [ ]
284+ ) ;
285+
286+ return fixture ;
287+ }
288+
289+ it ( "should allow a user to withdraw part of their stake" , async function ( ) {
290+ const { utt, user1, service1 } = await deployWithUserStake ( ) ;
291+ const balanceBefore = await utt . balanceOf ( user1 . address ) ;
292+ const stakeBefore = await utt . previousEndorserStakes (
293+ service1 . address ,
294+ user1 . address
295+ ) ;
296+ const totalStakeBefore = await utt . totalStake ( service1 . address ) ;
297+
298+ const withdrawP = utt
299+ . connect ( user1 )
300+ . withdrawStake ( service1 . address , 400 , mockTransactionId ) ;
301+
302+ await expect ( withdrawP )
303+ . to . emit ( utt , "WithdrawStake" )
304+ . withArgs ( user1 . address , service1 . address , 400 , mockTransactionId ) ;
305+ expect ( await utt . balanceOf ( user1 . address ) ) . to . eq ( balanceBefore + 400n ) ;
306+ expect (
307+ await utt . previousEndorserStakes ( service1 . address , user1 . address )
308+ ) . to . eq ( stakeBefore - 400n ) ;
309+ expect ( await utt . totalStake ( service1 . address ) ) . to . eq (
310+ totalStakeBefore - 400n
311+ ) ;
312+ } ) ;
313+
314+ it ( "should allow a user to withdraw all of their stake" , async function ( ) {
315+ const { utt, user1, service1 } = await deployWithUserStake ( ) ;
316+
317+ await utt
318+ . connect ( user1 )
319+ . withdrawStake ( service1 . address , 1000 , mockTransactionId ) ;
320+
321+ expect (
322+ await utt . previousEndorserStakes ( service1 . address , user1 . address )
323+ ) . to . eq ( 0 ) ;
324+ expect ( await utt . totalStake ( service1 . address ) ) . to . eq ( 0 ) ;
325+ } ) ;
326+
327+ it ( "should revert when withdrawing an invalid amount" , async function ( ) {
328+ const { utt, user1, service1 } = await deployWithUserStake ( ) ;
329+
330+ await expect (
331+ utt . connect ( user1 ) . withdrawStake ( service1 . address , 0 , mockTransactionId )
332+ ) . to . be . revertedWith ( "UTT: withdraw amount must be greater than zero" ) ;
333+ await expect (
334+ utt
335+ . connect ( user1 )
336+ . withdrawStake ( service1 . address , 1001 , mockTransactionId )
337+ ) . to . be . revertedWith ( "UTT: withdraw amount exceeds stake" ) ;
338+ } ) ;
339+
340+ it ( "should revert when withdrawing stake for a target the user never endorsed" , async function ( ) {
341+ const { utt, user1, service1 } = await loadFixture ( deployUTT ) ;
342+
343+ await expect (
344+ utt . connect ( user1 ) . withdrawStake ( service1 . address , 1 , mockTransactionId )
345+ ) . to . be . revertedWith ( "UTT: withdraw amount exceeds stake" ) ;
346+ } ) ;
347+
348+ it ( "should not emit reward events" , async function ( ) {
349+ const { utt, user1, service1 } = await deployWithUserStake ( ) ;
350+
351+ const withdrawP = utt
352+ . connect ( user1 )
353+ . withdrawStake ( service1 . address , 250 , mockTransactionId ) ;
354+
355+ await expect ( withdrawP )
356+ . to . not . emit ( utt , "RewardPreviousEndorserLevel1" )
357+ . to . not . emit ( utt , "RewardPreviousEndorserLevel2" )
358+ . to . not . emit ( utt , "RewardUTUCoin" ) ;
359+ } ) ;
360+
361+ it ( "should not change claimable UTU Coin" , async function ( ) {
362+ const { utt, user1, service1 } = await deployWithClaimableRewards ( ) ;
363+ const claimableBefore = await utt . claimableUTUCoin ( user1 . address ) ;
364+ const totalClaimableBefore = await utt . totalClaimableUTUCoin ( ) ;
365+ expect ( claimableBefore ) . to . be . gt ( 0 ) ;
366+ expect ( totalClaimableBefore ) . to . be . gt ( 0 ) ;
367+
368+ await utt
369+ . connect ( user1 )
370+ . withdrawStake ( service1 . address , 50 , mockTransactionId ) ;
371+
372+ expect ( await utt . claimableUTUCoin ( user1 . address ) ) . to . eq ( claimableBefore ) ;
373+ expect ( await utt . totalClaimableUTUCoin ( ) ) . to . eq ( totalClaimableBefore ) ;
374+ } ) ;
375+
376+ it ( "should block stake withdrawal while migration is active" , async function ( ) {
377+ const { utt, admin, user1, service1 } = await deployWithUserStake ( ) ;
378+
379+ await utt . connect ( admin ) . startMigrationToNewContract ( ) ;
380+
381+ await expect (
382+ utt
383+ . connect ( user1 )
384+ . withdrawStake ( service1 . address , 250 , mockTransactionId )
385+ ) . to . be . revertedWith ( "Contract is migrating" ) ;
386+ } ) ;
387+
388+ it ( "should revert stake withdrawal while the contract is paused" , async function ( ) {
389+ const { utt, admin, user1, service1 } = await deployWithUserStake ( ) ;
390+
391+ await utt . connect ( admin ) . pause ( ) ;
392+
393+ await expect (
394+ utt
395+ . connect ( user1 )
396+ . withdrawStake ( service1 . address , 250 , mockTransactionId )
397+ ) . to . be . reverted ;
398+ } ) ;
399+ } ) ;
400+
239401 describe ( "User tries to addConnection" , function ( ) {
240402 it ( "should not allow a user to add a connection by themselves" , async function ( ) {
241403 const { utt, user1 } = await loadFixture ( deployUTT ) ;
0 commit comments