diff --git a/integration-tests/src/tests/governance.rs b/integration-tests/src/tests/governance.rs index d43319a63..5dca84f09 100644 --- a/integration-tests/src/tests/governance.rs +++ b/integration-tests/src/tests/governance.rs @@ -16,11 +16,10 @@ use frame_support::{ fungible::InspectFreeze, tokens::Precision, Imbalance, LockableCurrency, ReservableCurrency, StorePreimage, }, }; -use pallet_democracy::{AccountVote, Conviction, ReferendumInfo, Vote}; +use pallet_democracy::{AccountVote, Conviction, ReferendumInfo, Vote, GetElectorate}; use pallet_vesting::VestingInfo; use polimec_base_runtime::{ - Balances, Council, Democracy, Elections, ParachainStaking, Preimage, RuntimeOrigin, TechnicalCommittee, Treasury, - Vesting, + Balances, Council, Democracy, Elections, ParachainStaking, PayMaster, Preimage, RuntimeOrigin, TechnicalCommittee, Treasury, Vesting }; use tests::defaults::*; use xcm_emulator::get_account_id_from_seed; @@ -178,6 +177,18 @@ fn democracy_works() { }); } +// Test that electorate configuration calculates correctly. +// Electorate is the total issuance minus the sum of the Growth + Operational treasury. +#[test] +fn electorate_calculates_correctly() { + PolimecBase::execute_with(|| { + let total_issuance = Balances::total_issuance(); + assert_ok!(Balances::write_balance(&Treasury::account_id(), 1000 * PLMC)); + assert_ok!(Balances::write_balance(&::PayMaster::get(), 1000 * PLMC)); + assert_eq!(::Electorate::get_electorate(), total_issuance - 2000 * PLMC); + }) +} + /// Test that a user with staked balance can vote on a democracy proposal. #[test] fn user_can_vote_with_staked_balance() { diff --git a/pallets/democracy/src/lib.rs b/pallets/democracy/src/lib.rs index 6c361d6fe..0bc6be020 100644 --- a/pallets/democracy/src/lib.rs +++ b/pallets/democracy/src/lib.rs @@ -169,6 +169,7 @@ use sp_std::prelude::*; mod conviction; mod types; +mod traits; mod vote; mod vote_threshold; pub mod weights; @@ -177,6 +178,7 @@ pub use pallet::*; pub use types::{ Delegations, MetadataOwner, PropIndex, ReferendumIndex, ReferendumInfo, ReferendumStatus, Tally, UnvoteScope, }; +pub use traits::GetElectorate; pub use vote::{AccountVote, Vote, Voting}; pub use vote_threshold::{Approved, VoteThreshold}; pub use weights::WeightInfo; @@ -343,6 +345,9 @@ pub mod pallet { /// Handler for the unbalanced reduction when slashing a preimage deposit. type Slash: OnUnbalanced>; + + /// Type returning the total electorate. + type Electorate: GetElectorate>; } /// The number of (public) proposals that have been made so far. @@ -1516,7 +1521,7 @@ impl Pallet { index: ReferendumIndex, status: ReferendumStatus, BoundedCallOf, BalanceOf>, ) -> bool { - let total_issuance = T::Fungible::total_issuance(); + let total_issuance = T::Electorate::get_electorate(); let approved = status.threshold.approved(status.tally, total_issuance); if approved { diff --git a/pallets/democracy/src/tests.rs b/pallets/democracy/src/tests.rs index dffa54a70..cd7659a90 100644 --- a/pallets/democracy/src/tests.rs +++ b/pallets/democracy/src/tests.rs @@ -158,11 +158,19 @@ impl SortedMembers for OneToFive { fn add(_m: &u64) {} } +pub struct Electorate; +impl GetElectorate> for Electorate { + fn get_electorate() -> BalanceOf { + Balances::total_issuance() + } +} + impl Config for Test { type BlacklistOrigin = EnsureRoot; type CancelProposalOrigin = EnsureRoot; type CancellationOrigin = EnsureSignedBy; type CooloffPeriod = ConstU64<2>; + type Electorate = Electorate; type EnactmentPeriod = ConstU64<2>; type ExternalDefaultOrigin = EnsureSignedBy; type ExternalMajorityOrigin = EnsureSignedBy; diff --git a/pallets/democracy/src/traits.rs b/pallets/democracy/src/traits.rs new file mode 100644 index 000000000..5183f8479 --- /dev/null +++ b/pallets/democracy/src/traits.rs @@ -0,0 +1,18 @@ +// Copyright (C) Parity Technologies (UK) Ltd. + +// Polimec Blockchain – https://www.polimec.org/ +// Copyright (C) Polimec 2022. All rights reserved. + +// This library includes code from Substrate, which is licensed +// under both the GNU General Public License version 3 (GPLv3) and the +// Apache License 2.0. You may choose to redistribute and/or modify this +// code under either the terms of the GPLv3 or the Apache 2.0 License, +// whichever suits your needs. + +pub trait GetElectorate { + /// Calculate the total size of the electorate (tokens in circulation that might be used + /// for voting) in terms of total Balance. + /// Used for the referendum approval threshold calculation. + /// Example: Total number of tokens in the system - total number of tokens in the treasury. + fn get_electorate() -> Balance; +} \ No newline at end of file diff --git a/runtimes/base/src/lib.rs b/runtimes/base/src/lib.rs index c28115815..5116cfa9c 100644 --- a/runtimes/base/src/lib.rs +++ b/runtimes/base/src/lib.rs @@ -24,10 +24,11 @@ extern crate frame_benchmarking; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use frame_support::{ construct_runtime, parameter_types, - traits::{fungible::Credit, tokens, ConstU32, Contains, EitherOfDiverse, InstanceFilter, PrivilegeCmp}, + traits::{fungible::{Credit, Inspect}, tokens, ConstU32, Contains, EitherOfDiverse, InstanceFilter, PrivilegeCmp}, weights::{ConstantMultiplier, Weight}, }; use frame_system::{EnsureRoot, EnsureSigned}; +use pallet_democracy::GetElectorate; use pallet_oracle_ocw::types::AssetName; use parachains_common::AssetIdForTrustBackedAssets as AssetId; use parity_scale_codec::Encode; @@ -484,6 +485,16 @@ impl pallet_elections_phragmen::Config for Runtime { type WeightInfo = pallet_elections_phragmen::weights::SubstrateWeight; } +pub struct Electorate; +impl GetElectorate for Electorate { + fn get_electorate() -> Balance { + let total_issuance = Balances::total_issuance(); + let growth_treasury_balance = Balances::balance(&Treasury::account_id()); + let protocol_treasury_balance = Balances::balance(&PayMaster::get()); + total_issuance.saturating_sub(growth_treasury_balance).saturating_sub(protocol_treasury_balance) + } +} + impl pallet_democracy::Config for Runtime { type BlacklistOrigin = EnsureRoot; // To cancel a proposal before it has been passed, the technical committee must be unanimous or @@ -495,6 +506,7 @@ impl pallet_democracy::Config for Runtime { // To cancel a proposal which has been passed, 2/3 of the council must agree to it. type CancellationOrigin = pallet_collective::EnsureProportionAtLeast; type CooloffPeriod = CooloffPeriod; + type Electorate = Electorate; type EnactmentPeriod = EnactmentPeriod; /// A unanimous council can have the next scheduled referendum be a straight default-carries /// (NTB) vote. diff --git a/runtimes/testnet/src/lib.rs b/runtimes/testnet/src/lib.rs index c87103a9d..a89731cc5 100644 --- a/runtimes/testnet/src/lib.rs +++ b/runtimes/testnet/src/lib.rs @@ -25,7 +25,7 @@ use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use frame_support::{ construct_runtime, ord_parameter_types, parameter_types, traits::{ - fungible::Credit, tokens, AsEnsureOriginWithArg, ConstU32, Currency, EitherOfDiverse, Everything, PrivilegeCmp, + fungible::{Credit, Inspect}, tokens, AsEnsureOriginWithArg, ConstU32, Currency, EitherOfDiverse, Everything, PrivilegeCmp, WithdrawReasons, }, weights::{ConstantMultiplier, Weight}, @@ -55,6 +55,7 @@ use sp_runtime::{ }; use pallet_oracle_ocw::types::AssetName; +use pallet_democracy::GetElectorate; use sp_std::{cmp::Ordering, prelude::*}; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -433,6 +434,16 @@ impl pallet_elections_phragmen::Config for Runtime { type WeightInfo = pallet_elections_phragmen::weights::SubstrateWeight; } +pub struct Electorate; +impl GetElectorate for Electorate { + fn get_electorate() -> Balance { + let total_issuance = Balances::total_issuance(); + let growth_treasury_balance = Balances::balance(&Treasury::account_id()); + let protocol_treasury_balance = Balances::balance(&PayMaster::get()); + total_issuance.saturating_sub(growth_treasury_balance).saturating_sub(protocol_treasury_balance) + } +} + impl pallet_democracy::Config for Runtime { type BlacklistOrigin = EnsureRoot; // To cancel a proposal before it has been passed, the technical committee must be unanimous or @@ -444,6 +455,7 @@ impl pallet_democracy::Config for Runtime { // To cancel a proposal which has been passed, 2/3 of the council must agree to it. type CancellationOrigin = pallet_collective::EnsureProportionAtLeast; type CooloffPeriod = CooloffPeriod; + type Electorate = Electorate; type EnactmentPeriod = EnactmentPeriod; /// A unanimous council can have the next scheduled referendum be a straight default-carries /// (NTB) vote.