3535
3636#![ cfg_attr( not( feature = "std" ) , no_std) ]
3737
38+ extern crate alloc;
39+
40+ use alloc:: vec;
41+ pub use alloc:: vec:: Vec ;
3842use frame_support:: {
3943 pallet_prelude:: * ,
4044 traits:: {
@@ -49,13 +53,12 @@ use sp_runtime::{
4953 traits:: { One , Saturating , UniqueSaturatedInto , Zero } ,
5054 Perbill , Permill , SaturatedConversion ,
5155} ;
52- pub use sp_std:: vec:: Vec ;
5356
5457use astar_primitives:: {
5558 dapp_staking:: {
5659 AccountCheck , CycleConfiguration , DAppId , EraNumber , Observer as DAppStakingObserver ,
5760 PeriodNumber , Rank , RankedTier , SmartContractHandle , StakingRewardHandler , TierId ,
58- TierSlots as TierSlotFunc , STANDARD_TIER_SLOTS_ARGS ,
61+ TierSlots as TierSlotFunc , MAX_ENCODED_RANK , STANDARD_TIER_SLOTS_ARGS ,
5962 } ,
6063 oracle:: PriceProvider ,
6164 Balance , BlockNumber ,
@@ -94,7 +97,7 @@ pub mod pallet {
9497 use super :: * ;
9598
9699 /// The current storage version.
97- pub const STORAGE_VERSION : StorageVersion = StorageVersion :: new ( 10 ) ;
100+ pub const STORAGE_VERSION : StorageVersion = StorageVersion :: new ( 11 ) ;
98101
99102 #[ pallet:: pallet]
100103 #[ pallet:: storage_version( STORAGE_VERSION ) ]
@@ -530,6 +533,8 @@ pub mod pallet {
530533 pub slot_number_args : ( u64 , u64 ) ,
531534 pub slots_per_tier : Vec < u16 > ,
532535 pub safeguard : Option < bool > ,
536+ pub rank_points : Vec < Vec < u8 > > ,
537+ pub base_reward_portion : Permill ,
533538 #[ serde( skip) ]
534539 pub _config : PhantomData < T > ,
535540 }
@@ -538,6 +543,15 @@ pub mod pallet {
538543 fn default ( ) -> Self {
539544 use sp_std:: vec;
540545 let num_tiers = T :: NumberOfTiers :: get ( ) ;
546+ let slots_per_tier = vec ! [ 100u16 ; num_tiers as usize ] ;
547+ let rank_points: Vec < Vec < u8 > > = slots_per_tier
548+ . iter ( )
549+ . map ( |& slots| {
550+ let capped = slots. min ( MAX_ENCODED_RANK as u16 ) ;
551+ ( 1 ..=capped as u8 ) . collect ( )
552+ } )
553+ . collect ( ) ;
554+
541555 Self {
542556 reward_portion : vec ! [ Permill :: from_percent( 100 / num_tiers) ; num_tiers as usize ] ,
543557 slot_distribution : vec ! [ Permill :: from_percent( 100 / num_tiers) ; num_tiers as usize ] ,
@@ -548,8 +562,10 @@ pub mod pallet {
548562 } )
549563 . collect ( ) ,
550564 slot_number_args : STANDARD_TIER_SLOTS_ARGS ,
551- slots_per_tier : vec ! [ 100 ; num_tiers as usize ] ,
565+ slots_per_tier,
552566 safeguard : None ,
567+ rank_points,
568+ base_reward_portion : Permill :: from_percent ( 50 ) ,
553569 _config : Default :: default ( ) ,
554570 }
555571 }
@@ -558,6 +574,12 @@ pub mod pallet {
558574 #[ pallet:: genesis_build]
559575 impl < T : Config > BuildGenesisConfig for GenesisConfig < T > {
560576 fn build ( & self ) {
577+ let rank_points: Vec < BoundedVec < u8 , ConstU32 < MAX_ENCODED_RANK > > > = self
578+ . rank_points
579+ . iter ( )
580+ . map ( |inner| BoundedVec :: try_from ( inner. clone ( ) ) . expect ( "Too many rank points" ) )
581+ . collect ( ) ;
582+
561583 // Prepare tier parameters & verify their correctness
562584 let tier_params = TierParameters :: < T :: NumberOfTiers > {
563585 reward_portion : BoundedVec :: < Permill , T :: NumberOfTiers > :: try_from (
@@ -573,6 +595,8 @@ pub mod pallet {
573595 )
574596 . expect ( "Invalid number of tier thresholds provided." ) ,
575597 slot_number_args : self . slot_number_args ,
598+ rank_points : BoundedVec :: try_from ( rank_points) . expect ( "Too many tiers" ) ,
599+ base_reward_portion : self . base_reward_portion ,
576600 } ;
577601 assert ! (
578602 tier_params. is_valid( ) ,
@@ -1882,6 +1906,7 @@ pub mod pallet {
18821906 dapp_stakes. sort_unstable_by ( |( _, amount_1) , ( _, amount_2) | amount_2. cmp ( amount_1) ) ;
18831907
18841908 let tier_config = TierConfig :: < T > :: get ( ) ;
1909+ let tier_params = StaticTierParams :: < T > :: get ( ) ;
18851910
18861911 // In case when tier has 1 more free slot, but two dApps with exactly same score satisfy the threshold,
18871912 // one of them will be assigned to the tier, and the other one will be assigned to the lower tier, if it exists.
@@ -1890,29 +1915,35 @@ pub mod pallet {
18901915 // There is no guarantee this will persist in the future, so it's best for dApps to do their
18911916 // best to avoid getting themselves into such situations.
18921917
1893- // 3. Calculate rewards.
1894- let tier_rewards = tier_config
1918+ // 3. Calculate tier allocations
1919+ let tier_allocations : Vec < Balance > = tier_config
18951920 . reward_portion
1921+ . iter ( )
1922+ . map ( |percent| * percent * dapp_reward_pool)
1923+ . collect ( ) ;
1924+
1925+ // 4. Base rewards = base_reward_portion% of allocation / slots capacity
1926+ let base_portion = tier_params. base_reward_portion ;
1927+ let base_rewards_per_tier: Vec < Balance > = tier_allocations
18961928 . iter ( )
18971929 . zip ( tier_config. slots_per_tier . iter ( ) )
1898- . map ( |( percent , slots ) | {
1899- if slots . is_zero ( ) {
1930+ . map ( |( allocation , capacity ) | {
1931+ if capacity . is_zero ( ) {
19001932 Zero :: zero ( )
19011933 } else {
1902- * percent * dapp_reward_pool / <u16 as Into < Balance > >:: into ( * slots)
1934+ base_portion
1935+ . mul_floor ( * allocation)
1936+ . saturating_div ( ( * capacity) . into ( ) )
19031937 }
19041938 } )
1905- . collect :: < Vec < _ > > ( ) ;
1939+ . collect ( ) ;
19061940
1907- // 4 .
1941+ // 5 .
19081942 // Iterate over configured tier and potential dApps.
19091943 // Each dApp will be assigned to the best possible tier if it satisfies the required condition,
19101944 // and tier capacity hasn't been filled yet.
19111945 let mut dapp_tiers = BTreeMap :: new ( ) ;
1912- let mut tier_slots = BTreeMap :: new ( ) ;
1913-
19141946 let mut upper_bound = Balance :: zero ( ) ;
1915- let mut rank_rewards = Vec :: new ( ) ;
19161947
19171948 for ( tier_id, ( tier_capacity, lower_bound) ) in tier_config
19181949 . slots_per_tier
@@ -1929,49 +1960,71 @@ pub mod pallet {
19291960 . take_while ( |( _, amount) | amount. ge ( lower_bound) )
19301961 . take ( * tier_capacity as usize )
19311962 {
1963+ let max_rank = tier_params
1964+ . rank_points
1965+ . get ( tier_id)
1966+ . map ( |v| v. len ( ) . saturating_sub ( 1 ) as u8 ) // ranks 0 to len-1, never exceed valid indices
1967+ . unwrap_or ( 0 ) ;
1968+
19321969 let rank = if T :: RankingEnabled :: get ( ) {
1933- RankedTier :: find_rank ( * lower_bound, upper_bound, * staked_amount)
1970+ RankedTier :: find_rank ( * lower_bound, upper_bound, * staked_amount, max_rank )
19341971 } else {
19351972 0
19361973 } ;
1937- tier_slots. insert ( * dapp_id, RankedTier :: new_saturated ( tier_id as u8 , rank) ) ;
1938- }
1939-
1940- // sum of all ranks for this tier
1941- let ranks_sum = tier_slots
1942- . iter ( )
1943- . fold ( 0u32 , |accum, ( _, x) | accum. saturating_add ( x. rank ( ) . into ( ) ) ) ;
19441974
1945- let reward_per_rank = if ranks_sum. is_zero ( ) {
1946- Balance :: zero ( )
1947- } else {
1948- // calculate reward per rank
1949- let tier_reward = tier_rewards. get ( tier_id) . copied ( ) . unwrap_or_default ( ) ;
1950- let empty_slots = tier_capacity. saturating_sub ( tier_slots. len ( ) as u16 ) ;
1951- let remaining_reward = tier_reward. saturating_mul ( empty_slots. into ( ) ) ;
1952- // make sure required reward doesn't exceed remaining reward
1953- let reward_per_rank = tier_reward. saturating_div ( RankedTier :: MAX_RANK . into ( ) ) ;
1954- let expected_reward_for_ranks =
1955- reward_per_rank. saturating_mul ( ranks_sum. into ( ) ) ;
1956- let reward_for_ranks = expected_reward_for_ranks. min ( remaining_reward) ;
1957- // re-calculate reward per rank based on available reward
1958- reward_for_ranks. saturating_div ( ranks_sum. into ( ) )
1959- } ;
1975+ let ranked_tier = RankedTier :: new_saturated ( tier_id as u8 , rank, max_rank) ;
1976+ dapp_tiers. insert ( * dapp_id, ranked_tier) ;
1977+ }
19601978
1961- rank_rewards. push ( reward_per_rank) ;
1962- dapp_tiers. append ( & mut tier_slots) ;
19631979 upper_bound = * lower_bound; // current threshold becomes upper bound for next tier
19641980 }
19651981
1966- // 5.
1982+ // 6. Calculate rank_rewards (reward per rank point for each tier)
1983+ // rank_rewards[tier] = (remainder portion of base% of tier allocation) / sum_of_all_rank_points_in_tier
1984+ let rank_rewards: Vec < Balance > = tier_allocations
1985+ . iter ( )
1986+ . zip ( tier_config. slots_per_tier . iter ( ) )
1987+ . enumerate ( )
1988+ . map ( |( tier_id, ( allocation, slots) ) | {
1989+ // If tier has no slots, no rank rewards can be claimed
1990+ if slots. is_zero ( ) {
1991+ return Zero :: zero ( ) ;
1992+ }
1993+
1994+ // Sum ALL configured rank_points for this tier
1995+ let total_points: u32 = tier_params
1996+ . rank_points
1997+ . get ( tier_id)
1998+ . map ( |points| points. iter ( ) . map ( |& p| p as u32 ) . sum ( ) )
1999+ . unwrap_or ( 0 ) ;
2000+
2001+ if total_points. is_zero ( ) {
2002+ Zero :: zero ( )
2003+ } else {
2004+ let rank_portion =
2005+ Permill :: one ( ) . saturating_sub ( tier_params. base_reward_portion ) ;
2006+ let rank_pool = rank_portion. mul_floor ( * allocation) ;
2007+ rank_pool. saturating_div ( total_points. into ( ) )
2008+ }
2009+ } )
2010+ . collect ( ) ;
2011+
2012+ let rank_points_vec: Vec < Vec < u8 > > = tier_params
2013+ . rank_points
2014+ . iter ( )
2015+ . map ( |inner_bv| inner_bv. clone ( ) . into_inner ( ) )
2016+ . collect ( ) ;
2017+
2018+ // 7.
19672019 // Prepare and return tier & rewards info.
19682020 // In case rewards creation fails, we just write the default value. This should never happen though.
19692021 (
19702022 DAppTierRewards :: < T :: MaxNumberOfContracts , T :: NumberOfTiers > :: new (
19712023 dapp_tiers,
1972- tier_rewards ,
2024+ base_rewards_per_tier ,
19732025 period,
19742026 rank_rewards,
2027+ rank_points_vec,
19752028 )
19762029 . unwrap_or_default ( ) ,
19772030 counter,
0 commit comments