Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stakingv3: rank dapps in-between tiers #1240

Merged
merged 24 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pallets/dapp-staking-v3/rpc/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

#![cfg_attr(not(feature = "std"), no_std)]

use astar_primitives::dapp_staking::{DAppId, EraNumber, PeriodNumber, TierId};
use astar_primitives::dapp_staking::{DAppId, EraNumber, PeriodNumber, TierAndRank};
use astar_primitives::BlockNumber;
pub use sp_std::collections::btree_map::BTreeMap;

Expand All @@ -42,6 +42,6 @@ sp_api::decl_runtime_apis! {
fn blocks_per_era() -> BlockNumber;

/// Get dApp tier assignment for the given dApp.
fn get_dapp_tier_assignment() -> BTreeMap<DAppId, TierId>;
fn get_dapp_tier_assignment() -> BTreeMap<DAppId, TierAndRank>;
}
}
4 changes: 2 additions & 2 deletions pallets/dapp-staking-v3/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1041,8 +1041,8 @@
&cleanup_marker.dapp_tiers_index,
DAppTierRewardsFor::<T> {
dapps: (0..T::MaxNumberOfContracts::get())
.map(|dapp_id| (dapp_id as DAppId, 0))
.collect::<BTreeMap<DAppId, TierId>>()
.map(|dapp_id| (dapp_id as DAppId, TierAndRank::new(0, 0)))
.collect::<BTreeMap<DAppId, TierAndRank>>()

Check failure on line 1045 in pallets/dapp-staking-v3/src/benchmarking/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

a value of type `std::collections::BTreeMap<u16, astar_primitives::dapp_staking::TierAndRank>` cannot be built from an iterator over elements of type `(u16, std::result::Result<astar_primitives::dapp_staking::TierAndRank, sp_runtime::ArithmeticError>)`

error[E0277]: a value of type `std::collections::BTreeMap<u16, astar_primitives::dapp_staking::TierAndRank>` cannot be built from an iterator over elements of type `(u16, std::result::Result<astar_primitives::dapp_staking::TierAndRank, sp_runtime::ArithmeticError>)` --> pallets/dapp-staking-v3/src/benchmarking/mod.rs:1045:22 | 1045 | .collect::<BTreeMap<DAppId, TierAndRank>>() | ^^^^^^^ value of type `std::collections::BTreeMap<u16, astar_primitives::dapp_staking::TierAndRank>` cannot be built from `std::iter::Iterator<Item=(u16, std::result::Result<astar_primitives::dapp_staking::TierAndRank, sp_runtime::ArithmeticError>)>` | = help: the trait `std::iter::FromIterator<(u16, std::result::Result<astar_primitives::dapp_staking::TierAndRank, sp_runtime::ArithmeticError>)>` is not implemented for `std::collections::BTreeMap<u16, astar_primitives::dapp_staking::TierAndRank>` = help: the trait `std::iter::FromIterator<(K, V)>` is implemented for `std::collections::BTreeMap<K, V>` note: the method call chain might not have had the expected associated types --> pallets/dapp-staking-v3/src/benchmarking/mod.rs:1044:22 | 1043 | dapps: (0..T::MaxNumberOfContracts::get()) | ----------------------------------- this expression has type `Range<u32>` 1044 | .map(|dapp_id| (dapp_id as DAppId, TierAndRank::new(0, 0))) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Iterator::Item` is `(u16, Result<TierAndRank, ArithmeticError>)` here note: required by a bound in `std::iter::Iterator::collect` --> /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/iter/traits/iterator.rs:2049:5
.try_into()
.expect("Using `MaxNumberOfContracts` as length; QED."),
rewards: vec![1_000_000_000_000; T::NumberOfTiers::get() as usize]
Expand Down
19 changes: 14 additions & 5 deletions pallets/dapp-staking-v3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ pub use sp_std::vec::Vec;
use astar_primitives::{
dapp_staking::{
AccountCheck, CycleConfiguration, DAppId, EraNumber, Observer as DAppStakingObserver,
PeriodNumber, SmartContractHandle, StakingRewardHandler, TierId, TierSlots as TierSlotFunc,
PeriodNumber, SmartContractHandle, StakingRewardHandler, TierAndRank, TierId,
TierSlots as TierSlotFunc,
},
oracle::PriceProvider,
Balance, BlockNumber,
Expand Down Expand Up @@ -1381,14 +1382,15 @@ pub mod pallet {
Error::<T>::RewardExpired
);

let (amount, tier_id) =
let (amount, tier_and_rank) =
dapp_tiers
.try_claim(dapp_info.id)
.map_err(|error| match error {
DAppTierError::NoDAppInTiers => Error::<T>::NoClaimableRewards,
_ => Error::<T>::InternalClaimDAppError,
})?;

let tier_id = tier_and_rank.tier_id();
// Get reward destination, and deposit the reward.
let beneficiary = dapp_info.reward_beneficiary();
T::StakingRewardHandler::payout_reward(&beneficiary, amount)
Expand Down Expand Up @@ -1648,7 +1650,7 @@ pub mod pallet {
}

/// Returns the dApp tier assignment for the current era, based on the current stake amounts.
pub fn get_dapp_tier_assignment() -> BTreeMap<DAppId, TierId> {
pub fn get_dapp_tier_assignment() -> BTreeMap<DAppId, TierAndRank> {
let protocol_state = ActiveProtocolState::<T>::get();

let (dapp_tiers, _count) = Self::get_dapp_tier_assignment_and_rewards(
Expand Down Expand Up @@ -1724,6 +1726,7 @@ pub mod pallet {

let mut global_idx = 0;
let mut tier_id = 0;
let mut upper_bound = Balance::zero();
for (tier_capacity, tier_threshold) in tier_config
.slots_per_tier
.iter()
Expand All @@ -1739,12 +1742,18 @@ pub mod pallet {
for (dapp_id, stake_amount) in dapp_stakes[global_idx..max_idx].iter() {
if tier_threshold.is_satisfied(*stake_amount) {
global_idx.saturating_inc();
dapp_tiers.insert(*dapp_id, tier_id);
let rank = TierAndRank::find_rank(
tier_threshold.threshold(),
upper_bound,
*stake_amount,
);
dapp_tiers.insert(*dapp_id, TierAndRank::new_saturated(tier_id, rank));
} else {
break;
}
}

// current threshold becomes upper bound for next tier
upper_bound = tier_threshold.threshold();
tier_id.saturating_inc();
}

Expand Down
4 changes: 2 additions & 2 deletions pallets/dapp-staking-v3/src/test/testing_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1015,7 +1015,7 @@ pub(crate) fn assert_claim_dapp_reward(
.get(&era)
.expect("Entry must exist.")
.clone();
let (expected_reward, expected_tier_id) = {
let (expected_reward, expected_tier_and_rank) = {
let mut info = pre_reward_info.clone();
info.try_claim(dapp_info.id).unwrap()
};
Expand All @@ -1029,7 +1029,7 @@ pub(crate) fn assert_claim_dapp_reward(
System::assert_last_event(RuntimeEvent::DappStaking(Event::DAppReward {
beneficiary: beneficiary.clone(),
smart_contract: smart_contract.clone(),
tier_id: expected_tier_id,
tier_id: expected_tier_and_rank.tier_id(),
era,
amount: expected_reward,
}));
Expand Down
14 changes: 7 additions & 7 deletions pallets/dapp-staking-v3/src/test/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use frame_support::{
use sp_runtime::{traits::Zero, FixedU128};

use astar_primitives::{
dapp_staking::{CycleConfiguration, EraNumber, SmartContractHandle},
dapp_staking::{CycleConfiguration, EraNumber, SmartContractHandle, TierAndRank},
Balance, BlockNumber,
};

Expand Down Expand Up @@ -2483,18 +2483,18 @@ fn get_dapp_tier_assignment_and_rewards_basic_example_works() {

// 1st tier checks
let (dapp_1_tier, dapp_2_tier) = (tier_assignment.dapps[&0], tier_assignment.dapps[&1]);
assert_eq!(dapp_1_tier, 0);
assert_eq!(dapp_2_tier, 0);
assert_eq!(dapp_1_tier, TierAndRank::new_saturated(0, 0));
assert_eq!(dapp_2_tier, TierAndRank::new_saturated(0, 0));

// 2nd tier checks
let (dapp_3_tier, dapp_4_tier) = (tier_assignment.dapps[&2], tier_assignment.dapps[&3]);
assert_eq!(dapp_3_tier, 1);
assert_eq!(dapp_4_tier, 1);
assert_eq!(dapp_3_tier, TierAndRank::new_saturated(1, 9));
assert_eq!(dapp_4_tier, TierAndRank::new_saturated(1, 9));

// 4th tier checks
let (dapp_5_tier, dapp_6_tier) = (tier_assignment.dapps[&4], tier_assignment.dapps[&5]);
assert_eq!(dapp_5_tier, 3);
assert_eq!(dapp_6_tier, 3);
assert_eq!(dapp_5_tier, TierAndRank::new_saturated(3, 0));
assert_eq!(dapp_6_tier, TierAndRank::new_saturated(3, 0));

// Sanity check - last dapp should not exists in the tier assignment
assert!(tier_assignment
Expand Down
27 changes: 21 additions & 6 deletions pallets/dapp-staking-v3/src/test/tests_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
// You should have received a copy of the GNU General Public License
// along with Astar. If not, see <http://www.gnu.org/licenses/>.

use astar_primitives::{dapp_staking::StandardTierSlots, Balance};
use astar_primitives::{
dapp_staking::{StandardTierSlots, TierAndRank},
Balance,
};
use frame_support::assert_ok;
use sp_arithmetic::fixed_point::FixedU128;
use sp_runtime::Permill;
Expand Down Expand Up @@ -2904,7 +2907,13 @@ fn dapp_tier_rewards_basic_tests() {
get_u32_type!(NumberOfTiers, 3);

// Example dApps & rewards
let dapps = BTreeMap::from([(1 as DAppId, 0 as TierId), (2, 0), (3, 1), (5, 1), (6, 2)]);
let dapps = BTreeMap::<DAppId, TierAndRank>::from([
(1, TierAndRank::new_saturated(0, 0)),
(2, TierAndRank::new_saturated(0, 0)),
(3, TierAndRank::new_saturated(1, 0)),
(5, TierAndRank::new_saturated(1, 0)),
(6, TierAndRank::new_saturated(2, 0)),
]);
let tier_rewards = vec![300, 20, 1];
let period = 2;

Expand All @@ -2916,16 +2925,22 @@ fn dapp_tier_rewards_basic_tests() {
.expect("Bounds are respected.");

// 1st scenario - claim reward for a dApps
let tier_id = dapps[&1];
let tier_and_rank = dapps[&1];
assert_eq!(
dapp_tier_rewards.try_claim(1),
Ok((tier_rewards[tier_id as usize], tier_id))
Ok((
tier_rewards[tier_and_rank.tier_id() as usize],
tier_and_rank
))
);

let tier_id = dapps[&5];
let tier_and_rank = dapps[&5];
assert_eq!(
dapp_tier_rewards.try_claim(5),
Ok((tier_rewards[tier_id as usize], tier_id))
Ok((
tier_rewards[tier_and_rank.tier_id() as usize],
tier_and_rank
))
);

// 2nd scenario - try to claim already claimed reward
Expand Down
37 changes: 26 additions & 11 deletions pallets/dapp-staking-v3/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ use sp_runtime::{
pub use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, vec::Vec};

use astar_primitives::{
dapp_staking::{DAppId, EraNumber, PeriodNumber, TierId, TierSlots as TierSlotsFunc},
dapp_staking::{DAppId, EraNumber, PeriodNumber, TierAndRank, TierSlots as TierSlotsFunc},
Balance, BlockNumber,
};

Expand Down Expand Up @@ -1738,7 +1738,7 @@ impl<NT: Get<u32>, T: TierSlotsFunc> TiersConfiguration<NT, T> {
#[scale_info(skip_type_params(MD, NT))]
pub struct DAppTierRewards<MD: Get<u32>, NT: Get<u32>> {
/// DApps and their corresponding tiers (or `None` if they have been claimed in the meantime)
pub dapps: BoundedBTreeMap<DAppId, TierId, MD>,
pub dapps: BoundedBTreeMap<DAppId, TierAndRank, MD>,
/// Rewards for each tier. First entry refers to the first tier, and so on.
pub rewards: BoundedVec<Balance, NT>,
/// Period during which this struct was created.
Expand All @@ -1760,7 +1760,7 @@ impl<MD: Get<u32>, NT: Get<u32>> DAppTierRewards<MD, NT> {
/// Attempt to construct `DAppTierRewards` struct.
/// If the provided arguments exceed the allowed capacity, return an error.
pub fn new(
dapps: BTreeMap<DAppId, TierId>,
dapps: BTreeMap<DAppId, TierAndRank>,
rewards: Vec<Balance>,
period: PeriodNumber,
) -> Result<Self, ()> {
Expand All @@ -1775,19 +1775,34 @@ impl<MD: Get<u32>, NT: Get<u32>> DAppTierRewards<MD, NT> {

/// Consume reward for the specified dapp id, returning its amount and tier Id.
/// In case dapp isn't applicable for rewards, or they have already been consumed, returns `None`.
pub fn try_claim(&mut self, dapp_id: DAppId) -> Result<(Balance, TierId), DAppTierError> {
pub fn try_claim(&mut self, dapp_id: DAppId) -> Result<(Balance, TierAndRank), DAppTierError> {
// Check if dApp Id exists.
let tier_id = self
let tier_and_rank = self
.dapps
.remove(&dapp_id)
.ok_or(DAppTierError::NoDAppInTiers)?;

Ok((
self.rewards
.get(tier_id as usize)
.map_or(Balance::zero(), |x| *x),
tier_id,
))
let (tier_id, rank) = tier_and_rank.destruct();

let mut amount = self
.rewards
.get(tier_id as usize)
.map_or(Balance::zero(), |x| *x);

if !rank.is_zero() {
// TODO: make sure this is the correct approach & add tests
ermalkaleci marked this conversation as resolved.
Show resolved Hide resolved
// (current_tier_reward - next_tier_reward) / MAX_RANK
let reward_per_rank = self
.rewards
.get(tier_id.saturating_add(1) as usize) // next tier reward
.map_or(Balance::zero(), |x| amount.saturating_sub(*x)) // delta amount
.saturating_div(TierAndRank::MAX_RANK.into()); // divided by ranks

let additional_reward = reward_per_rank.saturating_sub(rank.into());
amount = amount.saturating_add(additional_reward);
}

Ok((amount, tier_and_rank))
}
}

Expand Down
Loading
Loading