diff --git a/Cargo.lock b/Cargo.lock index c47c2fe..d7d6682 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8365,6 +8365,7 @@ dependencies = [ "polkadot-sdk", "scale-info", "substrate-fixed", + "test-utils", ] [[package]] diff --git a/pallets/governance/Cargo.toml b/pallets/governance/Cargo.toml index 18b4d45..eba9239 100644 --- a/pallets/governance/Cargo.toml +++ b/pallets/governance/Cargo.toml @@ -41,3 +41,6 @@ pallet-torus0.workspace = true pallet-emission0.workspace = true pallet-governance-api.workspace = true + +[dev-dependencies] +test-utils.workspace = true diff --git a/pallets/governance/src/application.rs b/pallets/governance/src/application.rs index 7516fd0..25021d3 100644 --- a/pallets/governance/src/application.rs +++ b/pallets/governance/src/application.rs @@ -86,6 +86,10 @@ pub fn accept_application(application_id: u32) -> DispatchResu crate::AgentApplications::::remove(application.id); whitelist::add_to_whitelist::(application.agent_key.clone())?; + + let _ = + ::Currency::deposit_creating(&application.payer_key, application.cost); + crate::Pallet::::deposit_event(crate::Event::::ApplicationAccepted(application.id)); crate::Pallet::::deposit_event(crate::Event::::WhitelistAdded(application.agent_key)); diff --git a/pallets/governance/src/lib.rs b/pallets/governance/src/lib.rs index cea6564..8fe224b 100644 --- a/pallets/governance/src/lib.rs +++ b/pallets/governance/src/lib.rs @@ -1,11 +1,11 @@ #![cfg_attr(not(feature = "std"), no_std)] -mod application; -mod config; -mod curator; -mod ext; -mod proposal; -mod voting; +pub mod application; +pub mod config; +pub mod curator; +pub mod ext; +pub mod proposal; +pub mod voting; pub mod whitelist; use crate::application::AgentApplication; @@ -99,6 +99,9 @@ pub mod pallet { #[pallet::constant] type DefaultTreasuryEmissionFee: Get; + #[pallet::constant] + type DefaultProposalCost: Get>; + #[pallet::no_default_bounds] type RuntimeEvent: From> + IsType<::RuntimeEvent>; diff --git a/pallets/governance/src/proposal.rs b/pallets/governance/src/proposal.rs index c41a274..609c018 100644 --- a/pallets/governance/src/proposal.rs +++ b/pallets/governance/src/proposal.rs @@ -82,6 +82,7 @@ impl Proposal { min_weight_control_fee, min_staking_fee, dividends_participation_weight, + proposal_cost, } = data; pallet_torus0::MinNameLength::::set(min_name_length); @@ -95,9 +96,11 @@ impl Proposal { Percent::from_percent(min_weight_control_fee); constraints.min_staking_fee = Percent::from_percent(min_staking_fee); }); - pallet_emission0::MaxAllowedWeights::::set(max_allowed_weights); pallet_emission0::MinStakePerWeight::::set(min_weight_stake); + crate::GlobalGovernanceConfig::::mutate(|config| { + config.proposal_cost = proposal_cost; + }); } ProposalData::TransferDaoTreasury { account, amount } => { ::Currency::transfer( @@ -186,6 +189,24 @@ pub struct GlobalParamsData { pub min_weight_control_fee: u8, pub min_staking_fee: u8, pub dividends_participation_weight: Percent, + pub proposal_cost: BalanceOf, +} + +impl Default for GlobalParamsData { + fn default() -> Self { + Self { + min_name_length: T::DefaultMinNameLength::get(), + max_name_length: T::DefaultMaxNameLength::get(), + max_allowed_agents: T::DefaultMaxAllowedAgents::get(), + max_allowed_weights: T::DefaultMaxAllowedWeights::get(), + min_weight_stake: 0, + min_weight_control_fee: T::DefaultMinWeightControlFee::get(), + min_staking_fee: T::DefaultMinStakingFee::get(), + dividends_participation_weight: + ::DefaultDividendsParticipationWeight::get(), + proposal_cost: T::DefaultProposalCost::get(), + } + } } impl GlobalParamsData { @@ -199,16 +220,21 @@ impl GlobalParamsData { crate::Error::::InvalidMaxNameLength ); ensure!( - self.max_allowed_agents < 2000, + self.max_allowed_agents <= 50000, crate::Error::::InvalidMaxAllowedAgents ); ensure!( - self.max_allowed_weights < 2000, + self.max_allowed_weights <= 2000, crate::Error::::InvalidMaxAllowedWeights ); ensure!( - self.min_weight_control_fee > 10, - crate::Error::::InvalidMaxAllowedWeights + self.min_weight_control_fee >= 5, + crate::Error::::InvalidMinWeightControlFee + ); + + ensure!( + self.proposal_cost <= to_nano(50_000), + crate::Error::::InvalidProposalCost ); Ok(()) @@ -464,7 +490,7 @@ fn calc_stake( pallet_torus0::stake::sum_staking_to::(voter) }; - let delegated_stake = pallet_torus0::stake::get_staking_to_vector::(voter) + let delegated_stake = pallet_torus0::stake::get_staked_by_vector::(voter) .into_iter() .filter(|(staker, _)| !not_delegating.contains(staker)) .map(|(_, stake)| stake) @@ -525,7 +551,7 @@ pub fn tick_proposal_rewards(block_number: u64) { ); } -fn get_reward_allocation( +pub fn get_reward_allocation( governance_config: &GovernanceConfiguration, n: u16, ) -> Result { diff --git a/pallets/governance/src/voting.rs b/pallets/governance/src/voting.rs index 580f56e..aef2e11 100644 --- a/pallets/governance/src/voting.rs +++ b/pallets/governance/src/voting.rs @@ -77,6 +77,14 @@ pub fn remove_vote(voter: AccountIdOf, proposal_id: u64) -> } pub fn enable_delegation(delegator: AccountIdOf) -> DispatchResult { + crate::NotDelegatingVotingPower::::mutate(|delegators| { + delegators.remove(&delegator); + }); + + Ok(()) +} + +pub fn disable_delegation(delegator: AccountIdOf) -> DispatchResult { crate::NotDelegatingVotingPower::::mutate(|delegators| { delegators .try_insert(delegator.clone()) @@ -84,11 +92,3 @@ pub fn enable_delegation(delegator: AccountIdOf) -> Dispatc .map_err(|_| Error::::InternalError.into()) }) } - -pub fn disable_delegation(delegator: AccountIdOf) -> DispatchResult { - crate::NotDelegatingVotingPower::::mutate(|delegators| { - delegators.remove(&delegator); - }); - - Ok(()) -} diff --git a/pallets/governance/tests/application.rs b/pallets/governance/tests/application.rs new file mode 100644 index 0000000..6dd2605 --- /dev/null +++ b/pallets/governance/tests/application.rs @@ -0,0 +1,94 @@ +use pallet_governance::AgentApplications; +use pallet_governance::GlobalGovernanceConfig; +use test_utils::*; + +#[test] +fn whitelist_executes_application_correctly() { + new_test_ext().execute_with(|| { + zero_min_burn(); + + let key = 0; + let adding_key = 1; + pallet_governance::Curators::::insert(key, ()); + + let proposal_cost = GlobalGovernanceConfig::::get().agent_application_cost; + let data = "test".as_bytes().to_vec(); + + add_balance(key, proposal_cost + 1); + // first submit an application + let balance_before = get_balance(key); + + assert_ok!(pallet_governance::Pallet::::submit_application( + get_origin(key), + adding_key, + data.clone(), + )); + + let balance_after = get_balance(key); + assert_eq!(balance_after, balance_before - proposal_cost); + + let mut application_id: u32 = u32::MAX; + for (_, value) in AgentApplications::::iter() { + assert_eq!(value.agent_key, adding_key); + assert_eq!(value.data, data); + application_id = value.id; + } + + assert_ok!(pallet_governance::Pallet::::accept_application( + get_origin(key), + application_id + )); + + let balance_after_accept = get_balance(key); + + assert_eq!(balance_after_accept, balance_before); + + assert!(pallet_governance::whitelist::is_whitelisted::( + &adding_key + )); + }); +} + +#[test] +fn application_denied_doesnt_add_to_whitelist() { + new_test_ext().execute_with(|| { + let key = 0; + let adding_key = 1; + pallet_governance::Curators::::insert(key, ()); + + let proposal_cost = GlobalGovernanceConfig::::get().agent_application_cost; + let data = "test".as_bytes().to_vec(); + + add_balance(key, proposal_cost + 1); + let balance_before = get_balance(key); + + assert_ok!(pallet_governance::Pallet::::submit_application( + get_origin(key), + adding_key, + data.clone(), + )); + + let balance_after = get_balance(key); + assert_eq!(balance_after, balance_before - proposal_cost); + + let mut application_id: u32 = u32::MAX; + for (_, value) in AgentApplications::::iter() { + assert_eq!(value.agent_key, adding_key); + assert_eq!(value.data, data); + application_id = value.id; + } + + assert_ok!(pallet_governance::Pallet::::deny_application( + get_origin(key), + application_id + )); + + let balance_after_accept = get_balance(key); + + assert_eq!(balance_after_accept, balance_after); + + assert!(!pallet_governance::whitelist::is_whitelisted::( + &adding_key + )); + }); +} diff --git a/pallets/governance/tests/curator.rs b/pallets/governance/tests/curator.rs new file mode 100644 index 0000000..866f620 --- /dev/null +++ b/pallets/governance/tests/curator.rs @@ -0,0 +1,38 @@ +use crate::pallet_governance::Error; +use pallet_governance::Curators; +use polkadot_sdk::frame_support::assert_err; +use test_utils::*; + +#[test] +fn user_is_added_to_whitelist() { + new_test_ext().execute_with(|| { + let curator_key = 0; + let module_key = 1; + Curators::::insert(curator_key, ()); + + assert_ok!(pallet_governance::Pallet::::add_to_whitelist( + get_origin(curator_key), + module_key + )); + + assert!(pallet_governance::whitelist::is_whitelisted::( + &module_key + )) + }); +} + +#[test] +fn only_curators_can_whitelist() { + new_test_ext().execute_with(|| { + let not_curator_key = 0; + let module_key = 1; + + assert_err!( + pallet_governance::Pallet::::add_to_whitelist( + get_origin(not_curator_key), + module_key + ), + Error::::NotCurator + ); + }); +} diff --git a/pallets/governance/tests/voting.rs b/pallets/governance/tests/voting.rs new file mode 100644 index 0000000..32ef639 --- /dev/null +++ b/pallets/governance/tests/voting.rs @@ -0,0 +1,363 @@ +use pallet_governance::{ + config::GovernanceConfiguration, + proposal::{GlobalParamsData, ProposalStatus}, + DaoTreasuryAddress, Error, GlobalGovernanceConfig, Proposals, +}; +use polkadot_sdk::frame_support::assert_err; +use polkadot_sdk::{frame_support::assert_ok, sp_runtime::Percent}; +use test_utils::{ + add_balance, get_balance, get_origin, new_test_ext, step_block, to_nano, zero_min_burn, + AccountId, Test, +}; + +fn register(account: AccountId, _unused: u16, module: AccountId, stake: u128) { + if get_balance(account) < stake { + add_balance(account, stake); + } + + let _ = pallet_governance::whitelist::add_to_whitelist::(module); + + assert_ok!(pallet_torus0::agent::register::( + module, + b"agent".to_vec(), + b"url".to_vec(), + b"metadata".to_vec(), + )); + + assert!(get_balance(account) >= stake); + + let min_stake: u128 = pallet_torus0::MinAllowedStake::::get(); + if stake >= min_stake { + if get_balance(account) - stake < 1 { + add_balance(account, 1); + } + assert_ok!(pallet_torus0::stake::add_stake::( + account, module, stake + )); + } +} + +fn config(proposal_cost: u128, proposal_expiration: u64) { + pallet_governance::GlobalGovernanceConfig::::mutate(|config| { + config.proposal_cost = proposal_cost; + config.proposal_expiration = proposal_expiration; + }); +} + +fn vote(account: u32, proposal_id: u64, agree: bool) { + if pallet_torus0::stake::sum_staked_by::(&account) < 1 { + stake(account, account, to_nano(1)); + } + + assert_ok!(pallet_governance::voting::add_vote::( + account, + proposal_id, + agree + )); +} + +fn delegate(account: u32) { + assert_ok!(pallet_governance::Pallet::::enable_vote_delegation( + get_origin(account) + )); +} + +pub fn stake(account: u32, module: u32, stake: u128) { + // if get_balance(account) <= stake { + add_balance(account, stake); + // } + + if get_balance(account) - stake < 1 { + add_balance(account, 1); + } + + assert_ok!(pallet_torus0::stake::add_stake::( + account, module, stake + )); +} + +#[test] +fn global_governance_config_validates_parameters_correctly() { + new_test_ext().execute_with(|| { + assert_err!( + GlobalParamsData:: { + min_name_length: 1, + ..GlobalParamsData::default() + } + .validate(), + Error::::InvalidMinNameLength + ); + + assert_err!( + GlobalParamsData:: { + max_name_length: 300, + ..GlobalParamsData::default() + } + .validate(), + Error::::InvalidMaxNameLength + ); + + assert_err!( + GlobalParamsData:: { + max_allowed_agents: 50001, + ..GlobalParamsData::default() + } + .validate(), + Error::::InvalidMaxAllowedAgents + ); + + assert_err!( + GlobalParamsData:: { + max_allowed_weights: 2001, + ..GlobalParamsData::default() + } + .validate(), + Error::::InvalidMaxAllowedWeights + ); + + assert_err!( + GlobalParamsData:: { + min_weight_control_fee: 2, + ..GlobalParamsData::default() + } + .validate(), + Error::::InvalidMinWeightControlFee + ); + }); +} + +#[test] +fn global_proposal_validates_parameters() { + new_test_ext().execute_with(|| { + zero_min_burn(); + const KEY: u32 = 0; + add_balance(KEY, to_nano(100_000)); + + let test = |global_params| { + pallet_governance::Pallet::::add_global_params_proposal( + get_origin(KEY), + global_params, + b"metadata".to_vec(), + ) + }; + + test(GlobalParamsData { + min_name_length: 0, + ..Default::default() + }) + .expect_err("created proposal with invalid max name length"); + + test(GlobalParamsData::default()).expect("failed to create proposal with valid parameters"); + }); +} + +#[test] +fn global_custom_proposal_is_accepted_correctly() { + new_test_ext().execute_with(|| { + zero_min_burn(); + const FOR: u32 = 0; + const AGAINST: u32 = 1; + + let key = 0; + + register(FOR, 0, 0, to_nano(10)); + register(AGAINST, 0, 1, to_nano(5)); + + config(1, 100); + + add_balance(key, 1); + assert_ok!( + pallet_governance::Pallet::::add_global_custom_proposal( + get_origin(key), + b"metadata".to_vec() + ) + ); + + vote(FOR, 0, true); + vote(AGAINST, 0, false); + + step_block(100); + + assert_eq!( + Proposals::::get(0).unwrap().status, + ProposalStatus::Accepted { + block: 100, + stake_for: to_nano(10), + stake_against: to_nano(5), + } + ); + }); +} + +#[test] +fn global_proposal_is_refused_correctly() { + new_test_ext().execute_with(|| { + zero_min_burn(); + const FOR: u32 = 0; + const AGAINST: u32 = 1; + + let key = 0; + + register(FOR, 0, 0, to_nano(5)); + register(AGAINST, 0, 1, to_nano(10)); + + config(1, 100); + + add_balance(FOR, 1); + assert_ok!( + pallet_governance::Pallet::::add_global_custom_proposal( + get_origin(key), + b"metadata".to_vec() + ) + ); + + vote(FOR, 0, true); + vote(AGAINST, 0, false); + + step_block(100); + + assert_eq!( + Proposals::::get(0).unwrap().status, + ProposalStatus::Refused { + block: 100, + stake_for: 5_000_000_000_000_000_000, + stake_against: 10_000_000_000_000_000_000, + } + ); + }); +} + +#[test] +fn global_params_proposal_accepted() { + new_test_ext().execute_with(|| { + zero_min_burn(); + const KEY: u32 = 0; + + register(KEY, 0, 0, to_nano(10)); + config(1, 100); + + let data = GlobalParamsData { + proposal_cost: 69_420, + ..Default::default() + }; + + add_balance(KEY, 1); + pallet_governance::Pallet::::add_global_params_proposal( + get_origin(KEY), + data, + b"metadata".to_vec(), + ) + .unwrap(); + + vote(KEY, 0, true); + step_block(100); + + assert_eq!(GlobalGovernanceConfig::::get().proposal_cost, 69_420); + }); +} + +#[test] +fn global_proposals_counts_delegated_stake() { + new_test_ext().execute_with(|| { + zero_min_burn(); + + const FOR: u32 = 0; + const AGAINST: u32 = 1; + const FOR_DELEGATED: u32 = 2; + const AGAINST_DELEGATED: u32 = 3; + + let origin = get_origin(0); + + register(FOR, 0, FOR, to_nano(5)); + // delegate(FOR); + register(AGAINST, 0, AGAINST, to_nano(10)); + // delegate(AGAINST); + + stake(FOR_DELEGATED, FOR, to_nano(10)); + delegate(FOR_DELEGATED); + + stake(AGAINST_DELEGATED, AGAINST, to_nano(3)); + delegate(AGAINST_DELEGATED); + + config(1, 100); + + add_balance(FOR, 1); + + assert_ok!( + pallet_governance::Pallet::::add_global_custom_proposal(origin, vec![b'0'; 64]) + ); + + vote(FOR, 0, true); + vote(AGAINST, 0, false); + + step_block(100); + + assert_eq!( + Proposals::::get(0).unwrap().status, + ProposalStatus::Accepted { + block: 100, + stake_for: 15_000_000_000_000_000_000, + stake_against: 13_000_000_000_000_000_000, + } + ); + }); +} + +#[test] +fn creates_treasury_transfer_proposal_and_transfers() { + new_test_ext().execute_with(|| { + zero_min_burn(); + + pallet_governance::TreasuryEmissionFee::::set(Percent::from_percent(0)); + + let origin = get_origin(0); + pallet_governance::Pallet::::add_dao_treasury_transfer_proposal( + origin.clone(), + to_nano(5), + 0, + vec![b'0'; 64], + ) + .expect_err("proposal should not be created when treasury does not have enough money"); + + add_balance(DaoTreasuryAddress::::get(), to_nano(10)); + add_balance(0, to_nano(3)); + register(0, 0, 0, 0); + config(1, 100); + + pallet_governance::Pallet::::add_dao_treasury_transfer_proposal( + origin, + to_nano(5), + 0, + vec![b'0'; 64], + ) + .expect("proposal should be created"); + vote(0, 0, true); + + step_block(100); + + assert_eq!(get_balance(DaoTreasuryAddress::::get()), to_nano(5)); + assert_eq!(get_balance(0), to_nano(8)); + }); +} + +#[test] +fn rewards_wont_exceed_treasury() { + new_test_ext().execute_with(|| { + zero_min_burn(); + // Fill the governance address with 1 mil so we are not limited by the max allocation + let amount = to_nano(1_000_000_000); + let key = DaoTreasuryAddress::::get(); + add_balance(key, amount); + + let governance_config: GovernanceConfiguration = + GlobalGovernanceConfig::::get(); + let n = 0; + let allocation = + pallet_governance::proposal::get_reward_allocation::(&governance_config, n) + .unwrap(); + assert_eq!( + allocation.to_num::(), + governance_config.max_proposal_reward_treasury_allocation + ); + }); +} diff --git a/pallets/torus0/src/agent.rs b/pallets/torus0/src/agent.rs index 31912ab..39adaaa 100644 --- a/pallets/torus0/src/agent.rs +++ b/pallets/torus0/src/agent.rs @@ -41,6 +41,12 @@ pub fn register( crate::Error::::AgentAlreadyRegistered ); + // TODO: Take pruning scores into consideration + ensure!( + crate::Agents::::iter().count() < crate::MaxAllowedAgents::::get() as usize, + crate::Error::::MaxAllowedAgents + ); + ensure!( crate::RegistrationsThisBlock::::get() < crate::MaxRegistrationsPerBlock::::get(), crate::Error::::TooManyAgentRegistrationsThisBlock diff --git a/pallets/torus0/src/lib.rs b/pallets/torus0/src/lib.rs index 44e411a..a2df0c9 100644 --- a/pallets/torus0/src/lib.rs +++ b/pallets/torus0/src/lib.rs @@ -81,10 +81,12 @@ pub mod pallet { pub type MaxRegistrationsPerBlock = StorageValue<_, u16, ValueQuery, T::DefaultMaxRegistrationsPerBlock>; + // StakeTo #[pallet::storage] pub type StakingTo = StorageDoubleMap<_, Identity, T::AccountId, Identity, T::AccountId, BalanceOf>; + // StakeFrom #[pallet::storage] pub type StakedBy = StorageDoubleMap<_, Identity, T::AccountId, Identity, T::AccountId, BalanceOf>; diff --git a/pallets/torus0/src/stake.rs b/pallets/torus0/src/stake.rs index 3ab566b..e735ea1 100644 --- a/pallets/torus0/src/stake.rs +++ b/pallets/torus0/src/stake.rs @@ -29,11 +29,11 @@ pub fn add_stake( ) .map_err(|_| crate::Error::::NotEnoughBalanceToStake)?; - crate::StakingTo::::mutate(&staker, &staked, |stake| { + crate::StakedBy::::mutate(&staked, &staker, |stake| { *stake = Some(stake.unwrap_or(0).saturating_add(amount)) }); - crate::StakedBy::::mutate(&staked, &staker, |stake| { + crate::StakingTo::::mutate(&staker, &staked, |stake| { *stake = Some(stake.unwrap_or(0).saturating_add(amount)) }); diff --git a/runtime/src/configs.rs b/runtime/src/configs.rs index 4aac9ae..a6bfcb1 100644 --- a/runtime/src/configs.rs +++ b/runtime/src/configs.rs @@ -368,6 +368,8 @@ impl pallet_governance::Config for Runtime { type DefaultTreasuryEmissionFee = DefaultTreasuryEmissionFee; + type DefaultProposalCost = ConstU128<10_000_000_000_000_000_000_000>; + type RuntimeEvent = RuntimeEvent; type Currency = Balances; diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index 9cec629..b53e413 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -1,6 +1,6 @@ #![allow(non_camel_case_types)] -use std::num::NonZeroU128; +use std::{cell::RefCell, num::NonZeroU128}; use pallet_torus0::MinAllowedStake; use polkadot_sdk::{ @@ -11,7 +11,7 @@ use polkadot_sdk::{ }, frame_system, pallet_balances, polkadot_sdk_frame::runtime::prelude::*, - sp_core::H256, + sp_core::{Get, H256}, sp_io, sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, @@ -58,6 +58,18 @@ parameter_types! { pub const SS58Prefix: u16 = 42; } +thread_local! { + static DEFAULT_MIN_BURN: RefCell = const { RefCell::new(to_nano(10)) }; +} + +pub struct MinBurnConfig; + +impl Get for MinBurnConfig { + fn get() -> u128 { + DEFAULT_MIN_BURN.with(|v| *v.borrow()) + } +} + // Balance of an account. pub type Balance = u128; @@ -71,6 +83,20 @@ parameter_types! { pub const DefaultDividendsParticipationWeight: Percent = Percent::from_parts(40); } +impl pallet_governance_api::GovernanceApi for Test { + fn dao_treasury_address() -> AccountId { + pallet_governance::DaoTreasuryAddress::::get() + } + + fn treasury_emission_fee() -> Percent { + pallet_governance::TreasuryEmissionFee::::get() + } + + fn is_whitelisted(key: &AccountId) -> bool { + pallet_governance::Whitelist::::contains_key(key) + } +} + impl pallet_torus0::Config for Test { type DefaultMinValidatorStake = ConstU128<50_000_000_000_000_000_000_000>; @@ -92,9 +118,9 @@ impl pallet_torus0::Config for Test { type DefaultMinStakingFee = ConstU8<5>; - type DefaultMinWeightControlFee = ConstU8<4>; + type DefaultMinWeightControlFee = ConstU8<5>; - type DefaultMinBurn = ConstU128<10_000_000_000_000_000_000>; + type DefaultMinBurn = MinBurnConfig; type DefaultMaxBurn = ConstU128<150_000_000_000_000_000_000>; @@ -172,6 +198,8 @@ impl pallet_governance::Config for Test { type DefaultTreasuryEmissionFee = DefaultTreasuryEmissionFee; + type DefaultProposalCost = ConstU128<10_000_000_000_000_000_000_000>; + type RuntimeEvent = RuntimeEvent; type Currency = Balances; @@ -284,8 +312,10 @@ pub fn step_block(count: BlockNumber) { for block in current..current + count { Torus0::on_finalize(block); Emission0::on_finalize(block); + Governance::on_finalize(block); System::on_finalize(block); System::set_block_number(block + 1); + Governance::on_initialize(block + 1); System::on_initialize(block + 1); Emission0::on_initialize(block + 1); Torus0::on_initialize(block + 1); @@ -326,6 +356,10 @@ pub fn round_first_five(num: u64) -> u64 { } } +pub fn zero_min_burn() { + DEFAULT_MIN_BURN.set(0); +} + #[macro_export] macro_rules! assert_ok { ( $x:expr $(,)? ) => {