Compare commits

...

5 Commits

Author SHA1 Message Date
Jędrzej Stuczyński d916854ba3 Revert "Ability to update mixnode config in maintenance mode"
This reverts commit dbd0e6b4fa.
2022-11-15 16:17:50 +00:00
Jędrzej Stuczyński dbd0e6b4fa Ability to update mixnode config in maintenance mode 2022-11-14 14:24:03 +00:00
Jędrzej Stuczyński abf7b57ccb Bugfix/correct staking supply accounting (#1706)
* Added staking_supply_scale_factor field on RewardingParams

* Scaling the amount of tokens released to the circulating/staking supplies
2022-10-27 11:05:55 +01:00
Jędrzej Stuczyński ec1988b745 Added code for gateway migration 2022-10-24 09:51:14 +01:00
Jędrzej Stuczyński 08bb9c0fbc Saving received messages from v1 contract into new storage 2022-10-24 09:51:13 +01:00
13 changed files with 332 additions and 265 deletions
Generated
+2
View File
@@ -737,7 +737,9 @@ name = "contracts-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"schemars",
"serde",
"thiserror",
]
[[package]]
@@ -9,3 +9,5 @@ edition = "2021"
[dependencies]
cosmwasm-std = "1.0.0"
serde = { version = "1.0", features = ["derive"] }
schemars = "0.8"
thiserror = "1"
@@ -1,7 +1,128 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use cosmwasm_std::Decimal;
use cosmwasm_std::Uint128;
use schemars::JsonSchema;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize};
use std::fmt::{self, Display, Formatter};
use std::ops::Mul;
use std::str::FromStr;
use thiserror::Error;
pub fn truncate_decimal(amount: Decimal) -> Uint128 {
amount * Uint128::new(1)
}
#[derive(Error, Debug)]
pub enum ContractsCommonError {
#[error("Provided percent value ({0}) is greater than 100%")]
InvalidPercent(Decimal),
#[error("{source}")]
StdErr {
#[from]
source: cosmwasm_std::StdError,
},
}
/// Percent represents a value between 0 and 100%
/// (i.e. between 0.0 and 1.0)
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Serialize, Deserialize, JsonSchema,
)]
pub struct Percent(#[serde(deserialize_with = "de_decimal_percent")] Decimal);
impl Percent {
pub fn new(value: Decimal) -> Result<Self, ContractsCommonError> {
if value > Decimal::one() {
Err(ContractsCommonError::InvalidPercent(value))
} else {
Ok(Percent(value))
}
}
pub fn is_zero(&self) -> bool {
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<Self, ContractsCommonError> {
Percent::new(Decimal::percent(value))
}
pub fn value(&self) -> Decimal {
self.0
}
pub fn round_to_integer(&self) -> u8 {
let hundred = Decimal::from_ratio(100u32, 1u32);
// we know the cast from u128 to u8 is a safe one since the internal value must be within 0 - 1 range
truncate_decimal(hundred * self.0).u128() as u8
}
}
impl Display for Percent {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let adjusted = Decimal::from_atomics(100u32, 0).unwrap() * self.0;
write!(f, "{}%", adjusted)
}
}
impl FromStr for Percent {
type Err = ContractsCommonError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Percent::new(Decimal::from_str(s)?)
}
}
impl Mul<Decimal> for Percent {
type Output = Decimal;
fn mul(self, rhs: Decimal) -> Self::Output {
self.0 * rhs
}
}
impl Mul<Percent> for Decimal {
type Output = Decimal;
fn mul(self, rhs: Percent) -> Self::Output {
rhs * self
}
}
impl Mul<Uint128> for Percent {
type Output = Uint128;
fn mul(self, rhs: Uint128) -> Self::Output {
self.0 * rhs
}
}
// implement custom Deserialize because we want to validate Percent has the correct range
fn de_decimal_percent<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
where
D: Deserializer<'de>,
{
let v = Decimal::deserialize(deserializer)?;
if v > Decimal::one() {
Err(D::Error::custom(
"provided decimal percent is larger than 100%",
))
} else {
Ok(v)
}
}
// TODO: there's no reason this couldn't be used for proper binaries, but in that case
// perhaps the struct should get renamed and moved to a "more" common crate
@@ -136,4 +136,7 @@ pub enum MixnetContractError {
#[error("Mixnode {mix_id} appears multiple times in the provided rewarded set update!")]
DuplicateRewardedSetNode { mix_id: MixId },
#[error("Migration from the old contract is currently in progress. The transaction is temporarily disabled until that is finished")]
MigrationInProgress,
}
@@ -8,7 +8,7 @@ use crate::reward_params::{
};
use crate::{delegation, ContractStateParams, MixId, Percent};
use crate::{Gateway, IdentityKey, MixNode};
use cosmwasm_std::Decimal;
use cosmwasm_std::{Addr, Coin, Decimal};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::time::Duration;
@@ -18,6 +18,7 @@ use std::time::Duration;
pub struct InstantiateMsg {
pub rewarding_validator_address: String,
pub vesting_contract_address: String,
pub v1_mixnet_contract_address: String,
pub rewarding_denom: String,
pub epochs_in_interval: u32,
@@ -31,6 +32,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 +53,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,
@@ -66,6 +69,80 @@ impl InitialRewardingParams {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
// special, first-time migration:
#[serde(rename = "m1")]
SaveOperator {
#[serde(rename = "a1")]
host: String,
#[serde(rename = "a2")]
mix_port: u16,
#[serde(rename = "a3")]
verloc_port: u16,
#[serde(rename = "a4")]
http_api_port: u16,
#[serde(rename = "a5")]
sphinx_key: String,
#[serde(rename = "a6")]
identity_key: IdentityKey,
#[serde(rename = "a7")]
version: String,
#[serde(rename = "a8")]
pledge_amount: Coin,
#[serde(rename = "a9")]
owner: Addr,
#[serde(rename = "a10")]
block_height: u64,
#[serde(rename = "a11")]
profit_margin_percent: u8,
#[serde(rename = "a12")]
proxy: Option<Addr>,
},
#[serde(rename = "m2")]
SaveDelegation {
#[serde(rename = "a1")]
owner: Addr,
#[serde(rename = "a2")]
mix_id: u32,
#[serde(rename = "a3")]
amount: Coin,
#[serde(rename = "a4")]
block_height: u64,
#[serde(rename = "a5")]
proxy: Option<Addr>,
},
#[serde(rename = "m3")]
SaveGateway {
#[serde(rename = "a1")]
pledge_amount: Coin,
#[serde(rename = "a2")]
owner: Addr,
#[serde(rename = "a3")]
block_height: u64,
#[serde(rename = "a4")]
gateway: Gateway,
#[serde(rename = "a5")]
proxy: Option<Addr>,
},
// state/sys-params-related
UpdateRewardingValidatorAddress {
address: String,
@@ -266,6 +343,7 @@ impl ExecuteMsg {
ExecuteMsg::TestingResolveAllPendingEvents { .. } => {
"resolving all pending events".into()
}
_ => unimplemented!("migration-specific messages are not supported by this"),
}
}
}
@@ -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.
@@ -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(),
@@ -24,87 +24,6 @@ pub type BlockHeight = u64;
pub type EpochEventId = u32;
pub type IntervalEventId = u32;
/// Percent represents a value between 0 and 100%
/// (i.e. between 0.0 and 1.0)
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Serialize, Deserialize, JsonSchema,
)]
pub struct Percent(#[serde(deserialize_with = "de_decimal_percent")] Decimal);
impl Percent {
pub fn new(value: Decimal) -> Result<Self, MixnetContractError> {
if value > Decimal::one() {
Err(MixnetContractError::InvalidPercent)
} else {
Ok(Percent(value))
}
}
pub fn is_zero(&self) -> bool {
self.0 == Decimal::zero()
}
pub fn from_percentage_value(value: u64) -> Result<Self, MixnetContractError> {
Percent::new(Decimal::percent(value))
}
pub fn value(&self) -> Decimal {
self.0
}
pub fn round_to_integer(&self) -> u8 {
let hundred = Decimal::from_ratio(100u32, 1u32);
// we know the cast from u128 to u8 is a safe one since the internal value must be within 0 - 1 range
truncate_decimal(hundred * self.0).u128() as u8
}
}
impl Display for Percent {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let adjusted = Decimal::from_atomics(100u32, 0).unwrap() * self.0;
write!(f, "{}%", adjusted)
}
}
impl Mul<Decimal> for Percent {
type Output = Decimal;
fn mul(self, rhs: Decimal) -> Self::Output {
self.0 * rhs
}
}
impl Mul<Percent> for Decimal {
type Output = Decimal;
fn mul(self, rhs: Percent) -> Self::Output {
rhs * self
}
}
impl Mul<Uint128> for Percent {
type Output = Uint128;
fn mul(self, rhs: Uint128) -> Self::Output {
self.0 * rhs
}
}
// implement custom Deserialize because we want to validate Percent has the correct range
fn de_decimal_percent<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
where
D: Deserializer<'de>,
{
let v = Decimal::deserialize(deserializer)?;
if v > Decimal::one() {
Err(D::Error::custom(
"provided decimal percent is larger than 100%",
))
} else {
Ok(v)
}
}
#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
pub struct LayerDistribution {
pub layer1: u64,
+2
View File
@@ -221,7 +221,9 @@ name = "contracts-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"schemars",
"serde",
"thiserror",
]
[[package]]
+103 -180
View File
@@ -2,18 +2,28 @@
// SPDX-License-Identifier: Apache-2.0
use crate::constants::{INITIAL_GATEWAY_PLEDGE_AMOUNT, INITIAL_MIXNODE_PLEDGE_AMOUNT};
use crate::delegations;
use crate::gateways::storage as gateways_storage;
use crate::interval::storage as interval_storage;
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnodes::storage as mixnode_storage;
use crate::mixnodes::storage::{assign_layer, next_mixnode_id_counter};
use crate::rewards::storage as rewards_storage;
use cosmwasm_std::{
entry_point, to_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
coin, entry_point, to_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response,
};
use cw_storage_plus::Item;
use mixnet_contract_common::error::MixnetContractError;
use mixnet_contract_common::{
ContractState, ContractStateParams, ExecuteMsg, InstantiateMsg, Interval, MigrateMsg, QueryMsg,
ContractState, ContractStateParams, Delegation, ExecuteMsg, GatewayBond, InstantiateMsg,
Interval, MigrateMsg, MixNode, MixNodeBond, MixNodeCostParams, MixNodeRewarding, Percent,
QueryMsg,
};
// To be removed once entire contract is unlocked
const V1_MIXNET_CONTRACT: Item<'_, String> = Item::new("v1_mixnet_contract");
fn default_initial_state(
owner: Addr,
rewarding_validator_address: Addr,
@@ -70,6 +80,8 @@ pub fn instantiate(
mixnode_storage::initialise_storage(deps.storage)?;
rewards_storage::initialise_storage(deps.storage, reward_params)?;
V1_MIXNET_CONTRACT.save(deps.storage, &msg.v1_mixnet_contract_address)?;
Ok(Response::default())
}
@@ -81,194 +93,102 @@ pub fn execute(
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, MixnetContractError> {
// only the old mixnet contract is allowed to request migration-specific commands here
if info.sender != V1_MIXNET_CONTRACT.load(deps.storage)? {
return Err(MixnetContractError::Unauthorized);
}
match msg {
// state/sys-params-related
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address(
deps, info, address,
)
}
ExecuteMsg::UpdateContractStateParams { updated_parameters } => {
crate::mixnet_contract_settings::transactions::try_update_contract_settings(
deps,
info,
updated_parameters,
)
}
ExecuteMsg::UpdateActiveSetSize {
active_set_size,
force_immediately,
} => crate::rewards::transactions::try_update_active_set_size(
deps,
env,
info,
active_set_size,
force_immediately,
),
ExecuteMsg::UpdateRewardingParams {
updated_params,
force_immediately,
} => crate::rewards::transactions::try_update_rewarding_params(
deps,
env,
info,
updated_params,
force_immediately,
),
ExecuteMsg::UpdateIntervalConfig {
epochs_in_interval,
epoch_duration_secs,
force_immediately,
} => crate::interval::transactions::try_update_interval_config(
deps,
env,
info,
epochs_in_interval,
epoch_duration_secs,
force_immediately,
),
ExecuteMsg::AdvanceCurrentEpoch {
new_rewarded_set,
expected_active_set_size,
} => crate::interval::transactions::try_advance_epoch(
deps,
env,
info,
new_rewarded_set,
expected_active_set_size,
),
ExecuteMsg::ReconcileEpochEvents { limit } => {
crate::interval::transactions::try_reconcile_epoch_events(deps, env, info, limit)
}
// mixnode-related:
ExecuteMsg::BondMixnode {
mix_node,
cost_params,
owner_signature,
} => crate::mixnodes::transactions::try_add_mixnode(
deps,
env,
info,
mix_node,
cost_params,
owner_signature,
),
ExecuteMsg::BondMixnodeOnBehalf {
mix_node,
cost_params,
ExecuteMsg::SaveOperator {
host,
mix_port,
verloc_port,
http_api_port,
sphinx_key,
identity_key,
version,
pledge_amount,
owner,
owner_signature,
} => crate::mixnodes::transactions::try_add_mixnode_on_behalf(
deps,
env,
info,
mix_node,
cost_params,
owner,
owner_signature,
),
ExecuteMsg::UnbondMixnode {} => {
crate::mixnodes::transactions::try_remove_mixnode(deps, env, info)
}
ExecuteMsg::UnbondMixnodeOnBehalf { owner } => {
crate::mixnodes::transactions::try_remove_mixnode_on_behalf(deps, env, info, owner)
}
ExecuteMsg::UpdateMixnodeCostParams { new_costs } => {
crate::mixnodes::transactions::try_update_mixnode_cost_params(
deps, env, info, new_costs,
)
}
ExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner } => {
crate::mixnodes::transactions::try_update_mixnode_cost_params_on_behalf(
deps, env, info, new_costs, owner,
)
}
ExecuteMsg::UpdateMixnodeConfig { new_config } => {
crate::mixnodes::transactions::try_update_mixnode_config(deps, info, new_config)
}
ExecuteMsg::UpdateMixnodeConfigOnBehalf { new_config, owner } => {
crate::mixnodes::transactions::try_update_mixnode_config_on_behalf(
deps, info, new_config, owner,
)
}
block_height,
profit_margin_percent,
proxy,
} => {
let mixnode = MixNode {
host,
mix_port,
verloc_port,
http_api_port,
sphinx_key,
identity_key,
version,
};
let layer = assign_layer(deps.storage)?;
// gateway-related:
ExecuteMsg::BondGateway {
gateway,
owner_signature,
} => crate::gateways::transactions::try_add_gateway(
deps,
env,
info,
gateway,
owner_signature,
),
ExecuteMsg::BondGatewayOnBehalf {
gateway,
owner,
owner_signature,
} => crate::gateways::transactions::try_add_gateway_on_behalf(
deps,
env,
info,
gateway,
owner,
owner_signature,
),
ExecuteMsg::UnbondGateway {} => {
crate::gateways::transactions::try_remove_gateway(deps, info)
}
ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
}
let cost_params = MixNodeCostParams {
// this value must be valid since we got it from the v1 contract which we trust
profit_margin_percent: Percent::from_percentage_value(profit_margin_percent as u64)
.unwrap(),
interval_operating_cost: coin(40_000_000, &pledge_amount.denom),
};
let node_id = next_mixnode_id_counter(deps.storage)?;
let current_epoch =
interval_storage::current_interval(deps.storage)?.current_epoch_absolute_id();
let mixnode_rewarding =
MixNodeRewarding::initialise_new(cost_params, &pledge_amount, current_epoch);
let mixnode_bond = MixNodeBond::new(
node_id,
owner,
pledge_amount,
layer,
mixnode,
proxy,
block_height,
);
// delegation-related:
ExecuteMsg::DelegateToMixnode { mix_id } => {
crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_id)
}
ExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, delegate } => {
crate::delegations::transactions::try_delegate_to_mixnode_on_behalf(
deps, env, info, mix_id, delegate,
)
}
ExecuteMsg::UndelegateFromMixnode { mix_id } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode(
deps, env, info, mix_id,
)
}
ExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, delegate } => {
crate::delegations::transactions::try_remove_delegation_from_mixnode_on_behalf(
deps, env, info, mix_id, delegate,
)
}
mixnode_storage::mixnode_bonds().save(deps.storage, node_id, &mixnode_bond)?;
rewards_storage::MIXNODE_REWARDING.save(deps.storage, node_id, &mixnode_rewarding)?;
// reward-related
ExecuteMsg::RewardMixnode {
Ok(Response::new())
}
ExecuteMsg::SaveDelegation {
owner,
mix_id,
performance,
} => crate::rewards::transactions::try_reward_mixnode(deps, env, info, mix_id, performance),
amount,
block_height,
proxy,
} => {
let mut mix_rewarding =
rewards_storage::MIXNODE_REWARDING.load(deps.storage, mix_id)?;
mix_rewarding.add_base_delegation(amount.amount);
rewards_storage::MIXNODE_REWARDING.save(deps.storage, mix_id, &mix_rewarding)?;
ExecuteMsg::WithdrawOperatorReward {} => {
crate::rewards::transactions::try_withdraw_operator_reward(deps, info)
let delegation = Delegation::new(
owner,
mix_id,
mix_rewarding.total_unit_reward,
amount,
block_height,
proxy,
);
let storage_key = delegation.storage_key();
delegations::storage::delegations().save(deps.storage, storage_key, &delegation)?;
Ok(Response::new())
}
ExecuteMsg::WithdrawOperatorRewardOnBehalf { owner } => {
crate::rewards::transactions::try_withdraw_operator_reward_on_behalf(deps, info, owner)
}
ExecuteMsg::WithdrawDelegatorReward { mix_id } => {
crate::rewards::transactions::try_withdraw_delegator_reward(deps, info, mix_id)
}
ExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => {
crate::rewards::transactions::try_withdraw_delegator_reward_on_behalf(
deps, info, mix_id, owner,
)
ExecuteMsg::SaveGateway {
pledge_amount,
owner,
block_height,
gateway,
proxy,
} => {
let bond = GatewayBond::new(pledge_amount, owner, block_height, gateway, proxy);
gateways_storage::gateways().save(deps.storage, bond.identity(), &bond)?;
Ok(Response::new())
}
// testing-only
#[cfg(feature = "contract-testing")]
ExecuteMsg::TestingResolveAllPendingEvents { limit } => {
crate::testing::transactions::try_resolve_all_pending_events(deps, env, limit)
}
_ => Err(MixnetContractError::MigrationInProgress),
}
}
@@ -491,12 +411,14 @@ mod tests {
let init_msg = InstantiateMsg {
rewarding_validator_address: "foomp123".to_string(),
vesting_contract_address: "bar456".to_string(),
v1_mixnet_contract_address: "whatever".to_string(),
rewarding_denom: "uatom".to_string(),
epochs_in_interval: 1234,
epoch_duration: Duration::from_secs(4321),
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 +457,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(),
+2 -1
View File
@@ -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;
@@ -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(),
+2
View File
@@ -665,7 +665,9 @@ name = "contracts-common"
version = "0.1.0"
dependencies = [
"cosmwasm-std",
"schemars",
"serde",
"thiserror",
]
[[package]]