diff --git a/pallets/liquidity-mining/src/lib.rs b/pallets/liquidity-mining/src/lib.rs index e1af4d344b..3c92ea0a4a 100644 --- a/pallets/liquidity-mining/src/lib.rs +++ b/pallets/liquidity-mining/src/lib.rs @@ -167,13 +167,19 @@ impl PoolInfo { v_new.saturating_sub(v_old).saturating_mul_int(user_deposit), ); - // Update the claimed of the reward - reward.claimed = reward.claimed.saturating_add(amount); // Sync the gain_avg between `DepositData` and `RewardData` deposit_data.gain_avgs.insert(*rtoken, v_new); deposit_data.update_b = self.update_b; - to_rewards.push((*rtoken, amount)); + let ed = T::MultiCurrency::minimum_balance(*rtoken); + let total = + T::MultiCurrency::total_balance(*rtoken, &user).saturating_add(amount); + + if total >= ed { + // Update the claimed of the reward + reward.claimed = reward.claimed.saturating_add(amount); + to_rewards.push((*rtoken, amount)); + } } } } diff --git a/pallets/liquidity-mining/src/mock.rs b/pallets/liquidity-mining/src/mock.rs index 4a77bcecb7..073976fbeb 100644 --- a/pallets/liquidity-mining/src/mock.rs +++ b/pallets/liquidity-mining/src/mock.rs @@ -93,7 +93,7 @@ impl frame_system::Config for Test { } parameter_types! { - pub const ExistentialDeposit: u128 = 0; + pub const ExistentialDeposit: u128 = 1_000_000; pub const TransferFee: u128 = 0; pub const CreationFee: u128 = 0; pub const TransactionByteFee: u128 = 0; diff --git a/pallets/liquidity-mining/src/tests.rs b/pallets/liquidity-mining/src/tests.rs index 5adfe9f133..0d04a4b6dc 100644 --- a/pallets/liquidity-mining/src/tests.rs +++ b/pallets/liquidity-mining/src/tests.rs @@ -2162,6 +2162,70 @@ fn claim_from_eb_farming_should_work() { }); } +#[test] +fn discard_reward_lower_than_ed_should_work() { + new_test_ext().execute_with(|| { + const PER_BLOCK: Balance = REWARD_AMOUNT / DAYS as Balance; + const HALF_ED_PER_BLOCK: Balance = ExistentialDeposit::get() / 2; + + assert_ok!(LM::create_eb_farming_pool( + pallet_collective::RawOrigin::Member(TC_MEMBER_1).into(), + 2001, + 13, + 20, + (REWARD_1, HALF_ED_PER_BLOCK * DAYS as Balance), + vec![(REWARD_2, REWARD_AMOUNT)].try_into().unwrap(), + DAYS, + 1_000_000, + 0 + )); + + // It is unable to call Collective::execute(..) which is private; + assert_ok!(LM::charge(Some(INVESTOR).into(), 0)); + + assert_ok!(Tokens::reserve(FARMING_DEPOSIT_1, &USER_1, DEPOSIT_AMOUNT)); + assert_ok!(Tokens::reserve(FARMING_DEPOSIT_2, &USER_1, DEPOSIT_AMOUNT)); + + assert_ok!(LM::deposit(Some(USER_1).into(), 0, DEPOSIT_AMOUNT)); + + let keeper = LM::pool(0).unwrap().keeper; + + // The action will discard the reward of `reward_1`. + run_to_block(1); + assert_ok!(LM::claim(Some(USER_1).into(), 0)); + + assert_eq!(Tokens::accounts(USER_1, REWARD_1).free, 0); + assert_eq!(Tokens::accounts(USER_1, REWARD_2).free, PER_BLOCK); + + run_to_block(3); + assert_ok!(LM::claim(Some(USER_1).into(), 0)); + + assert_eq!(Tokens::accounts(USER_1, REWARD_1).free, 2 * HALF_ED_PER_BLOCK); + assert_eq!(Tokens::accounts(USER_1, REWARD_2).free, 3 * PER_BLOCK); + + run_to_block(4); + assert_ok!(LM::claim(Some(USER_1).into(), 0)); + + assert_eq!(Tokens::accounts(USER_1, REWARD_1).free, 3 * HALF_ED_PER_BLOCK); + assert_eq!(Tokens::accounts(USER_1, REWARD_2).free, 4 * PER_BLOCK); + + run_to_block(DAYS); + assert_ok!(LM::redeem_all(Some(USER_1).into(), 0)); + + let reward_1 = (DAYS - 1) as Balance * HALF_ED_PER_BLOCK; + let reward_2 = DAYS as Balance * PER_BLOCK; + + assert_eq!(Tokens::accounts(USER_1, REWARD_1).free, reward_1); + assert_eq!(Tokens::accounts(USER_1, REWARD_2).free, reward_2); + + assert_eq!(Tokens::accounts(keeper.clone(), REWARD_1).free, 0); + assert_eq!(Tokens::accounts(keeper.clone(), REWARD_2).free, 0); + + assert_eq!(Tokens::accounts(INVESTOR, REWARD_1).free, REWARD_AMOUNT - reward_1); + assert_eq!(Tokens::accounts(INVESTOR, REWARD_2).free, REWARD_AMOUNT - reward_2); + }); +} + #[test] fn simple_integration_test() { new_test_ext().execute_with(|| {