@@ -294,3 +294,116 @@ fn test_delayed_resolution_has_fresh_claim_window() {
294294 . client ( )
295295 . sweep_unclaimed_winnings ( & setup. admin , & setup. market_id , & true ) ;
296296}
297+
298+ #[ test]
299+ #[ should_panic( expected = "Error(Contract, #411)" ) ]
300+ fn test_double_sweep_rejected ( ) {
301+ // A second sweep on the same market must return SweepAlreadyDone (411).
302+ let setup = TimeoutSweepSetup :: new ( ) ;
303+
304+ setup
305+ . client ( )
306+ . set_global_claim_period ( & setup. admin , & 100u64 ) ;
307+ setup. client ( ) . set_treasury ( & setup. admin , & setup. treasury ) ;
308+
309+ setup. set_time ( setup. end_time + 100 ) ;
310+
311+ // First sweep succeeds.
312+ let first = setup
313+ . client ( )
314+ . sweep_unclaimed_winnings ( & setup. admin , & setup. market_id , & false ) ;
315+ assert ! ( first > 0 ) ;
316+
317+ // Second sweep must panic with #411.
318+ setup
319+ . client ( )
320+ . sweep_unclaimed_winnings ( & setup. admin , & setup. market_id , & false ) ;
321+ }
322+
323+ #[ test]
324+ #[ should_panic( expected = "Error(Contract, #411)" ) ]
325+ fn test_double_sweep_burn_rejected ( ) {
326+ // Same guard applies when burn=true.
327+ let setup = TimeoutSweepSetup :: new ( ) ;
328+
329+ setup
330+ . client ( )
331+ . set_global_claim_period ( & setup. admin , & 100u64 ) ;
332+
333+ setup. set_time ( setup. end_time + 100 ) ;
334+
335+ setup
336+ . client ( )
337+ . sweep_unclaimed_winnings ( & setup. admin , & setup. market_id , & true ) ;
338+
339+ // Second call must panic.
340+ setup
341+ . client ( )
342+ . sweep_unclaimed_winnings ( & setup. admin , & setup. market_id , & true ) ;
343+ }
344+
345+ #[ test]
346+ fn test_claim_then_sweep_excludes_claimed_user ( ) {
347+ // winner_1 claims before the sweep; the swept amount must not include their payout,
348+ // and the treasury must only receive the unclaimed portion.
349+ let setup = TimeoutSweepSetup :: new ( ) ;
350+
351+ setup
352+ . client ( )
353+ . set_global_claim_period ( & setup. admin , & 100u64 ) ;
354+ setup. client ( ) . set_treasury ( & setup. admin , & setup. treasury ) ;
355+
356+ // winner_1 claims while the window is still open.
357+ setup. set_time ( setup. end_time + 50 ) ;
358+ setup
359+ . client ( )
360+ . claim_winnings ( & setup. winner_1 , & setup. market_id ) ;
361+
362+ // Advance past the claim period.
363+ setup. set_time ( setup. end_time + 100 ) ;
364+ let swept = setup
365+ . client ( )
366+ . sweep_unclaimed_winnings ( & setup. admin , & setup. market_id , & false ) ;
367+
368+ // Only winner_2's share should be swept (winner_1 already claimed).
369+ let treasury_balance = setup
370+ . client ( )
371+ . get_balance ( & setup. treasury , & ReflectorAsset :: Stellar )
372+ . amount ;
373+ assert_eq ! ( treasury_balance, swept) ;
374+
375+ // Verify winner_1 is still marked claimed (their own claim, not the sweep).
376+ let market = setup. client ( ) . get_market ( & setup. market_id ) . unwrap ( ) ;
377+ assert ! ( market
378+ . claimed
379+ . get( setup. winner_1. clone( ) )
380+ . map( |i| i. is_claimed( ) )
381+ . unwrap_or( false ) ) ;
382+ assert ! ( market
383+ . claimed
384+ . get( setup. winner_2. clone( ) )
385+ . map( |i| i. is_claimed( ) )
386+ . unwrap_or( false ) ) ;
387+
388+ // The swept flag must be set.
389+ assert ! ( market. winnings_swept) ;
390+ }
391+
392+ #[ test]
393+ fn test_sweep_sets_winnings_swept_flag ( ) {
394+ // After a successful sweep the market's winnings_swept field must be true.
395+ let setup = TimeoutSweepSetup :: new ( ) ;
396+
397+ setup
398+ . client ( )
399+ . set_global_claim_period ( & setup. admin , & 100u64 ) ;
400+ setup. client ( ) . set_treasury ( & setup. admin , & setup. treasury ) ;
401+ setup. set_time ( setup. end_time + 100 ) ;
402+
403+ setup
404+ . client ( )
405+ . sweep_unclaimed_winnings ( & setup. admin , & setup. market_id , & false ) ;
406+
407+ let market = setup. client ( ) . get_market ( & setup. market_id ) . unwrap ( ) ;
408+ assert ! ( market. winnings_swept) ;
409+ }
0 commit comments