Skip to content

Commit b7863cb

Browse files
authored
add migration for staked astro incentives (#187)
* add migration for staked astro incentives * tidy
1 parent f59d3a7 commit b7863cb

File tree

1 file changed

+278
-2
lines changed

1 file changed

+278
-2
lines changed

contracts/incentives/src/migrations/v2_0_1.rs

Lines changed: 278 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use crate::{
1010
contract::{CONTRACT_NAME, CONTRACT_VERSION},
1111
error::ContractError,
1212
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,
1515
},
1616
};
1717

@@ -27,6 +27,15 @@ pub mod v2_state {
2727
pub const USER_ASSET_INDICES: Map<(&UserIdKey, &str, &str), Decimal> = Map::new("indices_v2");
2828
pub const USER_UNCLAIMED_REWARDS: Map<(&UserIdKey, &str, &str), Uint128> =
2929
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");
3039
}
3140

3241
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
4150
migrate_incentive_states(&mut deps)?;
4251
migrate_emissions(&mut deps)?;
4352

53+
// Clear all zero balances in astro incentives
54+
clear_zero_amounts_in_staked_astro_lp(&mut deps)?;
4455
set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), CONTRACT_VERSION)?;
4556

4657
Ok(Response::new()
@@ -151,6 +162,44 @@ fn migrate_user_unclaimed_rewards(deps: DepsMut, limit: usize) -> Result<Respons
151162
.add_attribute("has_more", has_more.to_string()))
152163
}
153164

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+
154203
fn migrate_user_asset_indices(deps: DepsMut, limit: usize) -> Result<Response, ContractError> {
155204
// Only allow to migrate users asset indices if guard is locked via `migrate` entrypoint
156205
MIGRATION_GUARD.assert_locked(deps.storage)?;
@@ -240,6 +289,8 @@ fn clear_v2_state(deps: DepsMut) -> Result<Response, ContractError> {
240289

241290
#[cfg(test)]
242291
pub mod tests {
292+
use std::str::FromStr;
293+
243294
use cosmwasm_std::{attr, testing::mock_dependencies, Addr, Decimal, Uint128};
244295
use mars_types::incentives::IncentiveState;
245296
use mars_utils::error::GuardError;
@@ -295,6 +346,231 @@ pub mod tests {
295346
);
296347
}
297348

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+
298574
#[test]
299575
fn migrate_v2_user_unclaimed_rewards() {
300576
let mut deps = mock_dependencies();

0 commit comments

Comments
 (0)