diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs index 320212143d..e9b399c02e 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs @@ -47,6 +47,14 @@ impl Percent { self.0 == Decimal::zero() } + pub fn zero() -> Self { + Self(Decimal::zero()) + } + + pub fn hundred() -> Self { + Self(Decimal::one()) + } + pub fn from_percentage_value(value: u64) -> Result { Percent::new(Decimal::percent(value)) } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index ee172a4c20..c2046af9b0 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -31,6 +31,7 @@ pub struct InitialRewardingParams { pub initial_reward_pool: Decimal, pub initial_staking_supply: Decimal, + pub staking_supply_scale_factor: Percent, pub sybil_resistance: Percent, pub active_set_work_factor: Decimal, pub interval_pool_emission: Percent, @@ -51,6 +52,7 @@ impl InitialRewardingParams { interval: IntervalRewardParams { reward_pool: self.initial_reward_pool, staking_supply: self.initial_staking_supply, + staking_supply_scale_factor: self.staking_supply_scale_factor, epoch_reward_budget, stake_saturation_point, sybil_resistance: self.sybil_resistance, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs index fc543a3c02..73459f42d9 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs @@ -26,6 +26,11 @@ pub struct IntervalRewardParams { #[cfg_attr(feature = "generate-ts", ts(type = "string"))] pub staking_supply: Decimal, + /// Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set. + /// Also known as `beta`. + #[cfg_attr(feature = "generate-ts", ts(type = "string"))] + pub staking_supply_scale_factor: Percent, + // computed values /// Current value of the computed reward budget per epoch, per node. /// It is expected to be constant throughout the interval. diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/simulator/mod.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/simulator/mod.rs index bd4fc8d8d3..5fe1c46175 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/simulator/mod.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/simulator/mod.rs @@ -40,7 +40,12 @@ impl Simulator { if self.interval.current_interval_id() + 1 == updated.current_interval_id() { let old = self.system_rewarding_params.interval; let reward_pool = old.reward_pool - self.pending_reward_pool_emission; - let staking_supply = old.staking_supply + self.pending_reward_pool_emission; + let staking_supply = old.staking_supply + + self + .system_rewarding_params + .interval + .staking_supply_scale_factor + * self.pending_reward_pool_emission; let epoch_reward_budget = reward_pool / Decimal::from_atomics(self.interval.epochs_in_interval(), 0).unwrap() * old.interval_pool_emission.value(); @@ -51,6 +56,7 @@ impl Simulator { interval: IntervalRewardParams { reward_pool, staking_supply, + staking_supply_scale_factor: old.staking_supply_scale_factor, epoch_reward_budget, stake_saturation_point, sybil_resistance: old.sybil_resistance, @@ -209,6 +215,7 @@ mod tests { interval: IntervalRewardParams { reward_pool: Decimal::from_atomics(reward_pool, 0).unwrap(), // 250M * 1M (we're expressing it all in base tokens) staking_supply: Decimal::from_atomics(staking_supply, 0).unwrap(), // 100M * 1M + staking_supply_scale_factor: Percent::hundred(), epoch_reward_budget, stake_saturation_point, sybil_resistance: Percent::from_percentage_value(30).unwrap(), @@ -537,6 +544,7 @@ mod tests { interval: IntervalRewardParams { reward_pool: Decimal::from_atomics(reward_pool, 0).unwrap(), staking_supply: Decimal::from_atomics(staking_supply, 0).unwrap(), + staking_supply_scale_factor: Percent::hundred(), epoch_reward_budget, stake_saturation_point, sybil_resistance: Percent::from_percentage_value(30).unwrap(), diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index 1e91323c6c..af41f50b83 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -497,6 +497,7 @@ mod tests { initial_rewarding_params: InitialRewardingParams { initial_reward_pool: Decimal::from_atomics(100_000_000_000_000u128, 0).unwrap(), initial_staking_supply: Decimal::from_atomics(123_456_000_000_000u128, 0).unwrap(), + staking_supply_scale_factor: Percent::hundred(), sybil_resistance: Percent::from_percentage_value(23).unwrap(), active_set_work_factor: Decimal::from_atomics(10u32, 0).unwrap(), interval_pool_emission: Percent::from_percentage_value(1).unwrap(), @@ -535,6 +536,7 @@ mod tests { interval: IntervalRewardParams { reward_pool: Decimal::from_atomics(100_000_000_000_000u128, 0).unwrap(), staking_supply: Decimal::from_atomics(123_456_000_000_000u128, 0).unwrap(), + staking_supply_scale_factor: Percent::hundred(), epoch_reward_budget: expected_epoch_reward_budget, stake_saturation_point: expected_stake_saturation_point, sybil_resistance: Percent::from_percentage_value(23).unwrap(), diff --git a/contracts/mixnet/src/rewards/helpers.rs b/contracts/mixnet/src/rewards/helpers.rs index 02cdf83e68..894c260e6f 100644 --- a/contracts/mixnet/src/rewards/helpers.rs +++ b/contracts/mixnet/src/rewards/helpers.rs @@ -20,7 +20,8 @@ pub(crate) fn apply_reward_pool_changes( let reward_pool = rewarding_params.interval.reward_pool - pending_pool_change.removed + pending_pool_change.added; - let staking_supply = rewarding_params.interval.staking_supply + pending_pool_change.removed; + let staking_supply = rewarding_params.interval.staking_supply + + rewarding_params.interval.staking_supply_scale_factor * pending_pool_change.removed; let epoch_reward_budget = reward_pool / Decimal::from_atomics(interval.epochs_in_interval(), 0).unwrap() * rewarding_params.interval.interval_pool_emission; diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index 07b89671cd..a7cf4a3875 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -768,6 +768,7 @@ pub mod test_helpers { InitialRewardingParams { initial_reward_pool: Decimal::from_atomics(reward_pool, 0).unwrap(), // 250M * 1M (we're expressing it all in base tokens) initial_staking_supply: Decimal::from_atomics(staking_supply, 0).unwrap(), // 100M * 1M + staking_supply_scale_factor: Percent::hundred(), sybil_resistance: Percent::from_percentage_value(30).unwrap(), active_set_work_factor: Decimal::from_atomics(10u32, 0).unwrap(), interval_pool_emission: Percent::from_percentage_value(2).unwrap(),