diff --git a/pallets/proof-of-stake/src/schedule_rewards_calculator.rs b/pallets/proof-of-stake/src/schedule_rewards_calculator.rs index ce5cfc29e1..e46982902e 100644 --- a/pallets/proof-of-stake/src/schedule_rewards_calculator.rs +++ b/pallets/proof-of-stake/src/schedule_rewards_calculator.rs @@ -70,36 +70,33 @@ pub struct ScheduleRewards { impl ScheduleRewards { pub fn provide_rewards(&mut self, now: SessionId, amount: Balance) { - if now <= self.pending_session_id { - self.pending += amount; - } else { + if now > self.pending_session_id { self.total += self.pending.clone(); self.pending = amount; self.pending_session_id = now; + } else { + self.pending += amount; } } pub fn total_rewards(&self, now: SessionId) -> Balance { - if now <= self.pending_session_id { - self.total.clone() - } else { + if now > self.pending_session_id { self.total.clone() + self.pending.clone() + } else { + self.total.clone() } } - pub fn transfer_pending(&mut self, now: SessionId) { + pub fn clear(&mut self, now: SessionId) { if now > self.pending_session_id { - self.total += self.pending.clone(); + self.total = Balance::zero(); self.pending = Balance::zero(); self.pending_session_id = now; + } else { + self.total = Balance::zero(); + self.pending_session_id = now; } } - - pub fn clear(&mut self, now: SessionId) { - self.total = Balance::zero(); - self.pending = Balance::zero(); - self.pending_session_id = now; - } } pub struct ScheduleRewardsCalculator { @@ -119,8 +116,7 @@ impl ScheduleRewardsCalculator { let (cumulative, idx) = ScheduleRewardsPerLiquidity::::get((liquidity_asset_id, liquidity_assets_reward)); - if idx == (Pallet::::session_index() as u64) { - } else { + if (idx + 1) < (Pallet::::session_index() as u64) { let total_activated_liquidity = Self::total_activated_liquidity(liquidity_asset_id, liquidity_assets_reward); let total_schedule_rewards = @@ -129,7 +125,6 @@ impl ScheduleRewardsCalculator { ScheduleRewardsTotal::::mutate( (liquidity_asset_id, liquidity_assets_reward), |schedule| { - schedule.transfer_pending(session_id); schedule.clear(session_id); }, ); @@ -138,7 +133,7 @@ impl ScheduleRewardsCalculator { .unwrap_or_default(); ScheduleRewardsPerLiquidity::::insert( (liquidity_asset_id, liquidity_assets_reward), - (cumulative + pending, (Pallet::::session_index() as u64)), + (cumulative + pending, ((Pallet::::session_index() - 1) as u64)), ); } } diff --git a/pallets/proof-of-stake/src/tests.rs b/pallets/proof-of-stake/src/tests.rs index 099a9c586f..21a3051686 100644 --- a/pallets/proof-of-stake/src/tests.rs +++ b/pallets/proof-of-stake/src/tests.rs @@ -4313,3 +4313,182 @@ fn test_NotEnoughAssets_is_triggered_when_user_wants_to_deactive_more_tokens_tha ); }); } + +#[test] +#[serial] +fn test_cumulative_rewards_per_liquidity_over_multiple_sessions() { + ExtBuilder::new() + .issue(ALICE, LIQUIDITY_TOKEN, 100_000_000_000u128) + .issue(BOB, REWARD_TOKEN, 100_000_000_000_000_000_000_000_000u128) + .issue(BOB, LIQUIDITY_TOKEN, 500_000_000_000u128) + .execute_with_default_mocks(|| { + forward_to_block::(5); + ProofOfStake::reward_pool( + RuntimeOrigin::signed(BOB), + REWARDED_PAIR, + REWARD_TOKEN, + 10000u128, + 10u32.into(), + ) + .unwrap(); + + assert_eq!( + ScheduleRewardsPerLiquidity::::get((LIQUIDITY_TOKEN, REWARD_TOKEN)), + (U256::from(0), 0) + ); + + ProofOfStake::activate_liquidity_for_3rdparty_rewards( + RuntimeOrigin::signed(BOB), + LIQUIDITY_TOKEN, + 100u128, + REWARD_TOKEN, + None, + ) + .unwrap(); + + assert_eq!( + ScheduleRewardsPerLiquidity::::get((LIQUIDITY_TOKEN, REWARD_TOKEN)), + (U256::from(0), 0) + ); + + ScheduleRewardsCalculator::::update_cumulative_rewards( + LIQUIDITY_TOKEN, + REWARD_TOKEN, + ); + + assert_eq!( + ScheduleRewardsPerLiquidity::::get((LIQUIDITY_TOKEN, REWARD_TOKEN)), + (U256::from(0), 0) + ); + + forward_to_block::(9); + + ScheduleRewardsCalculator::::update_cumulative_rewards( + LIQUIDITY_TOKEN, + REWARD_TOKEN, + ); + assert_eq!( + ScheduleRewardsPerLiquidity::::get((LIQUIDITY_TOKEN, REWARD_TOKEN)), + (U256::from(0), 0) + ); + + forward_to_block::(18); + + ScheduleRewardsCalculator::::update_cumulative_rewards( + LIQUIDITY_TOKEN, + REWARD_TOKEN, + ); + assert_eq!( + ScheduleRewardsPerLiquidity::::get((LIQUIDITY_TOKEN, REWARD_TOKEN)), + (U256::from(0), 0) + ); + assert_eq!( + ProofOfStake::calculate_3rdparty_rewards_amount(BOB, LIQUIDITY_TOKEN, REWARD_TOKEN) + .unwrap(), + 0 + ); + + forward_to_block::(25); + + ScheduleRewardsCalculator::::update_cumulative_rewards( + LIQUIDITY_TOKEN, + REWARD_TOKEN, + ); + assert_eq!( + ScheduleRewardsPerLiquidity::::get((LIQUIDITY_TOKEN, REWARD_TOKEN)), + (U256::from(u128::MAX) * 1000 / 100, 1) + ); + + forward_to_block::(30); + ScheduleRewardsCalculator::::update_cumulative_rewards( + LIQUIDITY_TOKEN, + REWARD_TOKEN, + ); + + assert_eq!( + ScheduleRewardsPerLiquidity::::get((LIQUIDITY_TOKEN, REWARD_TOKEN)), + (U256::from(2) * (U256::from(u128::MAX) * 1000 / 100), 2) + ); + + assert_eq!( + ProofOfStake::calculate_3rdparty_rewards_amount(BOB, LIQUIDITY_TOKEN, REWARD_TOKEN) + .unwrap(), + 2000 + ); + + ScheduleRewardsCalculator::::update_cumulative_rewards( + LIQUIDITY_TOKEN, + REWARD_TOKEN, + ); + }); +} + +#[test] +#[serial] +fn test_amount_of_rewards_when_activation_happens_after_schedule_was_processed_in_current_session() +{ + ExtBuilder::new() + .issue(ALICE, LIQUIDITY_TOKEN, 100_000_000_000u128) + .issue(BOB, REWARD_TOKEN, 100_000_000_000_000_000_000_000_000u128) + .issue(BOB, LIQUIDITY_TOKEN, 500_000_000_000u128) + .execute_with_default_mocks(|| { + forward_to_block::(36); + ProofOfStake::reward_pool( + RuntimeOrigin::signed(BOB), + REWARDED_PAIR, + REWARD_TOKEN, + 100_000_000_000_000_000_000_000_000u128, + 7u32.into(), + ) + .unwrap(); + + forward_to_block::(40); + ProofOfStake::activate_liquidity_for_3rdparty_rewards( + RuntimeOrigin::signed(BOB), + LIQUIDITY_TOKEN, + 100_000_000_000u128, + REWARD_TOKEN, + None, + ) + .unwrap(); + + forward_to_block::(50); + ProofOfStake::activate_liquidity_for_3rdparty_rewards( + RuntimeOrigin::signed(ALICE), + LIQUIDITY_TOKEN, + 100_000_000_000u128, + REWARD_TOKEN, + None, + ) + .unwrap(); + + forward_to_block::(64); + ProofOfStake::activate_liquidity_for_3rdparty_rewards( + RuntimeOrigin::signed(BOB), + LIQUIDITY_TOKEN, + 100_000_000_000u128, + REWARD_TOKEN, + None, + ) + .unwrap(); + + roll_to_next_session::(); + roll_to_next_session::(); + roll_to_next_session::(); + roll_to_next_session::(); + roll_to_next_session::(); + + let alice_rewards = ProofOfStake::calculate_3rdparty_rewards_amount( + ALICE, + LIQUIDITY_TOKEN, + REWARD_TOKEN, + ) + .unwrap(); + let bob_rewards = + ProofOfStake::calculate_3rdparty_rewards_amount(BOB, LIQUIDITY_TOKEN, REWARD_TOKEN) + .unwrap(); + + // + 1 because of rounding + assert_eq!((alice_rewards + bob_rewards + 1), 100_000_000_000_000_000_000_000_000); + }); +}