@@ -10,8 +10,8 @@ use crate::{
10
10
contract:: { CONTRACT_NAME , CONTRACT_VERSION } ,
11
11
error:: ContractError ,
12
12
state:: {
13
- EMISSIONS , INCENTIVE_STATES , MIGRATION_GUARD , OWNER , USER_ASSET_INDICES ,
14
- USER_UNCLAIMED_REWARDS ,
13
+ ASTRO_USER_LP_DEPOSITS , EMISSIONS , INCENTIVE_STATES , MIGRATION_GUARD , OWNER ,
14
+ USER_ASSET_INDICES , USER_ASTRO_INCENTIVE_STATES , USER_UNCLAIMED_REWARDS ,
15
15
} ,
16
16
} ;
17
17
@@ -27,6 +27,15 @@ pub mod v2_state {
27
27
pub const USER_ASSET_INDICES : Map < ( & UserIdKey , & str , & str ) , Decimal > = Map :: new ( "indices_v2" ) ;
28
28
pub const USER_UNCLAIMED_REWARDS : Map < ( & UserIdKey , & str , & str ) , Uint128 > =
29
29
Map :: new ( "unclaimed_rewards_v2" ) ;
30
+
31
+ // Map of User Lp deposits. Key is (user_id, lp_denom)
32
+ pub const ASTRO_USER_LP_DEPOSITS : Map < ( & str , & str ) , Uint128 > = Map :: new ( "lp_deposits" ) ;
33
+
34
+ /// A map containing the individual incentive index for each unique user
35
+ /// Note - this may contain many denoms for one user
36
+ /// The key is (account_id, lp_token_denom, reward_denom)
37
+ pub const USER_ASTRO_INCENTIVE_STATES : Map < ( & str , & str , & str ) , Decimal > =
38
+ Map :: new ( "user_astroport_incentive_states" ) ;
30
39
}
31
40
32
41
pub fn migrate ( mut deps : DepsMut , _env : Env , _msg : Empty ) -> Result < Response , ContractError > {
@@ -41,6 +50,8 @@ pub fn migrate(mut deps: DepsMut, _env: Env, _msg: Empty) -> Result<Response, Co
41
50
migrate_incentive_states ( & mut deps) ?;
42
51
migrate_emissions ( & mut deps) ?;
43
52
53
+ // Clear all zero balances in astro incentives
54
+ clear_zero_amounts_in_staked_astro_lp ( & mut deps) ?;
44
55
set_contract_version ( deps. storage , format ! ( "crates.io:{CONTRACT_NAME}" ) , CONTRACT_VERSION ) ?;
45
56
46
57
Ok ( Response :: new ( )
@@ -151,6 +162,44 @@ fn migrate_user_unclaimed_rewards(deps: DepsMut, limit: usize) -> Result<Respons
151
162
. add_attribute ( "has_more" , has_more. to_string ( ) ) )
152
163
}
153
164
165
+ /// Clear all zero amounts in staked astro LP positions
166
+ /// This will delete all incentive states for (account_id, lp_denom) keys where the associated
167
+ /// staked amount is zero.
168
+ fn clear_zero_amounts_in_staked_astro_lp ( deps : & mut DepsMut ) -> Result < ( ) , ContractError > {
169
+ // Collect all LP positions that are zero
170
+ let zero_balance_items = ASTRO_USER_LP_DEPOSITS
171
+ . range ( deps. storage , None , None , Order :: Ascending )
172
+ . filter_map ( |item| match item {
173
+ Ok ( ( ( user_id, account) , value) ) if value. is_zero ( ) => {
174
+ Some ( Ok ( ( ( user_id. to_string ( ) , account. to_string ( ) ) , value) ) )
175
+ }
176
+ Ok ( _) => None ,
177
+ Err ( e) => Some ( Err ( e) ) ,
178
+ } )
179
+ . collect :: < StdResult < Vec < _ > > > ( ) ?;
180
+
181
+ // Iterate all LP positions that are zero, and delete the associated incentive indexes
182
+ for ( ( account_id, denom) , _) in zero_balance_items. iter ( ) {
183
+ ASTRO_USER_LP_DEPOSITS . remove ( deps. storage , ( account_id, denom) ) ;
184
+
185
+ // Get all incentives for (user, lp_token_denom) key
186
+ let prefix = USER_ASTRO_INCENTIVE_STATES . prefix ( ( account_id, denom) ) ;
187
+
188
+ // Iterate over all reward_denom keys
189
+ let keys_to_remove = prefix
190
+ . keys ( deps. storage , None , None , Order :: Ascending )
191
+ . collect :: < StdResult < Vec < String > > > ( ) ?;
192
+
193
+ // Delete each matching (account_id, lp_token_denom, reward_denom) incentive index.
194
+ for incentive_denom in keys_to_remove {
195
+ USER_ASTRO_INCENTIVE_STATES
196
+ . remove ( deps. storage , ( account_id, denom. as_str ( ) , & incentive_denom) ) ;
197
+ }
198
+ }
199
+
200
+ Ok ( ( ) )
201
+ }
202
+
154
203
fn migrate_user_asset_indices ( deps : DepsMut , limit : usize ) -> Result < Response , ContractError > {
155
204
// Only allow to migrate users asset indices if guard is locked via `migrate` entrypoint
156
205
MIGRATION_GUARD . assert_locked ( deps. storage ) ?;
@@ -240,6 +289,8 @@ fn clear_v2_state(deps: DepsMut) -> Result<Response, ContractError> {
240
289
241
290
#[ cfg( test) ]
242
291
pub mod tests {
292
+ use std:: str:: FromStr ;
293
+
243
294
use cosmwasm_std:: { attr, testing:: mock_dependencies, Addr , Decimal , Uint128 } ;
244
295
use mars_types:: incentives:: IncentiveState ;
245
296
use mars_utils:: error:: GuardError ;
@@ -295,6 +346,231 @@ pub mod tests {
295
346
) ;
296
347
}
297
348
349
+ #[ test]
350
+ fn clear_zero_amounts_in_staked_astro_lps ( ) {
351
+ let mut deps = mock_dependencies ( ) ;
352
+
353
+ MIGRATION_GUARD . try_lock ( deps. as_mut ( ) . storage ) . unwrap ( ) ;
354
+
355
+ let lp_denom_1 = "factory/neutronasdfkldshfkldsjfklfdsaaaaassss111/astroport/share" ;
356
+ let lp_denom_2 = "factory/neutronasdfkldshfkldsjfklfdsfdsfdsfd2222/astroport/share" ;
357
+ let reward_denom_1 = "untrn" ;
358
+ let reward_denom_2 = "ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858" ;
359
+
360
+ //
361
+ // Instantiate Deposits
362
+ //
363
+
364
+ // User 1
365
+ // Lp_denom 1 has balance > 0
366
+ // LP_denom 2 balance of 0
367
+ v2_state:: ASTRO_USER_LP_DEPOSITS
368
+ . save ( deps. as_mut ( ) . storage , ( "1" , lp_denom_1) , & Uint128 :: new ( 100000000 ) )
369
+ . unwrap ( ) ;
370
+ v2_state:: ASTRO_USER_LP_DEPOSITS
371
+ . save ( deps. as_mut ( ) . storage , ( "1" , lp_denom_2) , & Uint128 :: new ( 0 ) )
372
+ . unwrap ( ) ;
373
+
374
+ // User 2
375
+ // Lp_denom 1 has balance of 0
376
+ // LP_denom 2 balance > 0
377
+ v2_state:: ASTRO_USER_LP_DEPOSITS
378
+ . save ( deps. as_mut ( ) . storage , ( "2" , lp_denom_1) , & Uint128 :: new ( 0 ) )
379
+ . unwrap ( ) ;
380
+
381
+ v2_state:: ASTRO_USER_LP_DEPOSITS
382
+ . save ( deps. as_mut ( ) . storage , ( "2" , lp_denom_2) , & Uint128 :: new ( 100000000 ) )
383
+ . unwrap ( ) ;
384
+
385
+ // User 3
386
+ // Lp_denom 1 has balance > 0
387
+ v2_state:: ASTRO_USER_LP_DEPOSITS
388
+ . save ( deps. as_mut ( ) . storage , ( "3" , lp_denom_1) , & Uint128 :: new ( 100000000 ) )
389
+ . unwrap ( ) ;
390
+
391
+ // User 4
392
+ // Lp_denom 1 has balance of 0
393
+ v2_state:: ASTRO_USER_LP_DEPOSITS
394
+ . save ( deps. as_mut ( ) . storage , ( "4" , lp_denom_1) , & Uint128 :: new ( 0 ) )
395
+ . unwrap ( ) ;
396
+
397
+ // User 5
398
+ // Lp_denom 1 has balance > 0
399
+ // Lp_denom 2 has balance > 0
400
+ v2_state:: ASTRO_USER_LP_DEPOSITS
401
+ . save ( deps. as_mut ( ) . storage , ( "5" , lp_denom_1) , & Uint128 :: new ( 100000000 ) )
402
+ . unwrap ( ) ;
403
+ v2_state:: ASTRO_USER_LP_DEPOSITS
404
+ . save ( deps. as_mut ( ) . storage , ( "5" , lp_denom_2) , & Uint128 :: new ( 100000000 ) )
405
+ . unwrap ( ) ;
406
+
407
+ //
408
+ // Instantiate user reward states
409
+ //
410
+
411
+ // User 1
412
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
413
+ . save (
414
+ deps. as_mut ( ) . storage ,
415
+ ( "1" , lp_denom_1, reward_denom_2) ,
416
+ & Decimal :: from_str ( "1.0001456" ) . unwrap ( ) ,
417
+ )
418
+ . unwrap ( ) ;
419
+
420
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
421
+ . save (
422
+ deps. as_mut ( ) . storage ,
423
+ ( "1" , lp_denom_1, reward_denom_1) ,
424
+ & Decimal :: from_str ( "1.0001456" ) . unwrap ( ) ,
425
+ )
426
+ . unwrap ( ) ;
427
+
428
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
429
+ . save (
430
+ deps. as_mut ( ) . storage ,
431
+ ( "1" , lp_denom_2, reward_denom_1) ,
432
+ & Decimal :: from_str ( "1.21456" ) . unwrap ( ) ,
433
+ )
434
+ . unwrap ( ) ;
435
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
436
+ . save (
437
+ deps. as_mut ( ) . storage ,
438
+ ( "1" , lp_denom_2, reward_denom_2) ,
439
+ & Decimal :: from_str ( "1.21456" ) . unwrap ( ) ,
440
+ )
441
+ . unwrap ( ) ;
442
+
443
+ // User 2
444
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
445
+ . save (
446
+ deps. as_mut ( ) . storage ,
447
+ ( "2" , lp_denom_1, reward_denom_2) ,
448
+ & Decimal :: from_str ( "1.0001456" ) . unwrap ( ) ,
449
+ )
450
+ . unwrap ( ) ;
451
+
452
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
453
+ . save (
454
+ deps. as_mut ( ) . storage ,
455
+ ( "2" , lp_denom_2, reward_denom_1) ,
456
+ & Decimal :: from_str ( "1.0001456" ) . unwrap ( ) ,
457
+ )
458
+ . unwrap ( ) ;
459
+
460
+ // User 3
461
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
462
+ . save (
463
+ deps. as_mut ( ) . storage ,
464
+ ( "3" , lp_denom_1, reward_denom_2) ,
465
+ & Decimal :: from_str ( "1.0001456" ) . unwrap ( ) ,
466
+ )
467
+ . unwrap ( ) ;
468
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
469
+ . save (
470
+ deps. as_mut ( ) . storage ,
471
+ ( "3" , lp_denom_1, reward_denom_1) ,
472
+ & Decimal :: from_str ( "1.0001456" ) . unwrap ( ) ,
473
+ )
474
+ . unwrap ( ) ;
475
+
476
+ // User 4 no incentive states
477
+
478
+ // User 5 - only 1 reward asset
479
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
480
+ . save (
481
+ deps. as_mut ( ) . storage ,
482
+ ( "5" , lp_denom_1, reward_denom_2) ,
483
+ & Decimal :: from_str ( "1.0001456" ) . unwrap ( ) ,
484
+ )
485
+ . unwrap ( ) ;
486
+ v2_state:: USER_ASTRO_INCENTIVE_STATES
487
+ . save (
488
+ deps. as_mut ( ) . storage ,
489
+ ( "5" , lp_denom_2, reward_denom_2) ,
490
+ & Decimal :: from_str ( "1.0001456" ) . unwrap ( ) ,
491
+ )
492
+ . unwrap ( ) ;
493
+
494
+ // Assert user positions before
495
+ let user_deposits_before = v2_state:: ASTRO_USER_LP_DEPOSITS
496
+ . range ( deps. as_ref ( ) . storage , None , None , Order :: Ascending )
497
+ . collect :: < StdResult < Vec < _ > > > ( )
498
+ . unwrap ( ) ;
499
+
500
+ assert_eq ! ( user_deposits_before. len( ) , 8 ) ;
501
+ assert_eq ! ( user_deposits_before[ 0 ] . 0 , ( "1" . to_string( ) , lp_denom_1. to_string( ) ) ) ;
502
+ assert_eq ! ( user_deposits_before[ 1 ] . 0 , ( "1" . to_string( ) , lp_denom_2. to_string( ) ) ) ;
503
+ assert_eq ! ( user_deposits_before[ 2 ] . 0 , ( "2" . to_string( ) , lp_denom_1. to_string( ) ) ) ;
504
+ assert_eq ! ( user_deposits_before[ 3 ] . 0 , ( "2" . to_string( ) , lp_denom_2. to_string( ) ) ) ;
505
+ assert_eq ! ( user_deposits_before[ 4 ] . 0 , ( "3" . to_string( ) , lp_denom_1. to_string( ) ) ) ;
506
+ assert_eq ! ( user_deposits_before[ 5 ] . 0 , ( "4" . to_string( ) , lp_denom_1. to_string( ) ) ) ;
507
+ assert_eq ! ( user_deposits_before[ 6 ] . 0 , ( "5" . to_string( ) , lp_denom_1. to_string( ) ) ) ;
508
+ assert_eq ! ( user_deposits_before[ 7 ] . 0 , ( "5" . to_string( ) , lp_denom_2. to_string( ) ) ) ;
509
+
510
+ let incentive_states_before = v2_state:: USER_ASTRO_INCENTIVE_STATES
511
+ . range ( deps. as_ref ( ) . storage , None , None , Order :: Ascending )
512
+ . collect :: < StdResult < Vec < _ > > > ( )
513
+ . unwrap ( ) ;
514
+
515
+ // Assert all incentives are there
516
+ assert_eq ! ( incentive_states_before. len( ) , 10 ) ;
517
+
518
+ // Clear balances
519
+ clear_zero_amounts_in_staked_astro_lp ( & mut deps. as_mut ( ) ) . unwrap ( ) ;
520
+
521
+ let user_deposits_after = v2_state:: ASTRO_USER_LP_DEPOSITS
522
+ . range ( deps. as_ref ( ) . storage , None , None , Order :: Ascending )
523
+ . collect :: < StdResult < Vec < _ > > > ( )
524
+ . unwrap ( ) ;
525
+
526
+ assert_eq ! ( user_deposits_after[ 0 ] . 0 , ( "1" . to_string( ) , lp_denom_1. to_string( ) ) ) ;
527
+ assert_eq ! ( user_deposits_after[ 1 ] . 0 , ( "2" . to_string( ) , lp_denom_2. to_string( ) ) ) ;
528
+ assert_eq ! ( user_deposits_after[ 2 ] . 0 , ( "3" . to_string( ) , lp_denom_1. to_string( ) ) ) ;
529
+ assert_eq ! ( user_deposits_after[ 3 ] . 0 , ( "5" . to_string( ) , lp_denom_1. to_string( ) ) ) ;
530
+ assert_eq ! ( user_deposits_after[ 4 ] . 0 , ( "5" . to_string( ) , lp_denom_2. to_string( ) ) ) ;
531
+
532
+ // Incentive records that should be cleared
533
+ // (user_1,lp_denom_2)
534
+ // - both incentives (2 records deleted)
535
+
536
+ // (User 2, lp_denom_1)
537
+ // - one incentive
538
+ let incentive_states_after = v2_state:: USER_ASTRO_INCENTIVE_STATES
539
+ . range ( deps. as_ref ( ) . storage , None , None , Order :: Ascending )
540
+ . collect :: < StdResult < Vec < _ > > > ( )
541
+ . unwrap ( ) ;
542
+
543
+ assert_eq ! ( incentive_states_after. len( ) , 7 ) ; // because we deleted 3 records
544
+ assert_eq ! (
545
+ incentive_states_after[ 0 ] . 0 ,
546
+ ( "1" . to_string( ) , lp_denom_1. to_string( ) , reward_denom_2. to_string( ) )
547
+ ) ;
548
+ assert_eq ! (
549
+ incentive_states_after[ 1 ] . 0 ,
550
+ ( "1" . to_string( ) , lp_denom_1. to_string( ) , reward_denom_1. to_string( ) )
551
+ ) ;
552
+ assert_eq ! (
553
+ incentive_states_after[ 2 ] . 0 ,
554
+ ( "2" . to_string( ) , lp_denom_2. to_string( ) , reward_denom_1. to_string( ) )
555
+ ) ;
556
+ assert_eq ! (
557
+ incentive_states_after[ 3 ] . 0 ,
558
+ ( "3" . to_string( ) , lp_denom_1. to_string( ) , reward_denom_2. to_string( ) )
559
+ ) ;
560
+ assert_eq ! (
561
+ incentive_states_after[ 4 ] . 0 ,
562
+ ( "3" . to_string( ) , lp_denom_1. to_string( ) , reward_denom_1. to_string( ) )
563
+ ) ;
564
+ assert_eq ! (
565
+ incentive_states_after[ 5 ] . 0 ,
566
+ ( "5" . to_string( ) , lp_denom_1. to_string( ) , reward_denom_2. to_string( ) )
567
+ ) ;
568
+ assert_eq ! (
569
+ incentive_states_after[ 6 ] . 0 ,
570
+ ( "5" . to_string( ) , lp_denom_2. to_string( ) , reward_denom_2. to_string( ) )
571
+ ) ;
572
+ }
573
+
298
574
#[ test]
299
575
fn migrate_v2_user_unclaimed_rewards ( ) {
300
576
let mut deps = mock_dependencies ( ) ;
0 commit comments