Skip to content

Commit

Permalink
Claim dapp reward tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinonard committed Oct 24, 2023
1 parent 3ded046 commit f2f8a5f
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 7 deletions.
10 changes: 6 additions & 4 deletions pallets/dapp-staking-v3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1273,14 +1273,14 @@ pub mod pallet {

// Get reward destination, and deposit the reward.
// TODO: should we check reward is greater than zero, or even more precise, it's greater than the existential deposit? Seems redundant but still...
let reward_destination = dapp_info.get_reward_destination();
T::Currency::deposit_creating(reward_destination, amount);
let beneficiary = dapp_info.get_reward_beneficiary();
T::Currency::deposit_creating(beneficiary, amount);

// Write back updated struct to prevent double reward claims
DAppTiers::<T>::insert(&era, dapp_tiers);

Self::deposit_event(Event::<T>::DAppReward {
beneficiary: reward_destination.clone(),
beneficiary: beneficiary.clone(),
smart_contract,
tier_id,
era,
Expand Down Expand Up @@ -1397,7 +1397,9 @@ pub mod pallet {
.iter()
.zip(tier_config.tier_thresholds.iter())
{
let max_idx = global_idx.saturating_add(*tier_capacity as usize);
let max_idx = global_idx
.saturating_add(*tier_capacity as usize)
.min(dapp_stakes.len());

// Iterate over dApps until one of two conditions has been met:
// 1. Tier has no more capacity
Expand Down
3 changes: 2 additions & 1 deletion pallets/dapp-staking-v3/src/test/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ impl ExtBuilder {
};

pallet_dapp_staking::StaticTierParams::<Test>::put(tier_params);
pallet_dapp_staking::TierConfig::<Test>::put(init_tier_config);
pallet_dapp_staking::TierConfig::<Test>::put(init_tier_config.clone());
pallet_dapp_staking::NextTierConfig::<Test>::put(init_tier_config);

// DappStaking::on_initialize(System::block_number());
}
Expand Down
70 changes: 69 additions & 1 deletion pallets/dapp-staking-v3/src/test/testing_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use crate::test::mock::*;
use crate::types::*;
use crate::{
pallet::Config, ActiveProtocolState, BlockNumberFor, ContractStake, CurrentEraInfo, DAppId,
EraRewards, Event, IntegratedDApps, Ledger, NextDAppId, PeriodEnd, PeriodEndInfo, StakerInfo,
DAppTiers, EraRewards, Event, IntegratedDApps, Ledger, NextDAppId, PeriodEnd, PeriodEndInfo,
StakerInfo,
};

use frame_support::{assert_ok, traits::Get};
Expand Down Expand Up @@ -49,6 +50,7 @@ pub(crate) struct MemorySnapshot {
contract_stake: HashMap<<Test as Config>::SmartContract, ContractStakeAmountSeries>,
era_rewards: HashMap<EraNumber, EraRewardSpan<<Test as Config>::EraRewardSpanLength>>,
period_end: HashMap<PeriodNumber, PeriodEndInfo>,
dapp_tiers: HashMap<EraNumber, DAppTierRewardsFor<Test>>,
}

impl MemorySnapshot {
Expand All @@ -66,6 +68,7 @@ impl MemorySnapshot {
contract_stake: ContractStake::<Test>::iter().collect(),
era_rewards: EraRewards::<Test>::iter().collect(),
period_end: PeriodEnd::<Test>::iter().collect(),
dapp_tiers: DAppTiers::<Test>::iter().collect(),
}
}

Expand Down Expand Up @@ -907,6 +910,71 @@ pub(crate) fn assert_claim_bonus_reward(account: AccountId) {
}
}

/// Claim dapp reward for a particular era.
pub(crate) fn assert_claim_dapp_reward(
account: AccountId,
smart_contract: &MockSmartContract,
era: EraNumber,
) {
let pre_snapshot = MemorySnapshot::new();
let dapp_info = pre_snapshot.integrated_dapps.get(smart_contract).unwrap();
let beneficiary = dapp_info.get_reward_beneficiary();
let pre_total_issuance = <Test as Config>::Currency::total_issuance();
let pre_free_balance = <Test as Config>::Currency::free_balance(beneficiary);

let (expected_reward, expected_tier_id) = {
let mut info = pre_snapshot
.dapp_tiers
.get(&era)
.expect("Entry must exist.")
.clone();

info.try_consume(dapp_info.id).unwrap()
};

// Claim dApp reward & verify event
assert_ok!(DappStaking::claim_dapp_reward(
RuntimeOrigin::signed(account),
smart_contract.clone(),
era,
));
System::assert_last_event(RuntimeEvent::DappStaking(Event::DAppReward {
beneficiary: beneficiary.clone(),
smart_contract: smart_contract.clone(),
tier_id: expected_tier_id,
era,
amount: expected_reward,
}));

// Verify post-state

let post_total_issuance = <Test as Config>::Currency::total_issuance();
assert_eq!(
post_total_issuance,
pre_total_issuance + expected_reward,
"Total issuance must increase by the reward amount."
);

let post_free_balance = <Test as Config>::Currency::free_balance(beneficiary);
assert_eq!(
post_free_balance,
pre_free_balance + expected_reward,
"Free balance must increase by the reward amount."
);

let post_snapshot = MemorySnapshot::new();
let mut info = post_snapshot
.dapp_tiers
.get(&era)
.expect("Entry must exist.")
.clone();
assert_eq!(
info.try_consume(dapp_info.id),
Err(DAppTierError::RewardAlreadyClaimed),
"It must not be possible to claim the same reward twice!.",
);
}

/// Returns from which starting era to which ending era can rewards be claimed for the specified account.
///
/// If `None` is returned, there is nothing to claim.
Expand Down
141 changes: 141 additions & 0 deletions pallets/dapp-staking-v3/src/test/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1406,3 +1406,144 @@ fn claim_bonus_reward_after_expiry_fails() {
);
})
}

#[test]
fn claim_dapp_reward_works() {
ExtBuilder::build().execute_with(|| {
// Register smart contract, lock&stake some amount
let dev_account = 1;
let smart_contract = MockSmartContract::default();
assert_register(dev_account, &smart_contract);

let account = 2;
let amount = 300;
assert_lock(account, amount);
assert_stake(account, &smart_contract, amount);

// Advance 2 eras so we have an entry for reward claiming
advance_to_era(ActiveProtocolState::<Test>::get().era + 2);
assert_eq!(ActiveProtocolState::<Test>::get().era, 3, "Sanity check");

assert_claim_dapp_reward(
account,
&smart_contract,
ActiveProtocolState::<Test>::get().era - 1,
);

// Advance to next era, and ensure rewards can be paid out to a custom beneficiary
let new_beneficiary = 17;
assert_set_dapp_reward_destination(dev_account, &smart_contract, Some(new_beneficiary));
advance_to_next_era();
assert_claim_dapp_reward(
account,
&smart_contract,
ActiveProtocolState::<Test>::get().era - 1,
);
})
}

#[test]
fn claim_dapp_reward_from_non_existing_contract_fails() {
ExtBuilder::build().execute_with(|| {
let smart_contract = MockSmartContract::default();
assert_noop!(
DappStaking::claim_dapp_reward(RuntimeOrigin::signed(1), smart_contract, 1),
Error::<Test>::ContractNotFound,
);
})
}

#[test]
fn claim_dapp_reward_from_invalid_era_fails() {
ExtBuilder::build().execute_with(|| {
// Register smart contract, lock&stake some amount
let smart_contract = MockSmartContract::default();
assert_register(1, &smart_contract);

let account = 2;
let amount = 300;
assert_lock(account, amount);
assert_stake(account, &smart_contract, amount);

// Advance 2 eras and try to claim from the ongoing era.
advance_to_era(ActiveProtocolState::<Test>::get().era + 2);
assert_noop!(
DappStaking::claim_dapp_reward(
RuntimeOrigin::signed(1),
smart_contract,
ActiveProtocolState::<Test>::get().era
),
Error::<Test>::InvalidClaimEra,
);

// Try to claim from the era which corresponds to the voting period. No tier info should
assert_noop!(
DappStaking::claim_dapp_reward(RuntimeOrigin::signed(1), smart_contract, 1),
Error::<Test>::NoDAppTierInfo,
);
})
}

#[test]
fn claim_dapp_reward_if_dapp_not_in_any_tier_fails() {
ExtBuilder::build().execute_with(|| {
// Register smart contract, lock&stake some amount
let smart_contract_1 = MockSmartContract::Wasm(3);
let smart_contract_2 = MockSmartContract::Wasm(5);
assert_register(1, &smart_contract_1);
assert_register(1, &smart_contract_2);

let account = 2;
let amount = 300;
assert_lock(account, amount);
assert_stake(account, &smart_contract_1, amount);

// Advance 2 eras and try to claim reward for non-staked dApp.
advance_to_era(ActiveProtocolState::<Test>::get().era + 2);
let account = 2;
let claim_era = ActiveProtocolState::<Test>::get().era - 1;
assert_noop!(
DappStaking::claim_dapp_reward(
RuntimeOrigin::signed(account),
smart_contract_2,
claim_era
),
Error::<Test>::NoClaimableRewards,
);
// Staked dApp should still be able to claim.
assert_claim_dapp_reward(account, &smart_contract_1, claim_era);
})
}

#[test]
fn claim_dapp_reward_twice_for_same_era_fails() {
ExtBuilder::build().execute_with(|| {
// Register smart contract, lock&stake some amount
let smart_contract = MockSmartContract::default();
assert_register(1, &smart_contract);

let account = 2;
let amount = 300;
assert_lock(account, amount);
assert_stake(account, &smart_contract, amount);

// Advance 3 eras and claim rewards.
advance_to_era(ActiveProtocolState::<Test>::get().era + 3);

// We can only claim reward ONCE for a particular era
let claim_era_1 = ActiveProtocolState::<Test>::get().era - 2;
assert_claim_dapp_reward(account, &smart_contract, claim_era_1);
assert_noop!(
DappStaking::claim_dapp_reward(
RuntimeOrigin::signed(account),
smart_contract,
claim_era_1
),
Error::<Test>::DAppRewardAlreadyClaimed,
);

// We can still claim for another valid era
let claim_era_2 = claim_era_1 + 1;
assert_claim_dapp_reward(account, &smart_contract, claim_era_2);
})
}
4 changes: 3 additions & 1 deletion pallets/dapp-staking-v3/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub type DAppTierRewardsFor<T> =
DAppTierRewards<MaxNumberOfContractsU32<T>, <T as Config>::NumberOfTiers>;

// Helper struct for converting `u16` getter into `u32`
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct MaxNumberOfContractsU32<T: Config>(PhantomData<T>);
impl<T: Config> Get<u32> for MaxNumberOfContractsU32<T> {
fn get() -> u32 {
Expand Down Expand Up @@ -247,7 +248,7 @@ pub struct DAppInfo<AccountId> {

impl<AccountId> DAppInfo<AccountId> {
/// Reward destination account for this dApp.
pub fn get_reward_destination(&self) -> &AccountId {
pub fn get_reward_beneficiary(&self) -> &AccountId {
match &self.reward_destination {
Some(account_id) => account_id,
None => &self.owner,
Expand Down Expand Up @@ -1565,6 +1566,7 @@ pub struct DAppTierRewards<MD: Get<u32>, NT: Get<u32>> {
pub dapps: BoundedVec<DAppTier, MD>,
/// Rewards for each tier. First entry refers to the first tier, and so on.
pub rewards: BoundedVec<Balance, NT>,
// TODO: perhaps I can add 'PeriodNumber' here so it's easy to identify expired rewards
}

impl<MD: Get<u32>, NT: Get<u32>> Default for DAppTierRewards<MD, NT> {
Expand Down

0 comments on commit f2f8a5f

Please sign in to comment.