diff --git a/pallets/dapp-staking-v3/src/lib.rs b/pallets/dapp-staking-v3/src/lib.rs index 8994f55015..43ab418e4a 100644 --- a/pallets/dapp-staking-v3/src/lib.rs +++ b/pallets/dapp-staking-v3/src/lib.rs @@ -1254,6 +1254,9 @@ pub mod pallet { // Cleanup entry since the reward has been claimed StakerInfo::::remove(&account, &smart_contract); + Ledger::::mutate(&account, |ledger| { + ledger.contract_stake_count.saturating_dec(); + }); Self::deposit_event(Event::::BonusReward { account: account.clone(), diff --git a/pallets/dapp-staking-v3/src/test/testing_utils.rs b/pallets/dapp-staking-v3/src/test/testing_utils.rs index 1ed240409e..29cfd9e40d 100644 --- a/pallets/dapp-staking-v3/src/test/testing_utils.rs +++ b/pallets/dapp-staking-v3/src/test/testing_utils.rs @@ -919,6 +919,11 @@ pub(crate) fn assert_claim_bonus_reward(account: AccountId, smart_contract: &Moc !StakerInfo::::contains_key(&account, smart_contract), "Entry must be removed after successful reward claim." ); + assert_eq!( + pre_snapshot.ledger[&account].contract_stake_count, + Ledger::::get(&account).contract_stake_count + 1, + "Count must be reduced since the staker info entry was removed." + ); } /// Claim dapp reward for a particular era. diff --git a/pallets/dapp-staking-v3/src/test/tests.rs b/pallets/dapp-staking-v3/src/test/tests.rs index 2e97511007..40dba6ab31 100644 --- a/pallets/dapp-staking-v3/src/test/tests.rs +++ b/pallets/dapp-staking-v3/src/test/tests.rs @@ -1060,7 +1060,7 @@ fn stake_fails_due_to_too_many_staked_contracts() { // Advance to build&earn subperiod so we ensure non-loyal staking advance_to_next_subperiod(); - // Register smart contracts up the the max allowed number + // Register smart contracts up to the max allowed number for id in 1..=max_number_of_contracts { let smart_contract = MockSmartContract::Wasm(id.into()); assert_register(2, &MockSmartContract::Wasm(id.into())); @@ -2472,3 +2472,37 @@ fn stake_and_unstake_after_reward_claim_is_ok() { assert_unstake(account, &smart_contract, 1); }) } + +#[test] +fn stake_after_period_ends_with_max_staked_contracts() { + ExtBuilder::build().execute_with(|| { + let max_number_of_contracts: u32 = ::MaxNumberOfStakedContracts::get(); + + // Lock amount by staker + let account = 1; + assert_lock(account, 100 as Balance * max_number_of_contracts as Balance); + + // Register smart contracts up to the max allowed number + for id in 1..=max_number_of_contracts { + let smart_contract = MockSmartContract::Wasm(id.into()); + assert_register(2, &smart_contract); + assert_stake(account, &smart_contract, 10); + } + + // Advance to the next period, and claim ALL rewards + advance_to_next_period(); + for _ in 0..required_number_of_reward_claims(account) { + assert_claim_staker_rewards(account); + } + for id in 1..=max_number_of_contracts { + let smart_contract = MockSmartContract::Wasm(id.into()); + assert_claim_bonus_reward(account, &smart_contract); + } + + // Make sure it's possible to stake again + for id in 1..=max_number_of_contracts { + let smart_contract = MockSmartContract::Wasm(id.into()); + assert_stake(account, &smart_contract, 10); + } + }) +} diff --git a/pallets/dapp-staking-v3/src/types.rs b/pallets/dapp-staking-v3/src/types.rs index 569546ab03..a634e2ad75 100644 --- a/pallets/dapp-staking-v3/src/types.rs +++ b/pallets/dapp-staking-v3/src/types.rs @@ -390,6 +390,7 @@ pub struct AccountLedger> { /// Number of contract stake entries in storage. #[codec(compact)] pub contract_stake_count: u32, + // TODO: rename to staker_info_count? } impl Default for AccountLedger