diff --git a/Cargo.lock b/Cargo.lock index f8e2df769..5bbb8397f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4946,7 +4946,7 @@ dependencies = [ [[package]] name = "hydradx-adapters" -version = "1.3.6" +version = "1.3.7" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -4975,7 +4975,7 @@ dependencies = [ "pallet-referrals", "pallet-route-executor", "pallet-stableswap", - "pallet-staking 3.1.2", + "pallet-staking 4.0.0", "pallet-transaction-multi-payment", "pallet-uniques", "pallet-xyk", @@ -4996,7 +4996,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "261.0.0" +version = "262.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -5045,6 +5045,7 @@ dependencies = [ "pallet-collator-rewards", "pallet-collator-selection", "pallet-collective", + "pallet-conviction-voting 28.0.0", "pallet-currencies", "pallet-dca", "pallet-democracy 4.3.2", @@ -5076,13 +5077,14 @@ dependencies = [ "pallet-otc-settlements", "pallet-preimage", "pallet-proxy", + "pallet-referenda", "pallet-referrals", "pallet-relaychain-info", "pallet-route-executor", "pallet-scheduler", "pallet-session", "pallet-stableswap", - "pallet-staking 3.1.2", + "pallet-staking 4.0.0", "pallet-state-trie-migration", "pallet-timestamp", "pallet-tips", @@ -5093,6 +5095,7 @@ dependencies = [ "pallet-treasury", "pallet-uniques", "pallet-utility", + "pallet-whitelist", "pallet-xcm", "pallet-xyk", "pallet-xyk-liquidity-mining", @@ -5107,6 +5110,7 @@ dependencies = [ "serde", "smallvec", "sp-api", + "sp-arithmetic", "sp-block-builder", "sp-consensus-aura", "sp-core", @@ -7901,7 +7905,7 @@ dependencies = [ [[package]] name = "pallet-circuit-breaker" -version = "1.1.26" +version = "1.1.27" dependencies = [ "frame-benchmarking", "frame-support", @@ -8007,6 +8011,25 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-conviction-voting" +version = "28.0.0" +dependencies = [ + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-scheduler", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-conviction-voting" version = "28.0.0" @@ -8045,7 +8068,7 @@ dependencies = [ [[package]] name = "pallet-dca" -version = "1.6.1" +version = "1.6.2" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -8798,7 +8821,7 @@ dependencies = [ [[package]] name = "pallet-omnipool" -version = "4.3.4" +version = "4.4.0" dependencies = [ "bitflags 1.3.2", "frame-benchmarking", @@ -8825,7 +8848,7 @@ dependencies = [ [[package]] name = "pallet-omnipool-liquidity-mining" -version = "2.2.1" +version = "2.2.2" dependencies = [ "frame-benchmarking", "frame-support", @@ -8852,7 +8875,7 @@ dependencies = [ [[package]] name = "pallet-otc" -version = "2.0.2" +version = "2.0.3" dependencies = [ "frame-benchmarking", "frame-support", @@ -8874,7 +8897,7 @@ dependencies = [ [[package]] name = "pallet-otc-settlements" -version = "1.0.4" +version = "1.0.5" dependencies = [ "frame-benchmarking", "frame-support", @@ -9060,7 +9083,7 @@ dependencies = [ [[package]] name = "pallet-route-executor" -version = "2.6.1" +version = "2.6.2" dependencies = [ "frame-benchmarking", "frame-support", @@ -9160,7 +9183,7 @@ dependencies = [ [[package]] name = "pallet-stableswap" -version = "3.6.5" +version = "3.7.0" dependencies = [ "bitflags 1.3.2", "frame-benchmarking", @@ -9185,7 +9208,7 @@ dependencies = [ [[package]] name = "pallet-staking" -version = "3.1.2" +version = "4.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -9195,7 +9218,8 @@ dependencies = [ "orml-tokens", "orml-traits", "pallet-balances", - "pallet-democracy 4.3.2", + "pallet-conviction-voting 28.0.0", + "pallet-referenda", "pallet-uniques", "parity-scale-codec", "pretty_assertions", @@ -9362,7 +9386,7 @@ dependencies = [ [[package]] name = "pallet-transaction-pause" -version = "1.0.4" +version = "1.1.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -12112,7 +12136,7 @@ dependencies = [ "pallet-bounties", "pallet-child-bounties", "pallet-collective", - "pallet-conviction-voting", + "pallet-conviction-voting 28.0.0 (git+https://github.com/galacticcouncil/polkadot-sdk?branch=release-polkadot-v1.11.0-patch)", "pallet-democracy 28.0.0", "pallet-elections-phragmen", "pallet-grandpa", @@ -12259,7 +12283,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.23.7" +version = "1.23.8" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -12300,6 +12324,7 @@ dependencies = [ "pallet-circuit-breaker", "pallet-collator-selection", "pallet-collective", + "pallet-conviction-voting 28.0.0", "pallet-currencies", "pallet-dca", "pallet-democracy 4.3.2", @@ -12318,13 +12343,14 @@ dependencies = [ "pallet-omnipool-liquidity-mining", "pallet-otc", "pallet-otc-settlements", + "pallet-referenda", "pallet-referrals", "pallet-relaychain-info", "pallet-route-executor", "pallet-scheduler", "pallet-session", "pallet-stableswap", - "pallet-staking 3.1.2", + "pallet-staking 4.0.0", "pallet-timestamp", "pallet-tips", "pallet-transaction-multi-payment", @@ -12387,7 +12413,7 @@ dependencies = [ "pallet-asset-registry", "pallet-omnipool", "pallet-stableswap", - "pallet-staking 3.1.2", + "pallet-staking 4.0.0", "primitives", "scraper", "serde", @@ -17217,7 +17243,7 @@ dependencies = [ "pallet-beefy", "pallet-beefy-mmr", "pallet-collective", - "pallet-conviction-voting", + "pallet-conviction-voting 28.0.0 (git+https://github.com/galacticcouncil/polkadot-sdk?branch=release-polkadot-v1.11.0-patch)", "pallet-democracy 28.0.0", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", diff --git a/Cargo.toml b/Cargo.toml index f52e83457..dabe88cb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ members = [ 'pallets/evm-accounts', 'pallets/dynamic-evm-fee', 'pallets/xyk-liquidity-mining', + 'pallets/conviction-voting', 'precompiles/call-permit', 'runtime-mock' ] @@ -137,6 +138,7 @@ warehouse-liquidity-mining = { package = "pallet-liquidity-mining", path = "pall pallet-bonds = { path = "pallets/bonds", default-features = false} pallet-lbp = { path = "pallets/lbp", default-features = false} pallet-xyk = { path = "pallets/xyk", default-features = false} +pallet-conviction-voting = { path = "pallets/conviction-voting", default-features = false} pallet-xyk-liquidity-mining = { path = "pallets/xyk-liquidity-mining", default-features = false} pallet-referrals = { path = "pallets/referrals", default-features = false} pallet-evm-accounts = { path = "pallets/evm-accounts", default-features = false} @@ -234,6 +236,7 @@ pallet-identity = { git = "https://github.com/galacticcouncil/polkadot-sdk", bra pallet-multisig = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } pallet-preimage = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } pallet-proxy = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } +pallet-referenda = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } pallet-scheduler = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } pallet-session = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } pallet-sudo = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } @@ -248,6 +251,7 @@ pallet-utility = { git = "https://github.com/galacticcouncil/polkadot-sdk", bran pallet-im-online = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } pallet-message-queue = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } pallet-state-trie-migration = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } +pallet-whitelist = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } substrate-build-script-utils = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } substrate-frame-rpc-system = { git = "https://github.com/galacticcouncil/polkadot-sdk", branch = "release-polkadot-v1.11.0-patch", default-features = false } diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index a5f2455a2..6632a86e0 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.23.7" +version = "1.23.8" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" @@ -49,6 +49,8 @@ pallet-elections-phragmen = { workspace = true } pallet-tips = { workspace = true } pallet-xyk-liquidity-mining = { workspace = true } pallet-transaction-pause = { workspace = true } +pallet-referenda = { workspace = true } +pallet-conviction-voting = { workspace = true } # collator support pallet-collator-selection = { workspace = true } diff --git a/integration-tests/src/asset_registry.rs b/integration-tests/src/asset_registry.rs index 499d5cf03..d01d0a1fc 100644 --- a/integration-tests/src/asset_registry.rs +++ b/integration-tests/src/asset_registry.rs @@ -2,9 +2,9 @@ use crate::asset_registry::Junction::GeneralIndex; use crate::polkadot_test_net::*; -use frame_support::{assert_noop, assert_ok}; +use frame_support::assert_ok; use frame_system::RawOrigin; -use hydradx_runtime::{AssetRegistry as Registry, TechnicalCollective}; +use hydradx_runtime::AssetRegistry as Registry; use polkadot_xcm::v3::{ Junction::{self, Parachain}, Junctions::X2, @@ -38,88 +38,6 @@ fn root_should_update_decimals_when_it_was_already_set() { }); } -#[test] -fn tech_comm_should_not_update_decimals_when_it_was_aleady_set() { - TestNet::reset(); - Hydra::execute_with(|| { - let tech_comm = pallet_collective::RawOrigin::::Members(1, 1); - let new_decimals = 53_u8; - - assert_ne!(Registry::assets(HDX).unwrap().decimals.unwrap(), new_decimals); - - assert_noop!( - Registry::update( - tech_comm.into(), - HDX, - None, - None, - None, - None, - None, - None, - Some(new_decimals), - None - ), - pallet_asset_registry::Error::::Forbidden - ); - }); -} - -#[test] -fn tech_comm_should_update_decimals_when_it_wasnt_set_yet() { - TestNet::reset(); - Hydra::execute_with(|| { - let tech_comm = pallet_collective::RawOrigin::::Members(1, 1); - let new_decimals = 12_u8; - - assert!(Registry::assets(LRNA).unwrap().decimals.is_none()); - - assert_ok!(Registry::update( - tech_comm.into(), - LRNA, - None, - None, - None, - None, - None, - None, - Some(new_decimals), - None - )); - - assert_eq!(Registry::assets(LRNA).unwrap().decimals.unwrap(), new_decimals); - }); -} - -#[test] -fn tech_comm_should_not_update_location_when_asset_exists() { - TestNet::reset(); - Hydra::execute_with(|| { - let tech_comm = pallet_collective::RawOrigin::::Members(1, 1); - - assert!(Registry::locations(LRNA).is_none()); - - assert_noop!( - Registry::update( - tech_comm.into(), - LRNA, - None, - None, - None, - None, - None, - None, - None, - Some(hydradx_runtime::AssetLocation(MultiLocation::new( - 1, - X2(Parachain(MOONBEAM_PARA_ID), GeneralIndex(0)) - ))), - ), - pallet_asset_registry::Error::::Forbidden - ); - }); -} - #[test] fn root_should_update_location_when_asset_exists() { TestNet::reset(); diff --git a/integration-tests/src/call_filter.rs b/integration-tests/src/call_filter.rs index b3d8b27d6..ef885af72 100644 --- a/integration-tests/src/call_filter.rs +++ b/integration-tests/src/call_filter.rs @@ -4,10 +4,13 @@ use crate::polkadot_test_net::*; use frame_support::{ assert_ok, sp_runtime::{FixedU128, Permill}, - traits::Contains, + traits::{Contains, StorePreimage}, + weights::Weight, }; +use hydradx_runtime::{origins, Preimage}; use polkadot_xcm::v3::prelude::*; use polkadot_xcm::VersionedXcm; +use primitives::constants::currency::UNITS; use xcm_emulator::TestExt; #[test] @@ -289,3 +292,34 @@ fn create_contract_from_evm_pallet_should_be_filtered_by_call_filter() { assert!(!hydradx_runtime::CallFilter::contains(&call)); }); } + +#[test] +fn referenda_can_not_be_filtered() { + TestNet::reset(); + + Hydra::execute_with(|| { + // Arrange + // Try to pause transactions for Referenda/submit + assert_ok!(hydradx_runtime::TransactionPause::pause_transaction( + hydradx_runtime::RuntimeOrigin::root(), + b"Referenda".to_vec(), + b"submit".to_vec() + )); + + // Prepare a Referenda/submit call + let spend_call = hydradx_runtime::RuntimeCall::Treasury(pallet_treasury::Call::spend_local { + amount: 100 * UNITS, + beneficiary: ALICE.into(), + }); + let preimage = ::bound(spend_call).unwrap(); + + // Act & Assert + let successful_call = hydradx_runtime::RuntimeCall::Referenda(pallet_referenda::Call::submit { + proposal_origin: Box::new(hydradx_runtime::OriginCaller::Origins(origins::Origin::Tipper)), + proposal: preimage, + enactment_moment: frame_support::traits::schedule::DispatchTime::After(100), + }); + + assert!(hydradx_runtime::CallFilter::contains(&successful_call)); + }); +} diff --git a/integration-tests/src/insufficient_assets_ed.rs b/integration-tests/src/insufficient_assets_ed.rs index 36c0d4aed..61ac16a76 100644 --- a/integration-tests/src/insufficient_assets_ed.rs +++ b/integration-tests/src/insufficient_assets_ed.rs @@ -12,8 +12,8 @@ use hydradx_runtime::Omnipool; use hydradx_runtime::RuntimeOrigin as hydra_origin; use hydradx_runtime::DOT_ASSET_LOCATION; use hydradx_runtime::{ - AssetRegistry as Registry, Currencies, DustRemovalWhitelist, InsufficientEDinHDX, MultiTransactionPayment, - NativeExistentialDeposit, RuntimeEvent, TechnicalCollective, Tokens, TreasuryAccount, SUFFICIENCY_LOCK, + origins::Origin, AssetRegistry as Registry, Currencies, DustRemovalWhitelist, InsufficientEDinHDX, + MultiTransactionPayment, NativeExistentialDeposit, RuntimeEvent, Tokens, TreasuryAccount, SUFFICIENCY_LOCK, }; use hydradx_traits::AssetKind; use hydradx_traits::Create; @@ -1352,7 +1352,7 @@ fn tx_should_fail_with_unsupported_currency_error_when_fee_asset_price_was_not_p fn banned_asset_should_not_create_new_account() { TestNet::reset(); Hydra::execute_with(|| { - let tech_comm = pallet_collective::RawOrigin::::Members(1, 1); + let update_origin = hydradx_runtime::OriginCaller::Origins(Origin::GeneralAdmin); //Arrange let sht1: AssetId = register_external_asset(0_u128); assert_ok!(Tokens::set_balance( @@ -1363,7 +1363,7 @@ fn banned_asset_should_not_create_new_account() { 0, )); - assert_ok!(Registry::ban_asset(tech_comm.into(), sht1)); + assert_ok!(Registry::ban_asset(update_origin.into(), sht1)); assert_eq!(Currencies::free_balance(sht1, &ALICE.into()), 0); assert_eq!(treasury_sufficiency_lock(), 0); @@ -1380,7 +1380,7 @@ fn banned_asset_should_not_create_new_account() { fn banned_asset_should_not_be_transferable_to_existing_account() { TestNet::reset(); Hydra::execute_with(|| { - let tech_comm = pallet_collective::RawOrigin::::Members(1, 1); + let update_origin = hydradx_runtime::OriginCaller::Origins(Origin::GeneralAdmin); //Arrange let sht1: AssetId = register_external_asset(0_u128); assert_ok!(Tokens::set_balance( @@ -1399,7 +1399,7 @@ fn banned_asset_should_not_be_transferable_to_existing_account() { 0, )); - assert_ok!(Registry::ban_asset(tech_comm.into(), sht1)); + assert_ok!(Registry::ban_asset(update_origin.into(), sht1)); //Act & assert assert_noop!( diff --git a/integration-tests/src/staking.rs b/integration-tests/src/staking.rs index 7c4870855..55c661b81 100644 --- a/integration-tests/src/staking.rs +++ b/integration-tests/src/staking.rs @@ -1,22 +1,22 @@ #![cfg(test)] use crate::polkadot_test_net::*; -use frame_support::traits::LockIdentifier; use frame_support::{ assert_noop, assert_ok, dispatch::DispatchResult, - traits::{Bounded, OnInitialize, StorePreimage}, + traits::{schedule::DispatchTime, Bounded, LockIdentifier, OnInitialize, StorePreimage}, }; use frame_system::RawOrigin; use hydradx_runtime::{ - Balances, BlockNumber, Currencies, Democracy, Omnipool, Preimage, Scheduler, Staking, System, Tokens, Vesting, + Balances, BlockNumber, ConvictionVoting, Currencies, Democracy, Omnipool, Preimage, Referenda, Scheduler, Staking, + System, Tokens, Vesting, }; use orml_traits::currency::MultiCurrency; use orml_vesting::VestingSchedule; -use pallet_democracy::{AccountVote, Conviction, ReferendumIndex, ReferendumInfo, Vote}; +use pallet_conviction_voting::{AccountVote, Conviction, Vote}; +use pallet_referenda::ReferendumIndex; use pretty_assertions::assert_eq; -use primitives::constants::time::DAYS; -use primitives::AccountId; +use primitives::{constants::time::DAYS, AccountId}; use sp_runtime::AccountId32; use xcm_emulator::TestExt; @@ -24,6 +24,11 @@ type CallOf = ::RuntimeCall; type BoundedCallOf = Bounded, ::Hashing>; type Schedule = VestingSchedule; +const ROOT_TRACK: >::Id = 0; + fn vesting_schedule() -> Schedule { Schedule { start: 0, @@ -39,28 +44,42 @@ fn set_balance_proposal(who: AccountId, value: u128) -> BoundedCallOf DispatchResult { - Democracy::propose( +fn propose_set_balance(who: AccountId, dest: AccountId, value: u128, dispatch_time: BlockNumber) -> DispatchResult { + Referenda::submit( hydradx_runtime::RuntimeOrigin::signed(who), + Box::new(RawOrigin::Root.into()), set_balance_proposal(dest, value), - 100_000 * UNITS, + DispatchTime::At(dispatch_time), ) } fn begin_referendum() -> ReferendumIndex { - assert_ok!(propose_set_balance(ALICE.into(), CHARLIE.into(), 2)); - fast_forward_to(3 * DAYS); - 0 -} + let referendum_index = pallet_referenda::pallet::ReferendumCount::::get(); + let now = System::block_number(); -fn end_referendum() { - fast_forward_to(7 * DAYS); + assert_ok!(propose_set_balance(ALICE.into(), CHARLIE.into(), 2, now + 10 * DAYS)); + + assert_ok!(Balances::force_set_balance( + RawOrigin::Root.into(), + DAVE.into(), // not used in the tests + 2_000_000_000 * UNITS, + )); + + assert_ok!(Referenda::place_decision_deposit( + hydradx_runtime::RuntimeOrigin::signed(DAVE.into()), + referendum_index + )); + + assert_eq!(pallet_referenda::DecidingCount::::get(0), 0); + fast_forward_to(now + 8 * DAYS); + assert_eq!(pallet_referenda::DecidingCount::::get(0), 1); + + referendum_index } -fn fast_forward_by(n: u32) { - for _ in 1..n { - next_block(); - } +fn end_referendum() { + let now = System::block_number(); + fast_forward_to(now + 12 * DAYS); } fn fast_forward_to(n: u32) { @@ -142,13 +161,15 @@ fn democracy_vote_should_record_stake_vote() { ALICE.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(2 * UNITS) @@ -163,12 +184,11 @@ fn democracy_vote_should_record_stake_vote() { assert!(!stake_voting.votes.is_empty()); let (ref_vote_idx, vote) = stake_voting.votes[0]; - assert_eq!(ref_vote_idx, 0); + assert_eq!(ref_vote_idx, r); assert_eq!( vote, pallet_staking::types::Vote::new(2 * UNITS, pallet_staking::types::Conviction::None) ); - end_referendum(); }); } @@ -198,11 +218,12 @@ fn staking_action_should_claim_points_for_finished_referendums_when_voted() { let r = begin_referendum(); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(1_000 * UNITS) )); + end_referendum(); let alice_position_id = pallet_staking::Pallet::::get_user_position_id( @@ -251,11 +272,12 @@ fn staking_should_transfer_rewards_when_claimed() { let r = begin_referendum(); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(1_000 * UNITS) )); + end_referendum(); let alice_position_id = pallet_staking::Pallet::::get_user_position_id( @@ -271,8 +293,9 @@ fn staking_should_transfer_rewards_when_claimed() { 1_000 * UNITS )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); @@ -320,15 +343,17 @@ fn staking_should_not_reward_when_double_claimed() { let r = begin_referendum(); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(2 * UNITS) )); + end_referendum(); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); @@ -383,11 +408,12 @@ fn staking_should_not_reward_when_increase_stake_again_and_no_vote_activity() { let r = begin_referendum(); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(2 * UNITS) )); + end_referendum(); let alice_position_id = pallet_staking::Pallet::::get_user_position_id( @@ -401,8 +427,9 @@ fn staking_should_not_reward_when_increase_stake_again_and_no_vote_activity() { 1_000 * UNITS )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); @@ -453,30 +480,35 @@ fn increase_should_slash_min_amount_when_increase_is_low() { let r = begin_referendum(); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye6x(100_000 * UNITS) )); + end_referendum(); - assert_ok!(propose_set_balance(ALICE.into(), CHARLIE.into(), 2)); - fast_forward_to(10 * DAYS); + assert_ok!(propose_set_balance(ALICE.into(), CHARLIE.into(), 2, 22 * DAYS)); + + fast_forward_to(20 * DAYS); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1, aye6x(100_000 * UNITS) )); - fast_forward_to(17 * DAYS); - assert_ok!(Democracy::remove_vote( + fast_forward_to(30 * DAYS); + + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), 1 )); @@ -524,11 +556,12 @@ fn staking_should_claim_and_unreserve_rewards_when_unstaked() { let r = begin_referendum(); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(2 * UNITS) )); + end_referendum(); let alice_position_id = pallet_staking::Pallet::::get_user_position_id( @@ -543,8 +576,9 @@ fn staking_should_claim_and_unreserve_rewards_when_unstaked() { 1_000 * UNITS )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); @@ -554,7 +588,7 @@ fn staking_should_claim_and_unreserve_rewards_when_unstaked() { )); let alice_balance_after_claim = Currencies::free_balance(HDX, &AccountId32::from(ALICE)); assert!(alice_balance_after_claim > alice_balance); - assert_eq!(alice_balance_after_claim, 1000000001920799631); + assert_eq!(alice_balance_after_claim, 999900127998361620); let stake_position_id = pallet_staking::Pallet::::get_user_position_id( &sp_runtime::AccountId32::from(ALICE), @@ -584,13 +618,15 @@ fn staking_should_remove_vote_when_democracy_removes_vote() { ALICE.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(2 * UNITS) @@ -604,18 +640,20 @@ fn staking_should_remove_vote_when_democracy_removes_vote() { let stake_voting = pallet_staking::Pallet::::get_position_votes(stake_position_id); assert!(!stake_voting.votes.is_empty()); let (ref_vote_idx, vote) = stake_voting.votes[0]; - assert_eq!(ref_vote_idx, 0); + assert_eq!(ref_vote_idx, r); assert_eq!( vote, pallet_staking::types::Vote::new(2 * UNITS, pallet_staking::types::Conviction::None) ); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); let stake_voting = pallet_staking::Pallet::::get_position_votes(stake_position_id); assert!(stake_voting.votes.is_empty()); + end_referendum(); }); } @@ -639,13 +677,15 @@ fn staking_should_not_reward_when_refenrendum_is_ongoing() { ALICE.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(2 * UNITS) @@ -665,7 +705,6 @@ fn staking_should_not_reward_when_refenrendum_is_ongoing() { )); let alice_balance_after_claim = Currencies::free_balance(HDX, &AccountId32::from(ALICE)); assert_eq!(alice_balance, alice_balance_after_claim); - end_referendum(); }); } @@ -688,12 +727,15 @@ fn democracy_vote_should_work_correctly_when_account_has_no_stake() { ALICE.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); - assert_ok!(Democracy::vote( + + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(2 * UNITS) )); + end_referendum(); }); } @@ -719,16 +761,20 @@ fn democracy_remote_vote_should_work_correctly_when_account_has_no_stake() { ALICE.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); - assert_ok!(Democracy::vote( + + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, aye(2 * UNITS) )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); + end_referendum(); }); } @@ -1004,7 +1050,7 @@ fn staking_should_assign_less_action_points_when_portion_of_staking_lock_is_vest let r = begin_referendum(); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1015,6 +1061,7 @@ fn staking_should_assign_less_action_points_when_portion_of_staking_lock_is_vest balance: 150_000 * UNITS, } )); + end_referendum(); let stake_position_id = pallet_staking::Pallet::::get_user_position_id( @@ -1060,14 +1107,16 @@ fn staking_should_allow_to_remove_vote_and_lock_when_referendum_is_finished_and_ assert_ok!(Balances::force_set_balance( RawOrigin::Root.into(), ALICE.into(), - 1_000_000 * UNITS, + 2_000_000 * UNITS, )); assert_ok!(Balances::force_set_balance( RawOrigin::Root.into(), BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -1077,7 +1126,7 @@ fn staking_should_allow_to_remove_vote_and_lock_when_referendum_is_finished_and_ 1_000_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1085,11 +1134,11 @@ fn staking_should_allow_to_remove_vote_and_lock_when_referendum_is_finished_and_ aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1100,6 +1149,7 @@ fn staking_should_allow_to_remove_vote_and_lock_when_referendum_is_finished_and_ balance: 1_000_000 * UNITS, } )); + end_referendum(); let stake_position_id = pallet_staking::Pallet::::get_user_position_id( @@ -1107,19 +1157,22 @@ fn staking_should_allow_to_remove_vote_and_lock_when_referendum_is_finished_and_ ) .unwrap() .unwrap(); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), + Some(ROOT_TRACK), r ),); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), + ROOT_TRACK, BOB.into() ),); let stake_voting = pallet_staking::Pallet::::get_position_votes(stake_position_id); assert!(stake_voting.votes.is_empty()); let position = pallet_staking::Pallet::::get_position(stake_position_id).unwrap(); assert_eq!(position.get_action_points(), 100); - assert_lock(&BOB.into(), 1_000_000 * UNITS, DEMOCRACY_ID); + + assert_lock(&BOB.into(), 1_000_000 * UNITS, CONVICTION_VOTING_ID); }); } @@ -1140,14 +1193,16 @@ fn staking_should_allow_to_remove_vote_when_user_lost_and_conviction_expires() { assert_ok!(Balances::force_set_balance( RawOrigin::Root.into(), ALICE.into(), - 1_000_000 * UNITS, + 2_000_000 * UNITS, )); assert_ok!(Balances::force_set_balance( RawOrigin::Root.into(), BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 3_000 * UNITS @@ -1157,7 +1212,7 @@ fn staking_should_allow_to_remove_vote_when_user_lost_and_conviction_expires() { 1_000_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1169,7 +1224,7 @@ fn staking_should_allow_to_remove_vote_when_user_lost_and_conviction_expires() { } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1180,6 +1235,7 @@ fn staking_should_allow_to_remove_vote_when_user_lost_and_conviction_expires() { balance: 222 * UNITS, } )); + end_referendum(); fast_forward_to(18 * DAYS); @@ -1189,19 +1245,22 @@ fn staking_should_allow_to_remove_vote_when_user_lost_and_conviction_expires() { ) .unwrap() .unwrap(); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), + Some(ROOT_TRACK), r ),); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), + ROOT_TRACK, BOB.into() ),); let stake_voting = pallet_staking::Pallet::::get_position_votes(stake_position_id); assert!(stake_voting.votes.is_empty()); let position = pallet_staking::Pallet::::get_position(stake_position_id).unwrap(); assert_eq!(position.get_action_points(), 1); - assert_no_lock(&BOB.into(), DEMOCRACY_ID); + + assert_no_lock(&BOB.into(), CONVICTION_VOTING_ID); }); } @@ -1222,14 +1281,16 @@ fn staking_should_allow_to_remove_vote_when_user_won() { assert_ok!(Balances::force_set_balance( RawOrigin::Root.into(), ALICE.into(), - 1_000_000 * UNITS, + 2_000_000 * UNITS, )); assert_ok!(Balances::force_set_balance( RawOrigin::Root.into(), BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 3_000 * UNITS @@ -1239,7 +1300,7 @@ fn staking_should_allow_to_remove_vote_when_user_won() { 1_000_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1251,7 +1312,7 @@ fn staking_should_allow_to_remove_vote_when_user_won() { } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1262,6 +1323,7 @@ fn staking_should_allow_to_remove_vote_when_user_won() { balance: 222 * UNITS, } )); + end_referendum(); let stake_position_id = pallet_staking::Pallet::::get_user_position_id( @@ -1269,19 +1331,22 @@ fn staking_should_allow_to_remove_vote_when_user_won() { ) .unwrap() .unwrap(); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r ),); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + ROOT_TRACK, ALICE.into() ),); let stake_voting = pallet_staking::Pallet::::get_position_votes(stake_position_id); assert!(stake_voting.votes.is_empty()); let position = pallet_staking::Pallet::::get_position(stake_position_id).unwrap(); assert_eq!(position.get_action_points(), 100); - assert_lock(&ALICE.into(), 1_000_000 * UNITS, DEMOCRACY_ID); + + assert_lock(&ALICE.into(), 1_000_000 * UNITS, CONVICTION_VOTING_ID); }); } @@ -1302,14 +1367,16 @@ fn staking_should_allow_to_remove_vote_when_user_lost_with_no_conviction() { assert_ok!(Balances::force_set_balance( RawOrigin::Root.into(), ALICE.into(), - 1_000_000 * UNITS, + 2_000_000 * UNITS, )); assert_ok!(Balances::force_set_balance( RawOrigin::Root.into(), BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 3_000 * UNITS @@ -1319,7 +1386,7 @@ fn staking_should_allow_to_remove_vote_when_user_lost_with_no_conviction() { 1_000_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1331,7 +1398,7 @@ fn staking_should_allow_to_remove_vote_when_user_lost_with_no_conviction() { } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1342,6 +1409,7 @@ fn staking_should_allow_to_remove_vote_when_user_lost_with_no_conviction() { balance: 3_000 * UNITS, } )); + end_referendum(); let stake_position_id = pallet_staking::Pallet::::get_user_position_id( @@ -1349,19 +1417,22 @@ fn staking_should_allow_to_remove_vote_when_user_lost_with_no_conviction() { ) .unwrap() .unwrap(); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), + Some(ROOT_TRACK), r ),); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), + ROOT_TRACK, BOB.into() ),); let stake_voting = pallet_staking::Pallet::::get_position_votes(stake_position_id); assert!(stake_voting.votes.is_empty()); let position = pallet_staking::Pallet::::get_position(stake_position_id).unwrap(); assert_eq!(position.get_action_points(), 1); - assert_no_lock(&BOB.into(), DEMOCRACY_ID); + + assert_no_lock(&BOB.into(), CONVICTION_VOTING_ID); }); } @@ -1389,13 +1460,15 @@ fn remove_vote_should_not_lock_when_no_stake_and_lost() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1403,11 +1476,11 @@ fn remove_vote_should_not_lock_when_no_stake_and_lost() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1418,19 +1491,22 @@ fn remove_vote_should_not_lock_when_no_stake_and_lost() { balance: 1_000_000 * UNITS, } )); - end_referendum(); - fast_forward_to(DAYS); + end_referendum(); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); - assert_ok!(Democracy::unlock( + + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + ROOT_TRACK, ALICE.into() )); - assert_no_lock(&ALICE.into(), DEMOCRACY_ID); + + assert_no_lock(&ALICE.into(), CONVICTION_VOTING_ID); }); } @@ -1458,17 +1534,19 @@ fn remove_vote_should_extend_lock_when_vote_not_in_favor() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS )); assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), - 1_000_000 * UNITS + 500_000 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1476,11 +1554,11 @@ fn remove_vote_should_extend_lock_when_vote_not_in_favor() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1491,19 +1569,21 @@ fn remove_vote_should_extend_lock_when_vote_not_in_favor() { balance: 1_000_000 * UNITS, } )); - end_referendum(); - fast_forward_to(DAYS); + end_referendum(); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + ROOT_TRACK, ALICE.into() )); - assert_lock(&ALICE.into(), 1_000_000 * UNITS, DEMOCRACY_ID); + + assert_lock(&ALICE.into(), 500_000 * UNITS, CONVICTION_VOTING_ID); }); } @@ -1531,7 +1611,9 @@ fn remove_vote_should_extend_lock_for_partial_amount_when_vote_not_in_favor() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -1541,7 +1623,7 @@ fn remove_vote_should_extend_lock_for_partial_amount_when_vote_not_in_favor() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1549,11 +1631,11 @@ fn remove_vote_should_extend_lock_for_partial_amount_when_vote_not_in_favor() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1564,19 +1646,21 @@ fn remove_vote_should_extend_lock_for_partial_amount_when_vote_not_in_favor() { balance: 1_000_000 * UNITS, } )); - end_referendum(); - fast_forward_to(DAYS); + end_referendum(); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + ROOT_TRACK, ALICE.into() )); - assert_lock(&ALICE.into(), 222_222 * UNITS, DEMOCRACY_ID); + + assert_lock(&ALICE.into(), 222_222 * UNITS, CONVICTION_VOTING_ID); }); } @@ -1604,7 +1688,9 @@ fn unstake_should_fail_when_position_has_existing_votes() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -1614,7 +1700,7 @@ fn unstake_should_fail_when_position_has_existing_votes() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1626,7 +1712,7 @@ fn unstake_should_fail_when_position_has_existing_votes() { } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1637,8 +1723,9 @@ fn unstake_should_fail_when_position_has_existing_votes() { balance: 1_000_000 * UNITS, } )); + end_referendum(); - fast_forward_to(DAYS); + assert_noop!( Staking::unstake(hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1), pallet_staking::Error::::ExistingVotes @@ -1670,7 +1757,9 @@ fn unstake_should_fail_when_position_has_existing_processed_votes() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -1680,7 +1769,7 @@ fn unstake_should_fail_when_position_has_existing_processed_votes() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1692,7 +1781,7 @@ fn unstake_should_fail_when_position_has_existing_processed_votes() { } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1703,8 +1792,9 @@ fn unstake_should_fail_when_position_has_existing_processed_votes() { balance: 1_000_000 * UNITS, } )); + end_referendum(); - fast_forward_to(DAYS); + assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1, @@ -1742,7 +1832,9 @@ fn unstake_should_work_when_processed_votes_are_removed() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -1752,7 +1844,7 @@ fn unstake_should_work_when_processed_votes_are_removed() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1760,11 +1852,11 @@ fn unstake_should_work_when_processed_votes_are_removed() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1775,8 +1867,9 @@ fn unstake_should_work_when_processed_votes_are_removed() { balance: 1_000_000 * UNITS, } )); + end_referendum(); - fast_forward_to(DAYS); + // Votes are processed assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), @@ -1785,8 +1878,9 @@ fn unstake_should_work_when_processed_votes_are_removed() { )); // remove vote to allow unstake - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); @@ -1794,12 +1888,14 @@ fn unstake_should_work_when_processed_votes_are_removed() { hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1 )); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + ROOT_TRACK, ALICE.into() )); + // the amount should be locked - assert_lock(&ALICE.into(), 222_222 * UNITS, DEMOCRACY_ID); + assert_lock(&ALICE.into(), 222_222 * UNITS, CONVICTION_VOTING_ID); }); } @@ -1827,7 +1923,9 @@ fn remove_vote_should_not_lock_nor_assign_rewards_when_referendum_was_cancelled( BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -1837,7 +1935,7 @@ fn remove_vote_should_not_lock_nor_assign_rewards_when_referendum_was_cancelled( 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1845,11 +1943,11 @@ fn remove_vote_should_not_lock_nor_assign_rewards_when_referendum_was_cancelled( aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1860,33 +1958,39 @@ fn remove_vote_should_not_lock_nor_assign_rewards_when_referendum_was_cancelled( balance: 1_000_000 * UNITS, } )); - assert_ok!(Democracy::cancel_referendum(RawOrigin::Root.into(), r,)); - fast_forward_to(DAYS); + + assert_ok!(Referenda::cancel(RawOrigin::Root.into(), r,)); + assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1, 222_222 * UNITS )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), + Some(ROOT_TRACK), r )); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + ROOT_TRACK, ALICE.into() )); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), + ROOT_TRACK, BOB.into() )); - assert_no_lock(&BOB.into(), DEMOCRACY_ID); - assert_no_lock(&ALICE.into(), DEMOCRACY_ID); + + assert_no_lock(&BOB.into(), CONVICTION_VOTING_ID); + assert_no_lock(&ALICE.into(), CONVICTION_VOTING_ID); let stake_voting = pallet_staking::Pallet::::get_position_votes(1); assert!(stake_voting.votes.is_empty()); @@ -1932,7 +2036,9 @@ fn remove_vote_should_extend_lock_when_votes_are_already_processed() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -1942,7 +2048,7 @@ fn remove_vote_should_extend_lock_when_votes_are_already_processed() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -1950,11 +2056,11 @@ fn remove_vote_should_extend_lock_when_votes_are_already_processed() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -1965,8 +2071,9 @@ fn remove_vote_should_extend_lock_when_votes_are_already_processed() { balance: 1_000_000 * UNITS, } )); + end_referendum(); - fast_forward_to(DAYS); + // Votes are processed assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), @@ -1974,17 +2081,20 @@ fn remove_vote_should_extend_lock_when_votes_are_already_processed() { 222_222 * UNITS )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + ROOT_TRACK, ALICE.into() )); + // the amount should be locked - assert_lock(&ALICE.into(), 222_222 * UNITS, DEMOCRACY_ID); + assert_lock(&ALICE.into(), 222_222 * UNITS, CONVICTION_VOTING_ID); let stake_voting = pallet_staking::Pallet::::get_position_votes(1); assert!(stake_voting.votes.is_empty()); @@ -2021,7 +2131,9 @@ fn increase_stake_should_fail_when_position_has_existing_processed_votes() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -2031,7 +2143,7 @@ fn increase_stake_should_fail_when_position_has_existing_processed_votes() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -2039,11 +2151,11 @@ fn increase_stake_should_fail_when_position_has_existing_processed_votes() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -2054,8 +2166,9 @@ fn increase_stake_should_fail_when_position_has_existing_processed_votes() { balance: 1_000_000 * UNITS, } )); + end_referendum(); - fast_forward_to(DAYS); + // Votes are processed assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), @@ -2093,7 +2206,9 @@ fn claim_should_fail_when_position_has_existing_processed_votes() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -2103,7 +2218,7 @@ fn claim_should_fail_when_position_has_existing_processed_votes() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -2111,11 +2226,11 @@ fn claim_should_fail_when_position_has_existing_processed_votes() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -2126,8 +2241,9 @@ fn claim_should_fail_when_position_has_existing_processed_votes() { balance: 1_000_000 * UNITS, } )); + end_referendum(); - fast_forward_to(DAYS); + // Votes are processed assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), @@ -2165,7 +2281,9 @@ fn claim_should_work_when_processed_votes_are_removed() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -2175,7 +2293,7 @@ fn claim_should_work_when_processed_votes_are_removed() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -2183,11 +2301,11 @@ fn claim_should_work_when_processed_votes_are_removed() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -2198,31 +2316,24 @@ fn claim_should_work_when_processed_votes_are_removed() { balance: 1_000_000 * UNITS, } )); - end_referendum(); - let ref_info = pallet_democracy::Pallet::::referendum_info(r).unwrap(); - assert_eq!( - ref_info, - ReferendumInfo::Finished { - approved: false, - end: 43200 - } - ); + end_referendum(); - fast_forward_to(DAYS); // Votes are processed assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1, 222_222 * UNITS )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); assert_ok!(Staking::claim(hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1,)); - assert_ok!(Democracy::unlock( + assert_ok!(ConvictionVoting::unlock( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + ROOT_TRACK, ALICE.into() )); let stake_voting = pallet_staking::Pallet::::get_position_votes(1); @@ -2233,7 +2344,8 @@ fn claim_should_work_when_processed_votes_are_removed() { ); let stake_position = pallet_staking::Pallet::::get_position(1).unwrap(); assert_eq!(stake_position.get_action_points(), 100); - assert_lock(&ALICE.into(), 222_222 * UNITS, DEMOCRACY_ID); + + assert_lock(&ALICE.into(), 222_222 * UNITS, CONVICTION_VOTING_ID); }); } @@ -2261,7 +2373,9 @@ fn increase_stake_should_work_when_processed_votes_are_removed() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -2271,7 +2385,7 @@ fn increase_stake_should_work_when_processed_votes_are_removed() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -2279,11 +2393,11 @@ fn increase_stake_should_work_when_processed_votes_are_removed() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -2294,16 +2408,18 @@ fn increase_stake_should_work_when_processed_votes_are_removed() { balance: 1_000_000 * UNITS, } )); + end_referendum(); - fast_forward_to(DAYS); + // Votes are processed assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), 1, 222_222 * UNITS )); - assert_ok!(Democracy::remove_vote( + assert_ok!(ConvictionVoting::remove_vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Some(ROOT_TRACK), r )); assert_ok!(Staking::increase_stake( @@ -2338,7 +2454,9 @@ fn increase_stake_should_work_when_referendum_ongoing_and_votes_processed() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -2348,7 +2466,7 @@ fn increase_stake_should_work_when_referendum_ongoing_and_votes_processed() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -2356,11 +2474,11 @@ fn increase_stake_should_work_when_referendum_ongoing_and_votes_processed() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -2371,7 +2489,6 @@ fn increase_stake_should_work_when_referendum_ongoing_and_votes_processed() { balance: 1_000_000 * UNITS, } )); - fast_forward_to(DAYS); // Votes are processed assert_ok!(Staking::increase_stake( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), @@ -2414,7 +2531,9 @@ fn voting_on_next_referenda_should_process_votes() { BOB.into(), 1_000_000 * UNITS, )); + let r = begin_referendum(); + assert_ok!(Staking::stake( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), 1_000_000 * UNITS @@ -2424,7 +2543,7 @@ fn voting_on_next_referenda_should_process_votes() { 222_222 * UNITS )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -2432,11 +2551,11 @@ fn voting_on_next_referenda_should_process_votes() { aye: true, conviction: Conviction::Locked6x, }, - balance: 1_000_000 * UNITS, + balance: 500_000 * UNITS, } )); - assert_ok!(Democracy::vote( + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(BOB.into()), r, AccountVote::Standard { @@ -2447,15 +2566,16 @@ fn voting_on_next_referenda_should_process_votes() { balance: 1_000_000 * UNITS, } )); + end_referendum(); + assert!( pallet_staking::Pallet::::processed_votes::(ALICE.into(), r) .is_none() ); - let r = begin_referendum() + 1; - fast_forward_by(3 * DAYS); - assert_ok!(Democracy::vote( + let r = begin_referendum(); + assert_ok!(ConvictionVoting::vote( hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), r, AccountVote::Standard { @@ -2473,7 +2593,7 @@ fn voting_on_next_referenda_should_process_votes() { }); } -const DEMOCRACY_ID: LockIdentifier = *b"democrac"; +const CONVICTION_VOTING_ID: LockIdentifier = *b"pyconvot"; fn assert_lock(who: &AccountId, amount: Balance, lock_id: LockIdentifier) { let locks = Balances::locks(who); let lock = locks.iter().find(|e| e.id == lock_id); diff --git a/launch-configs/chopsticks/hydradx.yml b/launch-configs/chopsticks/hydradx.yml index 8d865c296..024d74b4c 100644 --- a/launch-configs/chopsticks/hydradx.yml +++ b/launch-configs/chopsticks/hydradx.yml @@ -2,7 +2,7 @@ endpoint: wss://rpc.hydradx.cloud mock-signature-host: true #block: 1872160 db: ./db.sqlite -wasm-override: target/release/hydradx_runtime.compact.compressed.wasm +wasm-override: target/release/wbuild/hydradx-runtime/hydradx_runtime.compact.compressed.wasm import-storage: System: diff --git a/pallets/circuit-breaker/Cargo.toml b/pallets/circuit-breaker/Cargo.toml index 70e1f216b..059c27660 100644 --- a/pallets/circuit-breaker/Cargo.toml +++ b/pallets/circuit-breaker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-circuit-breaker" -version = "1.1.26" +version = "1.1.27" authors = ["GalacticCouncil "] edition = "2021" license = "Apache-2.0" diff --git a/pallets/circuit-breaker/README.md b/pallets/circuit-breaker/README.md index 8b22044ec..75e711ad2 100644 --- a/pallets/circuit-breaker/README.md +++ b/pallets/circuit-breaker/README.md @@ -7,5 +7,5 @@ Three different limits are tracked independently for all assets: trading limit, All trading volumes and amounts of liquidity are reset to zero at the end of block execution, so no values are actually stored in the database. The default percentage limits are set for all assets in the pallet config. -To set a specific trade limit for a given asset, the `set_trade_volume_limit` extrinsic can be executed by `TechnicalOrigin`. -To set a specific limit for liquidity that can be added for a given asset, the `set_liquidity_limit` extrinsic can be executed by `TechnicalOrigin`. +To set a specific trade limit for a given asset, the `set_trade_volume_limit` extrinsic can be executed by `UpdateLimitsOrigin`. +To set a specific limit for liquidity that can be added for a given asset, the `set_liquidity_limit` extrinsic can be executed by `UpdateLimitsOrigin`. diff --git a/pallets/circuit-breaker/src/lib.rs b/pallets/circuit-breaker/src/lib.rs index 613a2f648..61160f688 100644 --- a/pallets/circuit-breaker/src/lib.rs +++ b/pallets/circuit-breaker/src/lib.rs @@ -195,7 +195,7 @@ pub mod pallet { + From; /// Origin able to change the trade volume limit of an asset. - type TechnicalOrigin: EnsureOrigin; + type UpdateLimitsOrigin: EnsureOrigin; /// List of accounts that bypass checks for adding/removing liquidity. Root is always whitelisted type WhitelistedAccounts: Contains; @@ -326,7 +326,7 @@ pub mod pallet { /// Set trade volume limit for an asset. /// /// Parameters: - /// - `origin`: The dispatch origin for this call. Must be `TechnicalOrigin` + /// - `origin`: The dispatch origin for this call. Must be `UpdateLimitsOrigin` /// - `asset_id`: The identifier of an asset /// - `trade_volume_limit`: New trade volume limit represented as a percentage /// @@ -339,7 +339,7 @@ pub mod pallet { asset_id: T::AssetId, trade_volume_limit: (u32, u32), ) -> DispatchResult { - T::TechnicalOrigin::ensure_origin(origin)?; + T::UpdateLimitsOrigin::ensure_origin(origin)?; ensure!(asset_id != T::OmnipoolHubAsset::get(), Error::::NotAllowed); @@ -358,7 +358,7 @@ pub mod pallet { /// Set add liquidity limit for an asset. /// /// Parameters: - /// - `origin`: The dispatch origin for this call. Must be `TechnicalOrigin` + /// - `origin`: The dispatch origin for this call. Must be `UpdateLimitsOrigin` /// - `asset_id`: The identifier of an asset /// - `liquidity_limit`: Optional add liquidity limit represented as a percentage /// @@ -371,7 +371,7 @@ pub mod pallet { asset_id: T::AssetId, liquidity_limit: Option<(u32, u32)>, ) -> DispatchResult { - T::TechnicalOrigin::ensure_origin(origin)?; + T::UpdateLimitsOrigin::ensure_origin(origin)?; ensure!(asset_id != T::OmnipoolHubAsset::get(), Error::::NotAllowed); @@ -392,7 +392,7 @@ pub mod pallet { /// Set remove liquidity limit for an asset. /// /// Parameters: - /// - `origin`: The dispatch origin for this call. Must be `TechnicalOrigin` + /// - `origin`: The dispatch origin for this call. Must be `UpdateLimitsOrigin` /// - `asset_id`: The identifier of an asset /// - `liquidity_limit`: Optional remove liquidity limit represented as a percentage /// @@ -405,7 +405,7 @@ pub mod pallet { asset_id: T::AssetId, liquidity_limit: Option<(u32, u32)>, ) -> DispatchResult { - T::TechnicalOrigin::ensure_origin(origin)?; + T::UpdateLimitsOrigin::ensure_origin(origin)?; ensure!(asset_id != T::OmnipoolHubAsset::get(), Error::::NotAllowed); diff --git a/pallets/circuit-breaker/src/tests/mock.rs b/pallets/circuit-breaker/src/tests/mock.rs index 9660cf6c3..bf61eddee 100644 --- a/pallets/circuit-breaker/src/tests/mock.rs +++ b/pallets/circuit-breaker/src/tests/mock.rs @@ -137,7 +137,7 @@ impl pallet_circuit_breaker::Config for Test { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; type Balance = Balance; - type TechnicalOrigin = EnsureRoot; + type UpdateLimitsOrigin = EnsureRoot; type WhitelistedAccounts = CircuitBreakerWhitelist; type DefaultMaxNetTradeVolumeLimitPerBlock = DefaultMaxNetTradeVolumeLimitPerBlock; type DefaultMaxAddLiquidityLimitPerBlock = DefaultMaxAddLiquidityLimitPerBlock; @@ -220,7 +220,7 @@ impl pallet_omnipool::Config for Test { type AssetRegistry = DummyRegistry; type MinimumTradingLimit = MinTradeAmount; type MinimumPoolLiquidity = MinAddedLiquidity; - type TechnicalOrigin = EnsureRoot; + type UpdateTradabilityOrigin = EnsureRoot; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; type CollectionId = u32; diff --git a/pallets/conviction-voting/Cargo.toml b/pallets/conviction-voting/Cargo.toml new file mode 100644 index 000000000..35e498e51 --- /dev/null +++ b/pallets/conviction-voting/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "pallet-conviction-voting" +edition = "2021" +version = "28.0.0" +license = "Apache-2.0" +homepage = "https://substrate.io" +description = "FRAME pallet for conviction voting in referenda" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +assert_matches = "1.3.0" +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", + "max-encoded-len", +] } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.195", features = ["derive"], optional = true } +frame-system = {workspace = true } +frame-support = { workspace = true } +sp-io = { workspace = true} +sp-runtime = { workspace = true} +sp-std = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } + +[dev-dependencies] +pallet-balances = { workspace = true } +pallet-scheduler = { workspace = true } +sp-core = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-scheduler/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-scheduler/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/conviction-voting/README.md b/pallets/conviction-voting/README.md new file mode 100644 index 000000000..5dc5d526d --- /dev/null +++ b/pallets/conviction-voting/README.md @@ -0,0 +1,8 @@ +# Voting Pallet + +- [`assembly::Config`](https://docs.rs/pallet-assembly/latest/pallet_assembly/trait.Config.html) +- [`Call`](https://docs.rs/pallet-assembly/latest/pallet_assembly/enum.Call.html) + +## Overview + +Pallet for voting in referenda. diff --git a/pallets/conviction-voting/src/benchmarking.rs b/pallets/conviction-voting/src/benchmarking.rs new file mode 100644 index 000000000..f37bbb873 --- /dev/null +++ b/pallets/conviction-voting/src/benchmarking.rs @@ -0,0 +1,298 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! ConvictionVoting pallet benchmarking. +#![allow(clippy::type_complexity)] + +use super::*; + +use assert_matches::assert_matches; +use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelist_account}; +use frame_support::{ + dispatch::RawOrigin, + traits::{ + fungible, + tokens::{Fortitude::Polite, Preservation::Expendable}, + Currency, Get, + }, +}; +use sp_runtime::traits::Bounded; +use sp_std::collections::btree_map::BTreeMap; + +use crate::Pallet as ConvictionVoting; + +const SEED: u32 = 0; + +/// Fill all classes as much as possible up to `MaxVotes` and return the Class with the most votes +/// ongoing. +fn fill_voting, I: 'static>() -> (ClassOf, BTreeMap, Vec>>) { + let mut r = BTreeMap::, Vec>>::new(); + for class in T::Polls::classes().into_iter() { + for _ in 0..T::MaxVotes::get() { + match T::Polls::create_ongoing(class.clone()) { + Ok(i) => r.entry(class.clone()).or_default().push(i), + Err(()) => break, + } + } + } + let c = r.iter().max_by_key(|(_, v)| v.len()).unwrap().0.clone(); + (c, r) +} + +fn funded_account, I: 'static>(name: &'static str, index: u32) -> T::AccountId { + let caller: T::AccountId = account(name, index, SEED); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + caller +} + +fn account_vote, I: 'static>(b: BalanceOf) -> AccountVote> { + let v = Vote { + aye: true, + conviction: Conviction::Locked1x, + }; + + AccountVote::Standard { vote: v, balance: b } +} + +benchmarks_instance_pallet! { + where_clause { where T::MaxVotes: core::fmt::Debug } + + vote_new { + let caller = funded_account::("caller", 0); + whitelist_account!(caller); + let account_vote = account_vote::(100u32.into()); + + T::VotingHooks::on_vote_worst_case(&caller); + + let (class, all_polls) = fill_voting::(); + let polls = &all_polls[&class]; + let r = polls.len() - 1; + // We need to create existing votes + for i in polls.iter().skip(1) { + ConvictionVoting::::vote(RawOrigin::Signed(caller.clone()).into(), *i, account_vote)?; + } + let votes = match VotingFor::::get(&caller, &class) { + Voting::Casting(Casting { votes, .. }) => votes, + _ => return Err("Votes are not direct".into()), + }; + assert_eq!(votes.len(), r, "Votes were not recorded."); + + let index = polls[0]; + }: vote(RawOrigin::Signed(caller.clone()), index, account_vote) + verify { + assert_matches!( + VotingFor::::get(&caller, &class), + Voting::Casting(Casting { votes, .. }) if votes.len() == (r + 1) + ); + } + + vote_existing { + let caller = funded_account::("caller", 0); + whitelist_account!(caller); + let old_account_vote = account_vote::(100u32.into()); + + T::VotingHooks::on_vote_worst_case(&caller); + + let (class, all_polls) = fill_voting::(); + let polls = &all_polls[&class]; + let r = polls.len(); + // We need to create existing votes + for i in polls.iter() { + ConvictionVoting::::vote(RawOrigin::Signed(caller.clone()).into(), *i, old_account_vote)?; + } + let votes = match VotingFor::::get(&caller, &class) { + Voting::Casting(Casting { votes, .. }) => votes, + _ => return Err("Votes are not direct".into()), + }; + assert_eq!(votes.len(), r, "Votes were not recorded."); + + let new_account_vote = account_vote::(200u32.into()); + let index = polls[0]; + }: vote(RawOrigin::Signed(caller.clone()), index, new_account_vote) + verify { + assert_matches!( + VotingFor::::get(&caller, &class), + Voting::Casting(Casting { votes, .. }) if votes.len() == r + ); + } + + remove_vote { + let caller = funded_account::("caller", 0); + whitelist_account!(caller); + let old_account_vote = account_vote::(100u32.into()); + + T::VotingHooks::on_remove_vote_worst_case(&caller); + + let (class, all_polls) = fill_voting::(); + let polls = &all_polls[&class]; + let r = polls.len(); + // We need to create existing votes + for i in polls.iter() { + ConvictionVoting::::vote(RawOrigin::Signed(caller.clone()).into(), *i, old_account_vote)?; + } + let votes = match VotingFor::::get(&caller, &class) { + Voting::Casting(Casting { votes, .. }) => votes, + _ => return Err("Votes are not direct".into()), + }; + assert_eq!(votes.len(), r, "Votes were not recorded."); + + let index = polls[0]; + }: _(RawOrigin::Signed(caller.clone()), Some(class.clone()), index) + verify { + assert_matches!( + VotingFor::::get(&caller, &class), + Voting::Casting(Casting { votes, .. }) if votes.len() == (r - 1) + ); + } + + remove_other_vote { + let caller = funded_account::("caller", 0); + let voter = funded_account::("caller", 0); + let voter_lookup = T::Lookup::unlookup(voter.clone()); + whitelist_account!(caller); + let old_account_vote = account_vote::(100u32.into()); + + T::VotingHooks::on_remove_vote_worst_case(&caller); + + let (class, all_polls) = fill_voting::(); + let polls = &all_polls[&class]; + let r = polls.len(); + // We need to create existing votes + for i in polls.iter() { + ConvictionVoting::::vote(RawOrigin::Signed(voter.clone()).into(), *i, old_account_vote)?; + } + let votes = match VotingFor::::get(&caller, &class) { + Voting::Casting(Casting { votes, .. }) => votes, + _ => return Err("Votes are not direct".into()), + }; + assert_eq!(votes.len(), r, "Votes were not recorded."); + + let index = polls[0]; + assert!(T::Polls::end_ongoing(index, false).is_ok()); + }: _(RawOrigin::Signed(caller.clone()), voter_lookup, class.clone(), index) + verify { + assert_matches!( + VotingFor::::get(&voter, &class), + Voting::Casting(Casting { votes, .. }) if votes.len() == (r - 1) + ); + } + + delegate { + let r in 0 .. T::MaxVotes::get().min(T::Polls::max_ongoing().1); + + let all_polls = fill_voting::().1; + let class = T::Polls::max_ongoing().0; + let polls = &all_polls[&class]; + let voter = funded_account::("voter", 0); + let voter_lookup = T::Lookup::unlookup(voter.clone()); + let caller = funded_account::("caller", 0); + whitelist_account!(caller); + + let delegated_balance: BalanceOf = 1000u32.into(); + let delegate_vote = account_vote::(delegated_balance); + + // We need to create existing delegations + for i in polls.iter().take(r as usize) { + ConvictionVoting::::vote(RawOrigin::Signed(voter.clone()).into(), *i, delegate_vote)?; + } + assert_matches!( + VotingFor::::get(&voter, &class), + Voting::Casting(Casting { votes, .. }) if votes.len() == r as usize + ); + + }: _(RawOrigin::Signed(caller.clone()), class.clone(), voter_lookup, Conviction::Locked1x, delegated_balance) + verify { + assert_matches!(VotingFor::::get(&caller, &class), Voting::Delegating(_)); + } + + undelegate { + let r in 0 .. T::MaxVotes::get().min(T::Polls::max_ongoing().1); + + let all_polls = fill_voting::().1; + let class = T::Polls::max_ongoing().0; + let polls = &all_polls[&class]; + let voter = funded_account::("voter", 0); + let voter_lookup = T::Lookup::unlookup(voter.clone()); + let caller = funded_account::("caller", 0); + whitelist_account!(caller); + + let delegated_balance: BalanceOf = 1000u32.into(); + let delegate_vote = account_vote::(delegated_balance); + + ConvictionVoting::::delegate( + RawOrigin::Signed(caller.clone()).into(), + class.clone(), + voter_lookup, + Conviction::Locked1x, + delegated_balance, + )?; + + // We need to create delegations + for i in polls.iter().take(r as usize) { + ConvictionVoting::::vote(RawOrigin::Signed(voter.clone()).into(), *i, delegate_vote)?; + } + assert_matches!( + VotingFor::::get(&voter, &class), + Voting::Casting(Casting { votes, .. }) if votes.len() == r as usize + ); + assert_matches!(VotingFor::::get(&caller, &class), Voting::Delegating(_)); + }: _(RawOrigin::Signed(caller.clone()), class.clone()) + verify { + assert_matches!(VotingFor::::get(&caller, &class), Voting::Casting(_)); + } + + unlock { + let caller = funded_account::("caller", 0); + let caller_lookup = T::Lookup::unlookup(caller.clone()); + whitelist_account!(caller); + let normal_account_vote = account_vote::(T::Currency::free_balance(&caller) - 100u32.into()); + let big_account_vote = account_vote::(T::Currency::free_balance(&caller)); + + // Fill everything up to the max by filling all classes with votes and voting on them all. + let (class, all_polls) = fill_voting::(); + assert!(!all_polls.is_empty()); + for (class, polls) in all_polls.iter() { + assert!(!all_polls.is_empty()); + for i in polls.iter() { + ConvictionVoting::::vote(RawOrigin::Signed(caller.clone()).into(), *i, normal_account_vote)?; + } + } + + let orig_usable = >::reducible_balance(&caller, Expendable, Polite); + let polls = &all_polls[&class]; + + // Vote big on the class with the most ongoing votes of them to bump the lock and make it + // hard to recompute when removed. + ConvictionVoting::::vote(RawOrigin::Signed(caller.clone()).into(), polls[0], big_account_vote)?; + let now_usable = >::reducible_balance(&caller, Expendable, Polite); + assert_eq!(orig_usable - now_usable, 100u32.into()); + + // Remove the vote + ConvictionVoting::::remove_vote(RawOrigin::Signed(caller.clone()).into(), Some(class.clone()), polls[0])?; + + // We can now unlock on `class` from 200 to 100... + }: _(RawOrigin::Signed(caller.clone()), class, caller_lookup) + verify { + assert_eq!(orig_usable, >::reducible_balance(&caller, Expendable, Polite)); + } + + impl_benchmark_test_suite!( + ConvictionVoting, + crate::tests::new_test_ext(), + crate::tests::Test + ); +} diff --git a/pallets/conviction-voting/src/conviction.rs b/pallets/conviction-voting/src/conviction.rs new file mode 100644 index 000000000..a10b46765 --- /dev/null +++ b/pallets/conviction-voting/src/conviction.rs @@ -0,0 +1,113 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The conviction datatype. + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Bounded, CheckedDiv, CheckedMul, Zero}, + RuntimeDebug, +}; + +use crate::types::Delegations; + +/// A value denoting the strength of conviction of a vote. +#[derive( + Default, Encode, Decode, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen, +)] +pub enum Conviction { + /// 0.1x votes, unlocked. + #[default] + None, + /// 1x votes, locked for an enactment period following a successful vote. + Locked1x, + /// 2x votes, locked for 2x enactment periods following a successful vote. + Locked2x, + /// 3x votes, locked for 4x... + Locked3x, + /// 4x votes, locked for 8x... + Locked4x, + /// 5x votes, locked for 16x... + Locked5x, + /// 6x votes, locked for 32x... + Locked6x, +} + +impl From for u8 { + fn from(c: Conviction) -> u8 { + match c { + Conviction::None => 0, + Conviction::Locked1x => 1, + Conviction::Locked2x => 2, + Conviction::Locked3x => 3, + Conviction::Locked4x => 4, + Conviction::Locked5x => 5, + Conviction::Locked6x => 6, + } + } +} + +impl TryFrom for Conviction { + type Error = (); + fn try_from(i: u8) -> Result { + Ok(match i { + 0 => Conviction::None, + 1 => Conviction::Locked1x, + 2 => Conviction::Locked2x, + 3 => Conviction::Locked3x, + 4 => Conviction::Locked4x, + 5 => Conviction::Locked5x, + 6 => Conviction::Locked6x, + _ => return Err(()), + }) + } +} + +impl Conviction { + /// The amount of time (in number of periods) that our conviction implies a successful voter's + /// balance should be locked for. + pub fn lock_periods(self) -> u32 { + match self { + Conviction::None => 0, + Conviction::Locked1x => 1, + Conviction::Locked2x => 2, + Conviction::Locked3x => 4, + Conviction::Locked4x => 8, + Conviction::Locked5x => 16, + Conviction::Locked6x => 32, + } + } + + /// The votes of a voter of the given `balance` with our conviction. + pub fn votes + Zero + Copy + CheckedMul + CheckedDiv + Bounded>(self, capital: B) -> Delegations { + let votes = match self { + Conviction::None => capital.checked_div(&10u8.into()).unwrap_or_else(Zero::zero), + x => capital.checked_mul(&u8::from(x).into()).unwrap_or_else(B::max_value), + }; + Delegations { votes, capital } + } +} + +impl Bounded for Conviction { + fn min_value() -> Self { + Conviction::None + } + fn max_value() -> Self { + Conviction::Locked6x + } +} diff --git a/pallets/conviction-voting/src/lib.rs b/pallets/conviction-voting/src/lib.rs new file mode 100644 index 000000000..302b3a3d5 --- /dev/null +++ b/pallets/conviction-voting/src/lib.rs @@ -0,0 +1,701 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Voting Pallet +//! +//! - [`Config`] +//! - [`Call`] +//! +//! ## Overview +//! +//! Pallet for managing actual voting in polls. + +#![recursion_limit = "256"] +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + dispatch::DispatchResult, + ensure, + traits::{ + fungible, Currency, Get, LockIdentifier, LockableCurrency, PollStatus, Polling, ReservableCurrency, + WithdrawReasons, + }, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, Saturating, StaticLookup, Zero}, + ArithmeticError, DispatchError, Perbill, +}; +use sp_std::prelude::*; + +mod conviction; +pub mod traits; +mod types; +mod vote; +pub mod weights; + +pub use self::{ + conviction::Conviction, + pallet::*, + traits::VotingHooks, + types::{Delegations, Tally, UnvoteScope}, + vote::{AccountVote, Casting, Delegating, Vote, Voting}, + weights::WeightInfo, +}; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +const CONVICTION_VOTING_ID: LockIdentifier = *b"pyconvot"; + +type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +type BalanceOf = <>::Currency as Currency<::AccountId>>::Balance; +type VotingOf = Voting< + BalanceOf, + ::AccountId, + BlockNumberFor, + PollIndexOf, + >::MaxVotes, +>; +#[allow(dead_code)] +type DelegatingOf = Delegating, ::AccountId, BlockNumberFor>; +pub type TallyOf = Tally, >::MaxTurnout>; +pub type VotesOf = BalanceOf; +type PollIndexOf = <>::Polls as Polling>>::Index; +#[cfg(feature = "runtime-benchmarks")] +type IndexOf = <>::Polls as Polling>>::Index; +type ClassOf = <>::Polls as Polling>>::Class; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + pallet_prelude::{DispatchResultWithPostInfo, IsType, StorageDoubleMap, StorageMap, ValueQuery}, + traits::ClassCountOf, + Twox64Concat, + }; + use frame_system::pallet_prelude::*; + use sp_runtime::BoundedVec; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + Sized { + // System level stuff. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + /// Currency type with which voting happens. + type Currency: ReservableCurrency + + LockableCurrency> + + fungible::Inspect; + + /// The implementation of the logic which conducts polls. + type Polls: Polling, Votes = BalanceOf, Moment = BlockNumberFor>; + + /// The maximum amount of tokens which may be used for voting. May just be + /// `Currency::total_issuance`, but you might want to reduce this in order to account for + /// funds in the system which are unable to vote (e.g. parachain auction deposits). + type MaxTurnout: Get>; + + /// The maximum number of concurrent votes an account may have. + /// + /// Also used to compute weight, an overly large value can lead to extrinsics with large + /// weight estimation: see `delegate` for instance. + #[pallet::constant] + type MaxVotes: Get; + + /// The minimum period of vote locking. + /// + /// It should be no shorter than enactment period to ensure that in the case of an approval, + /// those successful voters are locked into the consequences that their votes entail. + #[pallet::constant] + type VoteLockingPeriod: Get>; + + /// Hooks are actions that are executed on certain events. + /// Events: on_vote, on_remove_vote, on_remove_unsuccessful_vote + type VotingHooks: VotingHooks, BalanceOf>; + } + + /// All voting for a particular voter in a particular voting class. We store the balance for the + /// number of votes that we have recorded. + #[pallet::storage] + pub type VotingFor, I: 'static = ()> = + StorageDoubleMap<_, Twox64Concat, T::AccountId, Twox64Concat, ClassOf, VotingOf, ValueQuery>; + + /// The voting classes which have a non-zero lock requirement and the lock amounts which they + /// require. The actual amount locked on behalf of this pallet should always be the maximum of + /// this list. + #[pallet::storage] + pub type ClassLocksFor, I: 'static = ()> = StorageMap< + _, + Twox64Concat, + T::AccountId, + BoundedVec<(ClassOf, BalanceOf), ClassCountOf>>, + ValueQuery, + >; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// An account has delegated their vote to another account. \[who, target\] + Delegated(T::AccountId, T::AccountId), + /// An \[account\] has cancelled a previous delegation operation. + Undelegated(T::AccountId), + } + + #[pallet::error] + pub enum Error { + /// Poll is not ongoing. + NotOngoing, + /// The given account did not vote on the poll. + NotVoter, + /// The actor has no permission to conduct the action. + NoPermission, + /// The actor has no permission to conduct the action right now but will do in the future. + NoPermissionYet, + /// The account is already delegating. + AlreadyDelegating, + /// The account currently has votes attached to it and the operation cannot succeed until + /// these are removed, either through `unvote` or `reap_vote`. + AlreadyVoting, + /// Too high a balance was provided that the account cannot afford. + InsufficientFunds, + /// The account is not currently delegating. + NotDelegating, + /// Delegation to oneself makes no sense. + Nonsense, + /// Maximum number of votes reached. + MaxVotesReached, + /// The class must be supplied since it is not easily determinable from the state. + ClassNeeded, + /// The class ID supplied is invalid. + BadClass, + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Vote in a poll. If `vote.is_aye()`, the vote is to enact the proposal; + /// otherwise it is a vote to keep the status quo. + /// + /// The dispatch origin of this call must be _Signed_. + /// + /// - `poll_index`: The index of the poll to vote for. + /// - `vote`: The vote configuration. + /// + /// Weight: `O(R)` where R is the number of polls the voter has voted on. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::vote_new().max(T::WeightInfo::vote_existing()))] + pub fn vote( + origin: OriginFor, + #[pallet::compact] poll_index: PollIndexOf, + vote: AccountVote>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::try_vote(&who, poll_index, vote) + } + + /// Delegate the voting power (with some given conviction) of the sending account for a + /// particular class of polls. + /// + /// The balance delegated is locked for as long as it's delegated, and thereafter for the + /// time appropriate for the conviction's lock period. + /// + /// The dispatch origin of this call must be _Signed_, and the signing account must either: + /// - be delegating already; or + /// - have no voting activity (if there is, then it will need to be removed/consolidated + /// through `reap_vote` or `unvote`). + /// + /// - `to`: The account whose voting the `target` account's voting power will follow. + /// - `class`: The class of polls to delegate. To delegate multiple classes, multiple calls + /// to this function are required. + /// - `conviction`: The conviction that will be attached to the delegated votes. When the + /// account is undelegated, the funds will be locked for the corresponding period. + /// - `balance`: The amount of the account's balance to be used in delegating. This must not + /// be more than the account's current balance. + /// + /// Emits `Delegated`. + /// + /// Weight: `O(R)` where R is the number of polls the voter delegating to has + /// voted on. Weight is initially charged as if maximum votes, but is refunded later. + // NOTE: weight must cover an incorrect voting of origin with max votes, this is ensure + // because a valid delegation cover decoding a direct voting with max votes. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::delegate(T::MaxVotes::get()))] + pub fn delegate( + origin: OriginFor, + class: ClassOf, + to: AccountIdLookupOf, + conviction: Conviction, + balance: BalanceOf, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let to = T::Lookup::lookup(to)?; + let votes = Self::try_delegate(who, class, to, conviction, balance)?; + + Ok(Some(T::WeightInfo::delegate(votes)).into()) + } + + /// Undelegate the voting power of the sending account for a particular class of polls. + /// + /// Tokens may be unlocked following once an amount of time consistent with the lock period + /// of the conviction with which the delegation was issued has passed. + /// + /// The dispatch origin of this call must be _Signed_ and the signing account must be + /// currently delegating. + /// + /// - `class`: The class of polls to remove the delegation from. + /// + /// Emits `Undelegated`. + /// + /// Weight: `O(R)` where R is the number of polls the voter delegating to has + /// voted on. Weight is initially charged as if maximum votes, but is refunded later. + // NOTE: weight must cover an incorrect voting of origin with max votes, this is ensure + // because a valid delegation cover decoding a direct voting with max votes. + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::undelegate(T::MaxVotes::get()))] + pub fn undelegate(origin: OriginFor, class: ClassOf) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let votes = Self::try_undelegate(who, class)?; + Ok(Some(T::WeightInfo::undelegate(votes)).into()) + } + + /// Remove the lock caused by prior voting/delegating which has expired within a particular + /// class. + /// + /// The dispatch origin of this call must be _Signed_. + /// + /// - `class`: The class of polls to unlock. + /// - `target`: The account to remove the lock on. + /// + /// Weight: `O(R)` with R number of vote of target. + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::unlock())] + pub fn unlock(origin: OriginFor, class: ClassOf, target: AccountIdLookupOf) -> DispatchResult { + ensure_signed(origin)?; + let target = T::Lookup::lookup(target)?; + Self::update_lock(&class, &target); + Ok(()) + } + + /// Remove a vote for a poll. + /// + /// If: + /// - the poll was cancelled, or + /// - the poll is ongoing, or + /// - the poll has ended such that + /// - the vote of the account was in opposition to the result; or + /// - there was no conviction to the account's vote; or + /// - the account made a split vote + /// ...then the vote is removed cleanly and a following call to `unlock` may result in more + /// funds being available. + /// + /// If, however, the poll has ended and: + /// - it finished corresponding to the vote of the account, and + /// - the account made a standard vote with conviction, and + /// - the lock period of the conviction is not over + /// ...then the lock will be aggregated into the overall account's lock, which may involve + /// *overlocking* (where the two locks are combined into a single lock that is the maximum + /// of both the amount locked and the time is it locked for). + /// + /// The dispatch origin of this call must be _Signed_, and the signer must have a vote + /// registered for poll `index`. + /// + /// - `index`: The index of poll of the vote to be removed. + /// - `class`: Optional parameter, if given it indicates the class of the poll. For polls + /// which have finished or are cancelled, this must be `Some`. + /// + /// Weight: `O(R + log R)` where R is the number of polls that `target` has voted on. + /// Weight is calculated for the maximum number of vote. + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::remove_vote())] + pub fn remove_vote( + origin: OriginFor, + class: Option>, + index: PollIndexOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::try_remove_vote(&who, index, class, UnvoteScope::Any) + } + + /// Remove a vote for a poll. + /// + /// If the `target` is equal to the signer, then this function is exactly equivalent to + /// `remove_vote`. If not equal to the signer, then the vote must have expired, + /// either because the poll was cancelled, because the voter lost the poll or + /// because the conviction period is over. + /// + /// The dispatch origin of this call must be _Signed_. + /// + /// - `target`: The account of the vote to be removed; this account must have voted for poll + /// `index`. + /// - `index`: The index of poll of the vote to be removed. + /// - `class`: The class of the poll. + /// + /// Weight: `O(R + log R)` where R is the number of polls that `target` has voted on. + /// Weight is calculated for the maximum number of vote. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::remove_other_vote())] + pub fn remove_other_vote( + origin: OriginFor, + target: AccountIdLookupOf, + class: ClassOf, + index: PollIndexOf, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let target = T::Lookup::lookup(target)?; + let scope = if target == who { + UnvoteScope::Any + } else { + UnvoteScope::OnlyExpired + }; + Self::try_remove_vote(&target, index, Some(class), scope)?; + Ok(()) + } + } +} + +impl, I: 'static> Pallet { + /// Actually enact a vote, if legit. + fn try_vote( + who: &T::AccountId, + poll_index: PollIndexOf, + vote: AccountVote>, + ) -> DispatchResult { + ensure!( + vote.balance() <= T::Currency::total_balance(who), + Error::::InsufficientFunds + ); + T::Polls::try_access_poll(poll_index, |poll_status| { + let (tally, class) = poll_status.ensure_ongoing().ok_or(Error::::NotOngoing)?; + VotingFor::::try_mutate(who, &class, |voting| { + if let Voting::Casting(Casting { + ref mut votes, + delegations, + .. + }) = voting + { + match votes.binary_search_by_key(&poll_index, |i| i.0) { + Ok(i) => { + // Shouldn't be possible to fail, but we handle it gracefully. + tally.remove(votes[i].1).ok_or(ArithmeticError::Underflow)?; + if let Some(approve) = votes[i].1.as_standard() { + tally.reduce(approve, *delegations); + } + votes[i].1 = vote; + } + Err(i) => { + votes + .try_insert(i, (poll_index, vote)) + .map_err(|_| Error::::MaxVotesReached)?; + } + } + // Shouldn't be possible to fail, but we handle it gracefully. + tally.add(vote).ok_or(ArithmeticError::Overflow)?; + if let Some(approve) = vote.as_standard() { + tally.increase(approve, *delegations); + } + } else { + return Err(Error::::AlreadyDelegating.into()); + } + // Extend the lock to `balance` (rather than setting it) since we don't know what + // other votes are in place. + Self::extend_lock(who, &class, vote.balance()); + + // Call on_vote hook + T::VotingHooks::on_vote(who, poll_index, vote)?; + Ok(()) + }) + }) + } + + /// Remove the account's vote for the given poll if possible. This is possible when: + /// - The poll has not finished. + /// - The poll has finished and the voter lost their direction. + /// - The poll has finished and the voter's lock period is up. + /// + /// This will generally be combined with a call to `unlock`. + fn try_remove_vote( + who: &T::AccountId, + poll_index: PollIndexOf, + class_hint: Option>, + scope: UnvoteScope, + ) -> DispatchResult { + let class = class_hint + .or_else(|| Some(T::Polls::as_ongoing(poll_index)?.1)) + .ok_or(Error::::ClassNeeded)?; + VotingFor::::try_mutate(who, class, |voting| { + if let Voting::Casting(Casting { + ref mut votes, + delegations, + ref mut prior, + }) = voting + { + let i = votes + .binary_search_by_key(&poll_index, |i| i.0) + .map_err(|_| Error::::NotVoter)?; + let v = votes.remove(i); + + T::Polls::try_access_poll(poll_index, |poll_status| match poll_status { + PollStatus::Ongoing(tally, _) => { + ensure!(matches!(scope, UnvoteScope::Any), Error::::NoPermission); + // Shouldn't be possible to fail, but we handle it gracefully. + tally.remove(v.1).ok_or(ArithmeticError::Underflow)?; + if let Some(approve) = v.1.as_standard() { + tally.reduce(approve, *delegations); + } + T::VotingHooks::on_remove_vote(who, poll_index, Some(true)); + Ok(()) + } + PollStatus::Completed(end, approved) => { + if let Some((lock_periods, balance)) = v.1.locked_if(approved) { + let unlock_at = + end.saturating_add(T::VoteLockingPeriod::get().saturating_mul(lock_periods.into())); + let now = frame_system::Pallet::::block_number(); + if now < unlock_at { + ensure!(matches!(scope, UnvoteScope::Any), Error::::NoPermissionYet); + prior.accumulate(unlock_at, balance) + } + } else if v.1.as_standard() == Some(!approved) { + // Unsuccessful vote, use special hooks to lock the funds too in case of conviction. + if let Some(to_lock) = T::VotingHooks::locked_if_unsuccessful_vote(who, poll_index) { + if let AccountVote::Standard { vote, .. } = v.1 { + let unlock_at = end.saturating_add( + T::VoteLockingPeriod::get() + .saturating_mul(vote.conviction.lock_periods().into()), + ); + let now = frame_system::Pallet::::block_number(); + if now < unlock_at { + ensure!(matches!(scope, UnvoteScope::Any), Error::::NoPermissionYet); + prior.accumulate(unlock_at, to_lock) + } + } + } + } + // Call on_remove_vote hook + T::VotingHooks::on_remove_vote(who, poll_index, Some(false)); + Ok(()) + } + PollStatus::None => { + // Poll was cancelled. + T::VotingHooks::on_remove_vote(who, poll_index, None); + Ok(()) + } + }) + } else { + Ok(()) + } + }) + } + + /// Return the number of votes for `who`. + fn increase_upstream_delegation( + who: &T::AccountId, + class: &ClassOf, + amount: Delegations>, + ) -> u32 { + VotingFor::::mutate(who, class, |voting| match voting { + Voting::Delegating(Delegating { delegations, .. }) => { + // We don't support second level delegating, so we don't need to do anything more. + *delegations = delegations.saturating_add(amount); + 1 + } + Voting::Casting(Casting { votes, delegations, .. }) => { + *delegations = delegations.saturating_add(amount); + for &(poll_index, account_vote) in votes.iter() { + if let AccountVote::Standard { vote, .. } = account_vote { + T::Polls::access_poll(poll_index, |poll_status| { + if let PollStatus::Ongoing(tally, _) = poll_status { + tally.increase(vote.aye, amount); + } + }); + } + } + votes.len() as u32 + } + }) + } + + /// Return the number of votes for `who`. + fn reduce_upstream_delegation( + who: &T::AccountId, + class: &ClassOf, + amount: Delegations>, + ) -> u32 { + VotingFor::::mutate(who, class, |voting| match voting { + Voting::Delegating(Delegating { delegations, .. }) => { + // We don't support second level delegating, so we don't need to do anything more. + *delegations = delegations.saturating_sub(amount); + 1 + } + Voting::Casting(Casting { votes, delegations, .. }) => { + *delegations = delegations.saturating_sub(amount); + for &(poll_index, account_vote) in votes.iter() { + if let AccountVote::Standard { vote, .. } = account_vote { + T::Polls::access_poll(poll_index, |poll_status| { + if let PollStatus::Ongoing(tally, _) = poll_status { + tally.reduce(vote.aye, amount); + } + }); + } + } + votes.len() as u32 + } + }) + } + + /// Attempt to delegate `balance` times `conviction` of voting power from `who` to `target`. + /// + /// Return the upstream number of votes. + fn try_delegate( + who: T::AccountId, + class: ClassOf, + target: T::AccountId, + conviction: Conviction, + balance: BalanceOf, + ) -> Result { + ensure!(who != target, Error::::Nonsense); + T::Polls::classes() + .binary_search(&class) + .map_err(|_| Error::::BadClass)?; + ensure!( + balance <= T::Currency::total_balance(&who), + Error::::InsufficientFunds + ); + let votes = VotingFor::::try_mutate(&who, &class, |voting| -> Result { + let old = sp_std::mem::replace( + voting, + Voting::Delegating(Delegating { + balance, + target: target.clone(), + conviction, + delegations: Default::default(), + prior: Default::default(), + }), + ); + match old { + Voting::Delegating(Delegating { .. }) => return Err(Error::::AlreadyDelegating.into()), + Voting::Casting(Casting { + votes, + delegations, + prior, + }) => { + // here we just ensure that we're currently idling with no votes recorded. + ensure!(votes.is_empty(), Error::::AlreadyVoting); + voting.set_common(delegations, prior); + } + } + + let votes = Self::increase_upstream_delegation(&target, &class, conviction.votes(balance)); + // Extend the lock to `balance` (rather than setting it) since we don't know what + // other votes are in place. + Self::extend_lock(&who, &class, balance); + Ok(votes) + })?; + Self::deposit_event(Event::::Delegated(who, target)); + Ok(votes) + } + + /// Attempt to end the current delegation. + /// + /// Return the number of votes of upstream. + fn try_undelegate(who: T::AccountId, class: ClassOf) -> Result { + let votes = VotingFor::::try_mutate(&who, &class, |voting| -> Result { + match sp_std::mem::take(voting) { + Voting::Delegating(Delegating { + balance, + target, + conviction, + delegations, + mut prior, + }) => { + // remove any delegation votes to our current target. + let votes = Self::reduce_upstream_delegation(&target, &class, conviction.votes(balance)); + let now = frame_system::Pallet::::block_number(); + let lock_periods = conviction.lock_periods().into(); + prior.accumulate( + now.saturating_add(T::VoteLockingPeriod::get().saturating_mul(lock_periods)), + balance, + ); + voting.set_common(delegations, prior); + + Ok(votes) + } + Voting::Casting(_) => Err(Error::::NotDelegating.into()), + } + })?; + Self::deposit_event(Event::::Undelegated(who)); + Ok(votes) + } + + fn extend_lock(who: &T::AccountId, class: &ClassOf, amount: BalanceOf) { + ClassLocksFor::::mutate(who, |locks| match locks.iter().position(|x| &x.0 == class) { + Some(i) => locks[i].1 = locks[i].1.max(amount), + None => { + let ok = locks.try_push((class.clone(), amount)).is_ok(); + debug_assert!( + ok, + "Vec bounded by number of classes; \ + all items in Vec associated with a unique class; \ + qed" + ); + } + }); + T::Currency::extend_lock( + CONVICTION_VOTING_ID, + who, + amount, + WithdrawReasons::except(WithdrawReasons::RESERVE), + ); + } + + /// Rejig the lock on an account. It will never get more stringent (since that would indicate + /// a security hole) but may be reduced from what they are currently. + fn update_lock(class: &ClassOf, who: &T::AccountId) { + let class_lock_needed = VotingFor::::mutate(who, class, |voting| { + voting.rejig(frame_system::Pallet::::block_number()); + voting.locked_balance() + }); + let lock_needed = ClassLocksFor::::mutate(who, |locks| { + locks.retain(|x| &x.0 != class); + if !class_lock_needed.is_zero() { + let ok = locks.try_push((class.clone(), class_lock_needed)).is_ok(); + debug_assert!( + ok, + "Vec bounded by number of classes; \ + all items in Vec associated with a unique class; \ + qed" + ); + } + locks.iter().map(|x| x.1).max().unwrap_or(Zero::zero()) + }); + if lock_needed.is_zero() { + T::Currency::remove_lock(CONVICTION_VOTING_ID, who); + } else { + T::Currency::set_lock( + CONVICTION_VOTING_ID, + who, + lock_needed, + WithdrawReasons::except(WithdrawReasons::RESERVE), + ); + } + } +} diff --git a/pallets/conviction-voting/src/tests.rs b/pallets/conviction-voting/src/tests.rs new file mode 100644 index 000000000..d18bda853 --- /dev/null +++ b/pallets/conviction-voting/src/tests.rs @@ -0,0 +1,1176 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The crate's tests. +#![allow(clippy::manual_next_back)] + +use std::cell::RefCell; +use std::collections::BTreeMap; + +use frame_support::{ + assert_noop, assert_ok, derive_impl, parameter_types, + traits::{ConstU32, ConstU64, Contains, Polling, VoteTally}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +use super::*; +use crate as pallet_conviction_voting; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + Voting: pallet_conviction_voting, + } +); + +// Test that a fitlered call can be dispatched. +pub struct BaseFilter; +impl Contains for BaseFilter { + fn contains(call: &RuntimeCall) -> bool { + !matches!( + call, + &RuntimeCall::Balances(pallet_balances::Call::force_set_balance { .. }) + ) + } +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = BaseFilter; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type MaxLocks = ConstU32<10>; + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum TestPollState { + Ongoing(TallyOf, u8), + Completed(u64, bool), +} +use TestPollState::*; + +parameter_types! { + pub static Polls: BTreeMap = vec![ + (1, Completed(1, true)), + (2, Completed(2, false)), + (3, Ongoing(Tally::from_parts(0, 0, 0), 0)), + ].into_iter().collect(); +} + +pub struct TestPolls; +impl Polling> for TestPolls { + type Index = u8; + type Votes = u64; + type Moment = u64; + type Class = u8; + fn classes() -> Vec { + vec![0, 1, 2] + } + fn as_ongoing(index: u8) -> Option<(TallyOf, Self::Class)> { + Polls::get().remove(&index).and_then(|x| { + if let TestPollState::Ongoing(t, c) = x { + Some((t, c)) + } else { + None + } + }) + } + fn access_poll(index: Self::Index, f: impl FnOnce(PollStatus<&mut TallyOf, u64, u8>) -> R) -> R { + let mut polls = Polls::get(); + let entry = polls.get_mut(&index); + let r = match entry { + Some(Ongoing(ref mut tally_mut_ref, class)) => f(PollStatus::Ongoing(tally_mut_ref, *class)), + Some(Completed(when, succeeded)) => f(PollStatus::Completed(*when, *succeeded)), + None => f(PollStatus::None), + }; + Polls::set(polls); + r + } + fn try_access_poll( + index: Self::Index, + f: impl FnOnce(PollStatus<&mut TallyOf, u64, u8>) -> Result, + ) -> Result { + let mut polls = Polls::get(); + let entry = polls.get_mut(&index); + let r = match entry { + Some(Ongoing(ref mut tally_mut_ref, class)) => f(PollStatus::Ongoing(tally_mut_ref, *class)), + Some(Completed(when, succeeded)) => f(PollStatus::Completed(*when, *succeeded)), + None => f(PollStatus::None), + }?; + Polls::set(polls); + Ok(r) + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_ongoing(class: Self::Class) -> Result { + let mut polls = Polls::get(); + let i = polls.keys().rev().next().map_or(0, |x| x + 1); + polls.insert(i, Ongoing(Tally::new(0), class)); + Polls::set(polls); + Ok(i) + } + + #[cfg(feature = "runtime-benchmarks")] + fn end_ongoing(index: Self::Index, approved: bool) -> Result<(), ()> { + let mut polls = Polls::get(); + match polls.get(&index) { + Some(Ongoing(..)) => {} + _ => return Err(()), + } + let now = frame_system::Pallet::::block_number(); + polls.insert(index, Completed(now, approved)); + Polls::set(polls); + Ok(()) + } +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = pallet_balances::Pallet; + type VoteLockingPeriod = ConstU64<3>; + type MaxVotes = ConstU32<3>; + type WeightInfo = (); + type MaxTurnout = frame_support::traits::TotalIssuanceOf; + type Polls = TestPolls; + type VotingHooks = HooksHandler; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], + } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +#[test] +fn params_should_work() { + new_test_ext().execute_with(|| { + assert_eq!(Balances::free_balance(42), 0); + assert_eq!(Balances::total_issuance(), 210); + }); +} + +fn next_block() { + System::set_block_number(System::block_number() + 1); +} + +#[allow(dead_code)] +fn run_to(n: u64) { + while System::block_number() < n { + next_block(); + } +} + +fn aye(amount: u64, conviction: u8) -> AccountVote { + let vote = Vote { + aye: true, + conviction: conviction.try_into().unwrap(), + }; + AccountVote::Standard { vote, balance: amount } +} + +fn nay(amount: u64, conviction: u8) -> AccountVote { + let vote = Vote { + aye: false, + conviction: conviction.try_into().unwrap(), + }; + AccountVote::Standard { vote, balance: amount } +} + +fn split(aye: u64, nay: u64) -> AccountVote { + AccountVote::Split { aye, nay } +} + +fn split_abstain(aye: u64, nay: u64, abstain: u64) -> AccountVote { + AccountVote::SplitAbstain { aye, nay, abstain } +} + +fn tally(index: u8) -> TallyOf { + >>::as_ongoing(index) + .expect("No poll") + .0 +} + +fn class(index: u8) -> u8 { + >>::as_ongoing(index) + .expect("No poll") + .1 +} + +#[test] +#[ignore] +#[should_panic(expected = "No poll")] +fn unknown_poll_should_panic() { + let _ = tally(0); +} + +#[test] +#[ignore] +#[should_panic(expected = "No poll")] +fn completed_poll_should_panic() { + let _ = tally(1); +} + +#[test] +fn basic_stuff() { + new_test_ext().execute_with(|| { + assert_eq!(tally(3), Tally::from_parts(0, 0, 0)); + }); +} + +#[test] +fn basic_voting_works() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(2, 5))); + assert_eq!(tally(3), Tally::from_parts(10, 0, 2)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, nay(2, 5))); + assert_eq!(tally(3), Tally::from_parts(0, 10, 0)); + assert_eq!(Balances::usable_balance(1), 8); + + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(5, 1))); + assert_eq!(tally(3), Tally::from_parts(5, 0, 5)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, nay(5, 1))); + assert_eq!(tally(3), Tally::from_parts(0, 5, 0)); + assert_eq!(Balances::usable_balance(1), 5); + + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(10, 0))); + assert_eq!(tally(3), Tally::from_parts(1, 0, 10)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, nay(10, 0))); + assert_eq!(tally(3), Tally::from_parts(0, 1, 0)); + assert_eq!(Balances::usable_balance(1), 0); + + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), None, 3)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 0)); + + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), class(3), 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn split_voting_works() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, split(10, 0))); + assert_eq!(tally(3), Tally::from_parts(1, 0, 10)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, split(5, 5))); + assert_eq!(tally(3), Tally::from_parts(0, 0, 5)); + assert_eq!(Balances::usable_balance(1), 0); + + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), None, 3)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 0)); + + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), class(3), 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn abstain_voting_works() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, split_abstain(0, 0, 10))); + assert_eq!(tally(3), Tally::from_parts(0, 0, 10)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 3, split_abstain(0, 0, 20))); + assert_eq!(tally(3), Tally::from_parts(0, 0, 30)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 3, split_abstain(10, 0, 10))); + assert_eq!(tally(3), Tally::from_parts(1, 0, 30)); + assert_eq!(Balances::usable_balance(1), 0); + assert_eq!(Balances::usable_balance(2), 0); + + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), None, 3)); + assert_eq!(tally(3), Tally::from_parts(1, 0, 20)); + + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(2), None, 3)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 0)); + + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), class(3), 1)); + assert_eq!(Balances::usable_balance(1), 10); + + assert_ok!(Voting::unlock(RuntimeOrigin::signed(2), class(3), 2)); + assert_eq!(Balances::usable_balance(2), 20); + }); +} + +#[test] +fn voting_balance_gets_locked() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(2, 5))); + assert_eq!(tally(3), Tally::from_parts(10, 0, 2)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, nay(2, 5))); + assert_eq!(tally(3), Tally::from_parts(0, 10, 0)); + assert_eq!(Balances::usable_balance(1), 8); + + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(5, 1))); + assert_eq!(tally(3), Tally::from_parts(5, 0, 5)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, nay(5, 1))); + assert_eq!(tally(3), Tally::from_parts(0, 5, 0)); + assert_eq!(Balances::usable_balance(1), 5); + + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(10, 0))); + assert_eq!(tally(3), Tally::from_parts(1, 0, 10)); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, nay(10, 0))); + assert_eq!(tally(3), Tally::from_parts(0, 1, 0)); + assert_eq!(Balances::usable_balance(1), 0); + + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), None, 3)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 0)); + + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), class(3), 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn successful_but_zero_conviction_vote_balance_can_be_unlocked() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(1, 1))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 3, nay(20, 0))); + let c = class(3); + Polls::set(vec![(3, Completed(3, false))].into_iter().collect()); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(2), Some(c), 3)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(2), c, 2)); + assert_eq!(Balances::usable_balance(2), 20); + }); +} + +#[test] +fn unsuccessful_conviction_vote_balance_can_be_unlocked() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(1, 1))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 3, nay(20, 0))); + let c = class(3); + Polls::set(vec![(3, Completed(3, false))].into_iter().collect()); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(c), 3)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), c, 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn successful_conviction_vote_balance_stays_locked_for_correct_time() { + new_test_ext().execute_with(|| { + for i in 1..=5 { + assert_ok!(Voting::vote(RuntimeOrigin::signed(i), 3, aye(10, i as u8))); + } + let c = class(3); + Polls::set(vec![(3, Completed(3, true))].into_iter().collect()); + for i in 1..=5 { + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(i), Some(c), 3)); + } + for block in 1..=(3 + 5 * 3) { + run_to(block); + for i in 1..=5 { + assert_ok!(Voting::unlock(RuntimeOrigin::signed(i), c, i)); + let expired = block >= (3 << (i - 1)) + 3; + assert_eq!(Balances::usable_balance(i), i * 10 - if expired { 0 } else { 10 }); + } + } + }); +} + +#[test] +fn classwise_delegation_works() { + new_test_ext().execute_with(|| { + Polls::set( + vec![ + (0, Ongoing(Tally::new(0), 0)), + (1, Ongoing(Tally::new(0), 1)), + (2, Ongoing(Tally::new(0), 2)), + (3, Ongoing(Tally::new(0), 2)), + ] + .into_iter() + .collect(), + ); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 5 + )); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 1, + 3, + Conviction::Locked1x, + 5 + )); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 2, + 4, + Conviction::Locked1x, + 5 + )); + assert_eq!(Balances::usable_balance(1), 5); + + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 0, aye(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 1, nay(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 2, nay(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(3), 0, nay(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(3), 1, aye(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(3), 2, nay(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(4), 0, nay(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(4), 1, nay(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(4), 2, aye(10, 0))); + // 4 hasn't voted yet + + assert_eq!( + Polls::get(), + vec![ + (0, Ongoing(Tally::from_parts(6, 2, 15), 0)), + (1, Ongoing(Tally::from_parts(6, 2, 15), 1)), + (2, Ongoing(Tally::from_parts(6, 2, 15), 2)), + (3, Ongoing(Tally::from_parts(0, 0, 0), 2)), + ] + .into_iter() + .collect() + ); + + // 4 votes nay to 3. + assert_ok!(Voting::vote(RuntimeOrigin::signed(4), 3, nay(10, 0))); + assert_eq!( + Polls::get(), + vec![ + (0, Ongoing(Tally::from_parts(6, 2, 15), 0)), + (1, Ongoing(Tally::from_parts(6, 2, 15), 1)), + (2, Ongoing(Tally::from_parts(6, 2, 15), 2)), + (3, Ongoing(Tally::from_parts(0, 6, 0), 2)), + ] + .into_iter() + .collect() + ); + + // Redelegate for class 2 to account 3. + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 2)); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 2, + 3, + Conviction::Locked1x, + 5 + )); + assert_eq!( + Polls::get(), + vec![ + (0, Ongoing(Tally::from_parts(6, 2, 15), 0)), + (1, Ongoing(Tally::from_parts(6, 2, 15), 1)), + (2, Ongoing(Tally::from_parts(1, 7, 10), 2)), + (3, Ongoing(Tally::from_parts(0, 1, 0), 2)), + ] + .into_iter() + .collect() + ); + + // Redelegating with a lower lock does not forget previous lock and updates correctly. + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 1)); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 2)); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 3 + )); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 1, + 3, + Conviction::Locked1x, + 3 + )); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 2, + 4, + Conviction::Locked1x, + 3 + )); + assert_eq!( + Polls::get(), + vec![ + (0, Ongoing(Tally::from_parts(4, 2, 13), 0)), + (1, Ongoing(Tally::from_parts(4, 2, 13), 1)), + (2, Ongoing(Tally::from_parts(4, 2, 13), 2)), + (3, Ongoing(Tally::from_parts(0, 4, 0), 2)), + ] + .into_iter() + .collect() + ); + assert_eq!(Balances::usable_balance(1), 5); + + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 2, 1)); + // unlock does nothing since the delegation already took place. + assert_eq!(Balances::usable_balance(1), 5); + + // Redelegating with higher amount extends previous lock. + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 6 + )); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 4); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 1)); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 1, + 3, + Conviction::Locked1x, + 7 + )); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 1, 1)); + assert_eq!(Balances::usable_balance(1), 3); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 2)); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 2, + 4, + Conviction::Locked1x, + 8 + )); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 2, 1)); + assert_eq!(Balances::usable_balance(1), 2); + assert_eq!( + Polls::get(), + vec![ + (0, Ongoing(Tally::from_parts(7, 2, 16), 0)), + (1, Ongoing(Tally::from_parts(8, 2, 17), 1)), + (2, Ongoing(Tally::from_parts(9, 2, 18), 2)), + (3, Ongoing(Tally::from_parts(0, 9, 0), 2)), + ] + .into_iter() + .collect() + ); + }); +} + +#[test] +fn redelegation_after_vote_ending_should_keep_lock() { + new_test_ext().execute_with(|| { + Polls::set(vec![(0, Ongoing(Tally::new(0), 0))].into_iter().collect()); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 5 + )); + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 0, aye(10, 1))); + Polls::set(vec![(0, Completed(1, true))].into_iter().collect()); + assert_eq!(Balances::usable_balance(1), 5); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 3, + Conviction::Locked1x, + 3 + )); + assert_eq!(Balances::usable_balance(1), 5); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 5); + }); +} + +#[test] +fn lock_amalgamation_valid_with_multiple_removed_votes() { + new_test_ext().execute_with(|| { + Polls::set( + vec![ + (0, Ongoing(Tally::new(0), 0)), + (1, Ongoing(Tally::new(0), 0)), + (2, Ongoing(Tally::new(0), 0)), + ] + .into_iter() + .collect(), + ); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 0, aye(5, 1))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 1, aye(10, 1))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 2, aye(5, 2))); + assert_eq!(Balances::usable_balance(1), 0); + + Polls::set( + vec![ + (0, Completed(1, true)), + (1, Completed(1, true)), + (2, Completed(1, true)), + ] + .into_iter() + .collect(), + ); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(0), 0)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(0), 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(0), 2)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + run_to(3); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + run_to(6); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert!(Balances::usable_balance(1) <= 5); + + run_to(7); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn lock_amalgamation_valid_with_multiple_delegations() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 5 + )); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 10 + )); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked2x, + 5 + )); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + + run_to(3); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + run_to(6); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert!(Balances::usable_balance(1) <= 5); + + run_to(7); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn lock_amalgamation_valid_with_move_roundtrip_to_delegation() { + new_test_ext().execute_with(|| { + Polls::set(vec![(0, Ongoing(Tally::new(0), 0))].into_iter().collect()); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 0, aye(5, 1))); + Polls::set(vec![(0, Completed(1, true))].into_iter().collect()); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(0), 0)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 5); + + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 10 + )); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + Polls::set(vec![(1, Ongoing(Tally::new(0), 0))].into_iter().collect()); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 1, aye(5, 2))); + Polls::set(vec![(1, Completed(1, true))].into_iter().collect()); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(0), 1)); + + run_to(3); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + run_to(6); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert!(Balances::usable_balance(1) <= 5); + + run_to(7); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn lock_amalgamation_valid_with_move_roundtrip_to_casting() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 5 + )); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 5); + + Polls::set(vec![(0, Ongoing(Tally::new(0), 0))].into_iter().collect()); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 0, aye(10, 1))); + Polls::set(vec![(0, Completed(1, true))].into_iter().collect()); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(0), 0)); + + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked2x, + 10 + )); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + + run_to(3); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + run_to(6); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert!(Balances::usable_balance(1) <= 5); + + run_to(7); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn lock_aggregation_over_different_classes_with_delegation_works() { + new_test_ext().execute_with(|| { + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 0, + 2, + Conviction::Locked1x, + 5 + )); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 1, + 2, + Conviction::Locked2x, + 5 + )); + assert_ok!(Voting::delegate( + RuntimeOrigin::signed(1), + 2, + 2, + Conviction::Locked1x, + 10 + )); + + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 1)); + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 2)); + + run_to(3); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 2, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + run_to(6); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 2, 1)); + assert_eq!(Balances::usable_balance(1), 5); + + run_to(7); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 2, 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn lock_aggregation_over_different_classes_with_casting_works() { + new_test_ext().execute_with(|| { + Polls::set( + vec![ + (0, Ongoing(Tally::new(0), 0)), + (1, Ongoing(Tally::new(0), 1)), + (2, Ongoing(Tally::new(0), 2)), + ] + .into_iter() + .collect(), + ); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 0, aye(5, 1))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 1, aye(10, 1))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 2, aye(5, 2))); + Polls::set( + vec![ + (0, Completed(1, true)), + (1, Completed(1, true)), + (2, Completed(1, true)), + ] + .into_iter() + .collect(), + ); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(0), 0)); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(1), 1)); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(2), 2)); + + run_to(3); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 2, 1)); + assert_eq!(Balances::usable_balance(1), 0); + + run_to(6); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 2, 1)); + assert_eq!(Balances::usable_balance(1), 5); + + run_to(7); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 0, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 1, 1)); + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), 2, 1)); + assert_eq!(Balances::usable_balance(1), 10); + }); +} + +#[test] +fn errors_with_vote_work() { + new_test_ext().execute_with(|| { + assert_noop!( + Voting::vote(RuntimeOrigin::signed(1), 0, aye(10, 0)), + Error::::NotOngoing + ); + assert_noop!( + Voting::vote(RuntimeOrigin::signed(1), 1, aye(10, 0)), + Error::::NotOngoing + ); + assert_noop!( + Voting::vote(RuntimeOrigin::signed(1), 2, aye(10, 0)), + Error::::NotOngoing + ); + assert_noop!( + Voting::vote(RuntimeOrigin::signed(1), 3, aye(11, 0)), + Error::::InsufficientFunds + ); + + assert_ok!(Voting::delegate(RuntimeOrigin::signed(1), 0, 2, Conviction::None, 10)); + assert_noop!( + Voting::vote(RuntimeOrigin::signed(1), 3, aye(10, 0)), + Error::::AlreadyDelegating + ); + + assert_ok!(Voting::undelegate(RuntimeOrigin::signed(1), 0)); + Polls::set( + vec![ + (0, Ongoing(Tally::new(0), 0)), + (1, Ongoing(Tally::new(0), 0)), + (2, Ongoing(Tally::new(0), 0)), + (3, Ongoing(Tally::new(0), 0)), + ] + .into_iter() + .collect(), + ); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 0, aye(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 1, aye(10, 0))); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 2, aye(10, 0))); + assert_noop!( + Voting::vote(RuntimeOrigin::signed(1), 3, aye(10, 0)), + Error::::MaxVotesReached + ); + }); +} + +#[test] +fn errors_with_delegating_work() { + new_test_ext().execute_with(|| { + assert_noop!( + Voting::delegate(RuntimeOrigin::signed(1), 0, 2, Conviction::None, 11), + Error::::InsufficientFunds + ); + assert_noop!( + Voting::delegate(RuntimeOrigin::signed(1), 3, 2, Conviction::None, 10), + Error::::BadClass + ); + + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(10, 0))); + assert_noop!( + Voting::delegate(RuntimeOrigin::signed(1), 0, 2, Conviction::None, 10), + Error::::AlreadyVoting + ); + + assert_noop!( + Voting::undelegate(RuntimeOrigin::signed(1), 0), + Error::::NotDelegating + ); + }); +} + +#[test] +fn remove_other_vote_works() { + new_test_ext().execute_with(|| { + assert_noop!( + Voting::remove_other_vote(RuntimeOrigin::signed(2), 1, 0, 3), + Error::::NotVoter + ); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(10, 2))); + assert_noop!( + Voting::remove_other_vote(RuntimeOrigin::signed(2), 1, 0, 3), + Error::::NoPermission + ); + Polls::set(vec![(3, Completed(1, true))].into_iter().collect()); + run_to(6); + assert_noop!( + Voting::remove_other_vote(RuntimeOrigin::signed(2), 1, 0, 3), + Error::::NoPermissionYet + ); + run_to(7); + assert_ok!(Voting::remove_other_vote(RuntimeOrigin::signed(2), 1, 0, 3)); + }); +} + +#[test] +fn errors_with_remove_vote_work() { + new_test_ext().execute_with(|| { + assert_noop!( + Voting::remove_vote(RuntimeOrigin::signed(1), Some(0), 3), + Error::::NotVoter + ); + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(10, 2))); + Polls::set(vec![(3, Completed(1, true))].into_iter().collect()); + assert_noop!( + Voting::remove_vote(RuntimeOrigin::signed(1), None, 3), + Error::::ClassNeeded + ); + }); +} +thread_local! { + static LAST_ON_VOTE_DATA: RefCell)>> = RefCell::new(None); + static LAST_ON_REMOVE_VOTE_DATA: RefCell)>> = RefCell::new(None); + static LAST_LOCKED_IF_UNSUCCESSFUL_VOTE_DATA: RefCell> = RefCell::new(None); + static REMOVE_VOTE_LOCKED_AMOUNT: RefCell> = RefCell::new(None); +} + +pub struct HooksHandler; + +impl HooksHandler { + fn last_on_vote_data() -> Option<(u64, u8, AccountVote)> { + LAST_ON_VOTE_DATA.with(|data| *data.borrow()) + } + + fn last_on_remove_vote_data() -> Option<(u64, u8, Option)> { + LAST_ON_REMOVE_VOTE_DATA.with(|data| *data.borrow()) + } + + fn last_locked_if_unsuccessful_vote_data() -> Option<(u64, u8)> { + LAST_LOCKED_IF_UNSUCCESSFUL_VOTE_DATA.with(|data| *data.borrow()) + } + + fn reset() { + LAST_ON_VOTE_DATA.with(|data| *data.borrow_mut() = None); + LAST_ON_REMOVE_VOTE_DATA.with(|data| *data.borrow_mut() = None); + LAST_LOCKED_IF_UNSUCCESSFUL_VOTE_DATA.with(|data| *data.borrow_mut() = None); + REMOVE_VOTE_LOCKED_AMOUNT.with(|data| *data.borrow_mut() = None); + } + + fn with_remove_locked_amount(v: u64) { + REMOVE_VOTE_LOCKED_AMOUNT.with(|data| *data.borrow_mut() = Some(v)); + } +} + +impl VotingHooks for HooksHandler { + fn on_vote(who: &u64, ref_index: u8, vote: AccountVote) -> DispatchResult { + LAST_ON_VOTE_DATA.with(|data| { + *data.borrow_mut() = Some((*who, ref_index, vote)); + }); + Ok(()) + } + + fn on_remove_vote(who: &u64, ref_index: u8, ongoing: Option) { + LAST_ON_REMOVE_VOTE_DATA.with(|data| { + *data.borrow_mut() = Some((*who, ref_index, ongoing)); + }); + } + + fn locked_if_unsuccessful_vote(who: &u64, ref_index: u8) -> Option { + LAST_LOCKED_IF_UNSUCCESSFUL_VOTE_DATA.with(|data| { + *data.borrow_mut() = Some((*who, ref_index)); + + REMOVE_VOTE_LOCKED_AMOUNT.with(|data| *data.borrow()) + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn on_vote_worst_case(_who: &u64) {} + + #[cfg(feature = "runtime-benchmarks")] + fn on_remove_vote_worst_case(_who: &u64) {} +} + +#[test] +fn voting_hooks_are_called_correctly() { + new_test_ext().execute_with(|| { + let c = class(3); + + let usable_balance_1 = Balances::usable_balance(1); + dbg!(usable_balance_1); + + // Voting + assert_ok!(Voting::vote(RuntimeOrigin::signed(1), 3, aye(1, 1))); + assert_eq!( + HooksHandler::last_on_vote_data(), + Some(( + 1, + 3, + AccountVote::Standard { + vote: Vote { + aye: true, + conviction: Conviction::Locked1x + }, + balance: 1 + } + )) + ); + assert_ok!(Voting::vote(RuntimeOrigin::signed(2), 3, nay(20, 2))); + assert_eq!( + HooksHandler::last_on_vote_data(), + Some(( + 2, + 3, + AccountVote::Standard { + vote: Vote { + aye: false, + conviction: Conviction::Locked2x + }, + balance: 20 + } + )) + ); + HooksHandler::reset(); + + // removing vote while ongoing + assert_ok!(Voting::vote(RuntimeOrigin::signed(3), 3, nay(20, 0))); + assert_eq!( + HooksHandler::last_on_vote_data(), + Some(( + 3, + 3, + AccountVote::Standard { + vote: Vote { + aye: false, + conviction: Conviction::None + }, + balance: 20 + } + )) + ); + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(3), Some(c), 3)); + assert_eq!(HooksHandler::last_on_remove_vote_data(), Some((3, 3, Some(true)))); + HooksHandler::reset(); + + Polls::set(vec![(3, Completed(3, false))].into_iter().collect()); + + // removing successful vote while completed + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(2), Some(c), 3)); + assert_eq!(HooksHandler::last_on_remove_vote_data(), Some((2, 3, Some(false)))); + assert_eq!(HooksHandler::last_locked_if_unsuccessful_vote_data(), None); + + HooksHandler::reset(); + + HooksHandler::with_remove_locked_amount(5); + + // removing unsuccessful vote when completed + assert_ok!(Voting::remove_vote(RuntimeOrigin::signed(1), Some(c), 3)); + assert_eq!(HooksHandler::last_on_remove_vote_data(), Some((1, 3, Some(false)))); + assert_eq!(HooksHandler::last_locked_if_unsuccessful_vote_data(), Some((1, 3))); + + // Removing unsuccessful vote when completed should lock if given amount from the hook + assert_ok!(Voting::unlock(RuntimeOrigin::signed(1), c, 1)); + assert_eq!(Balances::usable_balance(1), 5); + }); +} diff --git a/pallets/conviction-voting/src/traits.rs b/pallets/conviction-voting/src/traits.rs new file mode 100644 index 000000000..77bf54c98 --- /dev/null +++ b/pallets/conviction-voting/src/traits.rs @@ -0,0 +1,39 @@ +use crate::AccountVote; +use frame_support::dispatch::DispatchResult; + +pub trait VotingHooks { + // Called when vote is executed. + fn on_vote(who: &AccountId, ref_index: Index, vote: AccountVote) -> DispatchResult; + + // Called when removed vote is executed. + // is_finished indicates the state of the referendum = None if referendum is cancelled, Some(true) if referendum is ongoing and Some(false) when finished. + fn on_remove_vote(who: &AccountId, ref_index: Index, ongoing: Option); + + // Called when removed vote is executed and voter lost the direction to possibly lock some balance. + // Can Return the amount that should be locked for the conviction time. + fn locked_if_unsuccessful_vote(who: &AccountId, ref_index: Index) -> Option; + + #[cfg(feature = "runtime-benchmarks")] + fn on_vote_worst_case(_who: &AccountId); + + #[cfg(feature = "runtime-benchmarks")] + fn on_remove_vote_worst_case(_who: &AccountId); +} + +impl VotingHooks for () { + fn on_vote(_who: &A, _ref_index: I, _vote: AccountVote) -> DispatchResult { + Ok(()) + } + + fn on_remove_vote(_who: &A, _ref_index: I, _ongoing: Option) {} + + fn locked_if_unsuccessful_vote(_who: &A, _ref_index: I) -> Option { + None + } + + #[cfg(feature = "runtime-benchmarks")] + fn on_vote_worst_case(_who: &A) {} + + #[cfg(feature = "runtime-benchmarks")] + fn on_remove_vote_worst_case(_who: &A) {} +} diff --git a/pallets/conviction-voting/src/types.rs b/pallets/conviction-voting/src/types.rs new file mode 100644 index 000000000..f1e1f1143 --- /dev/null +++ b/pallets/conviction-voting/src/types.rs @@ -0,0 +1,268 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Miscellaneous additional datatypes. + +use codec::{Codec, Decode, Encode, MaxEncodedLen}; +use frame_support::{traits::VoteTally, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Saturating, Zero}, + RuntimeDebug, +}; +use sp_std::{fmt::Debug, marker::PhantomData}; + +use super::*; +use crate::{AccountVote, Conviction, Vote}; + +/// Info regarding an ongoing referendum. +#[derive(CloneNoBound, PartialEqNoBound, EqNoBound, RuntimeDebugNoBound, TypeInfo, Encode, Decode, MaxEncodedLen)] +#[scale_info(skip_type_params(Total))] +#[codec(mel_bound(Votes: MaxEncodedLen))] +pub struct Tally { + /// The number of aye votes, expressed in terms of post-conviction lock-vote. + pub ayes: Votes, + /// The number of nay votes, expressed in terms of post-conviction lock-vote. + pub nays: Votes, + /// The basic number of aye votes, expressed pre-conviction. + pub support: Votes, + /// Dummy. + dummy: PhantomData, +} + +impl< + Votes: Clone + Default + PartialEq + Eq + Debug + Copy + AtLeast32BitUnsigned + TypeInfo + Codec, + Total: Get, + Class, + > VoteTally for Tally +{ + fn new(_: Class) -> Self { + Self { + ayes: Zero::zero(), + nays: Zero::zero(), + support: Zero::zero(), + dummy: PhantomData, + } + } + + fn ayes(&self, _: Class) -> Votes { + self.ayes + } + + fn support(&self, _: Class) -> Perbill { + Perbill::from_rational(self.support, Total::get()) + } + + fn approval(&self, _: Class) -> Perbill { + Perbill::from_rational(self.ayes, self.ayes.saturating_add(self.nays)) + } + + #[cfg(feature = "runtime-benchmarks")] + fn unanimity(_: Class) -> Self { + Self { + ayes: Total::get(), + nays: Zero::zero(), + support: Total::get(), + dummy: PhantomData, + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn rejection(_: Class) -> Self { + Self { + ayes: Zero::zero(), + nays: Total::get(), + support: Total::get(), + dummy: PhantomData, + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn from_requirements(support: Perbill, approval: Perbill, _: Class) -> Self { + let support = support.mul_ceil(Total::get()); + let ayes = approval.mul_ceil(support); + Self { + ayes, + nays: support - ayes, + support, + dummy: PhantomData, + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn setup(_: Class, _: Perbill) {} +} + +impl< + Votes: Clone + Default + PartialEq + Eq + Debug + Copy + AtLeast32BitUnsigned + TypeInfo + Codec, + Total: Get, + > Tally +{ + /// Create a new tally. + pub fn from_vote(vote: Vote, balance: Votes) -> Self { + let Delegations { votes, capital } = vote.conviction.votes(balance); + Self { + ayes: if vote.aye { votes } else { Zero::zero() }, + nays: if vote.aye { Zero::zero() } else { votes }, + support: capital, + dummy: PhantomData, + } + } + + pub fn from_parts(ayes_with_conviction: Votes, nays_with_conviction: Votes, ayes: Votes) -> Self { + Self { + ayes: ayes_with_conviction, + nays: nays_with_conviction, + support: ayes, + dummy: PhantomData, + } + } + + /// Add an account's vote into the tally. + pub fn add(&mut self, vote: AccountVote) -> Option<()> { + match vote { + AccountVote::Standard { vote, balance } => { + let Delegations { votes, capital } = vote.conviction.votes(balance); + match vote.aye { + true => { + self.support = self.support.checked_add(&capital)?; + self.ayes = self.ayes.checked_add(&votes)? + } + false => self.nays = self.nays.checked_add(&votes)?, + } + } + AccountVote::Split { aye, nay } => { + let aye = Conviction::None.votes(aye); + let nay = Conviction::None.votes(nay); + self.support = self.support.checked_add(&aye.capital)?; + self.ayes = self.ayes.checked_add(&aye.votes)?; + self.nays = self.nays.checked_add(&nay.votes)?; + } + AccountVote::SplitAbstain { aye, nay, abstain } => { + let aye = Conviction::None.votes(aye); + let nay = Conviction::None.votes(nay); + let abstain = Conviction::None.votes(abstain); + self.support = self.support.checked_add(&aye.capital)?.checked_add(&abstain.capital)?; + self.ayes = self.ayes.checked_add(&aye.votes)?; + self.nays = self.nays.checked_add(&nay.votes)?; + } + } + Some(()) + } + + /// Remove an account's vote from the tally. + pub fn remove(&mut self, vote: AccountVote) -> Option<()> { + match vote { + AccountVote::Standard { vote, balance } => { + let Delegations { votes, capital } = vote.conviction.votes(balance); + match vote.aye { + true => { + self.support = self.support.checked_sub(&capital)?; + self.ayes = self.ayes.checked_sub(&votes)? + } + false => self.nays = self.nays.checked_sub(&votes)?, + } + } + AccountVote::Split { aye, nay } => { + let aye = Conviction::None.votes(aye); + let nay = Conviction::None.votes(nay); + self.support = self.support.checked_sub(&aye.capital)?; + self.ayes = self.ayes.checked_sub(&aye.votes)?; + self.nays = self.nays.checked_sub(&nay.votes)?; + } + AccountVote::SplitAbstain { aye, nay, abstain } => { + let aye = Conviction::None.votes(aye); + let nay = Conviction::None.votes(nay); + let abstain = Conviction::None.votes(abstain); + self.support = self.support.checked_sub(&aye.capital)?.checked_sub(&abstain.capital)?; + self.ayes = self.ayes.checked_sub(&aye.votes)?; + self.nays = self.nays.checked_sub(&nay.votes)?; + } + } + Some(()) + } + + /// Increment some amount of votes. + pub fn increase(&mut self, approve: bool, delegations: Delegations) { + match approve { + true => { + self.support = self.support.saturating_add(delegations.capital); + self.ayes = self.ayes.saturating_add(delegations.votes); + } + false => self.nays = self.nays.saturating_add(delegations.votes), + } + } + + /// Decrement some amount of votes. + pub fn reduce(&mut self, approve: bool, delegations: Delegations) { + match approve { + true => { + self.support = self.support.saturating_sub(delegations.capital); + self.ayes = self.ayes.saturating_sub(delegations.votes); + } + false => self.nays = self.nays.saturating_sub(delegations.votes), + } + } +} + +/// Amount of votes and capital placed in delegation for an account. +#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct Delegations { + /// The number of votes (this is post-conviction). + pub votes: Balance, + /// The amount of raw capital, used for the support. + pub capital: Balance, +} + +impl Saturating for Delegations { + fn saturating_add(self, o: Self) -> Self { + Self { + votes: self.votes.saturating_add(o.votes), + capital: self.capital.saturating_add(o.capital), + } + } + + fn saturating_sub(self, o: Self) -> Self { + Self { + votes: self.votes.saturating_sub(o.votes), + capital: self.capital.saturating_sub(o.capital), + } + } + + fn saturating_mul(self, o: Self) -> Self { + Self { + votes: self.votes.saturating_mul(o.votes), + capital: self.capital.saturating_mul(o.capital), + } + } + + fn saturating_pow(self, exp: usize) -> Self { + Self { + votes: self.votes.saturating_pow(exp), + capital: self.capital.saturating_pow(exp), + } + } +} + +/// Whether an `unvote` operation is able to make actions that are not strictly always in the +/// interest of an account. +pub enum UnvoteScope { + /// Permitted to do everything. + Any, + /// Permitted to do only the changes that do not need the owner's permission. + OnlyExpired, +} diff --git a/pallets/conviction-voting/src/vote.rs b/pallets/conviction-voting/src/vote.rs new file mode 100644 index 000000000..6fa279966 --- /dev/null +++ b/pallets/conviction-voting/src/vote.rs @@ -0,0 +1,262 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The vote datatype. + +use crate::{Conviction, Delegations}; +use codec::{Decode, Encode, EncodeLike, Input, MaxEncodedLen, Output}; +use frame_support::{pallet_prelude::Get, BoundedVec}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Saturating, Zero}, + RuntimeDebug, +}; +use sp_std::prelude::*; + +/// A number of lock periods, plus a vote, one way or the other. +#[derive(Copy, Clone, Eq, PartialEq, Default, RuntimeDebug, MaxEncodedLen)] +pub struct Vote { + pub aye: bool, + pub conviction: Conviction, +} + +impl Encode for Vote { + fn encode_to(&self, output: &mut T) { + output.push_byte(u8::from(self.conviction) | if self.aye { 0b1000_0000 } else { 0 }); + } +} + +impl EncodeLike for Vote {} + +impl Decode for Vote { + fn decode(input: &mut I) -> Result { + let b = input.read_byte()?; + Ok(Vote { + aye: (b & 0b1000_0000) == 0b1000_0000, + conviction: Conviction::try_from(b & 0b0111_1111).map_err(|_| codec::Error::from("Invalid conviction"))?, + }) + } +} + +impl TypeInfo for Vote { + type Identity = Self; + + fn type_info() -> scale_info::Type { + scale_info::Type::builder() + .path(scale_info::Path::new("Vote", module_path!())) + .composite( + scale_info::build::Fields::unnamed() + .field(|f| f.ty::().docs(&["Raw vote byte, encodes aye + conviction"])), + ) + } +} + +/// A vote for a referendum of a particular account. +#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub enum AccountVote { + /// A standard vote, one-way (approve or reject) with a given amount of conviction. + Standard { vote: Vote, balance: Balance }, + /// A split vote with balances given for both ways, and with no conviction, useful for + /// parachains when voting. + Split { aye: Balance, nay: Balance }, + /// A split vote with balances given for both ways as well as abstentions, and with no + /// conviction, useful for parachains when voting, other off-chain aggregate accounts and + /// individuals who wish to abstain. + SplitAbstain { + aye: Balance, + nay: Balance, + abstain: Balance, + }, +} + +impl AccountVote { + /// Returns `Some` of the lock periods that the account is locked for, assuming that the + /// referendum passed iff `approved` is `true`. + pub fn locked_if(self, approved: bool) -> Option<(u32, Balance)> { + // winning side: can only be removed after the lock period ends. + match self { + AccountVote::Standard { + vote: Vote { + conviction: Conviction::None, + .. + }, + .. + } => None, + AccountVote::Standard { vote, balance } if vote.aye == approved => { + Some((vote.conviction.lock_periods(), balance)) + } + _ => None, + } + } + + /// The total balance involved in this vote. + pub fn balance(self) -> Balance { + match self { + AccountVote::Standard { balance, .. } => balance, + AccountVote::Split { aye, nay } => aye.saturating_add(nay), + AccountVote::SplitAbstain { aye, nay, abstain } => aye.saturating_add(nay).saturating_add(abstain), + } + } + + /// Returns `Some` with whether the vote is an aye vote if it is standard, otherwise `None` if + /// it is split. + pub fn as_standard(self) -> Option { + match self { + AccountVote::Standard { vote, .. } => Some(vote.aye), + _ => None, + } + } +} + +/// A "prior" lock, i.e. a lock for some now-forgotten reason. +#[derive( + Encode, Decode, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen, +)] +pub struct PriorLock(BlockNumber, Balance); + +impl PriorLock { + /// Accumulates an additional lock. + pub fn accumulate(&mut self, until: BlockNumber, amount: Balance) { + self.0 = self.0.max(until); + self.1 = self.1.max(amount); + } + + pub fn locked(&self) -> Balance { + self.1 + } + + pub fn rejig(&mut self, now: BlockNumber) { + if now >= self.0 { + self.0 = Zero::zero(); + self.1 = Zero::zero(); + } + } +} + +/// Information concerning the delegation of some voting power. +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct Delegating { + /// The amount of balance delegated. + pub balance: Balance, + /// The account to which the voting power is delegated. + pub target: AccountId, + /// The conviction with which the voting power is delegated. When this gets undelegated, the + /// relevant lock begins. + pub conviction: Conviction, + /// The total amount of delegations that this account has received, post-conviction-weighting. + pub delegations: Delegations, + /// Any pre-existing locks from past voting/delegating activity. + pub prior: PriorLock, +} + +/// Information concerning the direct vote-casting of some voting power. +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(MaxVotes))] +#[codec(mel_bound(Balance: MaxEncodedLen, BlockNumber: MaxEncodedLen, PollIndex: MaxEncodedLen))] +pub struct Casting +where + MaxVotes: Get, +{ + /// The current votes of the account. + pub votes: BoundedVec<(PollIndex, AccountVote), MaxVotes>, + /// The total amount of delegations that this account has received, post-conviction-weighting. + pub delegations: Delegations, + /// Any pre-existing locks from past voting/delegating activity. + pub prior: PriorLock, +} + +/// An indicator for what an account is doing; it can either be delegating or voting. +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[scale_info(skip_type_params(MaxVotes))] +#[codec(mel_bound( + Balance: MaxEncodedLen, AccountId: MaxEncodedLen, BlockNumber: MaxEncodedLen, + PollIndex: MaxEncodedLen, +))] +pub enum Voting +where + MaxVotes: Get, +{ + /// The account is voting directly. + Casting(Casting), + /// The account is delegating `balance` of its balance to a `target` account with `conviction`. + Delegating(Delegating), +} + +impl Default + for Voting +where + MaxVotes: Get, +{ + fn default() -> Self { + Voting::Casting(Casting { + votes: Default::default(), + delegations: Default::default(), + prior: PriorLock(Zero::zero(), Default::default()), + }) + } +} + +impl AsMut> + for Voting +where + MaxVotes: Get, +{ + fn as_mut(&mut self) -> &mut PriorLock { + match self { + Voting::Casting(Casting { prior, .. }) => prior, + Voting::Delegating(Delegating { prior, .. }) => prior, + } + } +} + +impl + Voting +where + MaxVotes: Get, +{ + pub fn rejig(&mut self, now: BlockNumber) { + AsMut::>::as_mut(self).rejig(now); + } + + /// The amount of this account's balance that must currently be locked due to voting. + pub fn locked_balance(&self) -> Balance { + match self { + Voting::Casting(Casting { votes, prior, .. }) => votes + .iter() + .map(|i| i.1.balance()) + .fold(prior.locked(), |a, i| a.max(i)), + Voting::Delegating(Delegating { balance, prior, .. }) => *balance.max(&prior.locked()), + } + } + + pub fn set_common(&mut self, delegations: Delegations, prior: PriorLock) { + let (d, p) = match self { + Voting::Casting(Casting { + ref mut delegations, + ref mut prior, + .. + }) => (delegations, prior), + Voting::Delegating(Delegating { + ref mut delegations, + ref mut prior, + .. + }) => (delegations, prior), + }; + *d = delegations; + *p = prior; + } +} diff --git a/pallets/conviction-voting/src/weights.rs b/pallets/conviction-voting/src/weights.rs new file mode 100644 index 000000000..225f5c2ca --- /dev/null +++ b/pallets/conviction-voting/src/weights.rs @@ -0,0 +1,341 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_conviction_voting +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/production/substrate +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_conviction_voting +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/conviction-voting/src/weights.rs +// --header=./HEADER-APACHE2 +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_conviction_voting. +pub trait WeightInfo { + fn vote_new() -> Weight; + fn vote_existing() -> Weight; + fn remove_vote() -> Weight; + fn remove_other_vote() -> Weight; + fn delegate(r: u32, ) -> Weight; + fn undelegate(r: u32, ) -> Weight; + fn unlock() -> Weight; +} + +/// Weights for pallet_conviction_voting using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + fn vote_new() -> Weight { + // Proof Size summary in bytes: + // Measured: `13074` + // Estimated: `219984` + // Minimum execution time: 112_936_000 picoseconds. + Weight::from_parts(116_972_000, 219984) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + fn vote_existing() -> Weight { + // Proof Size summary in bytes: + // Measured: `20216` + // Estimated: `219984` + // Minimum execution time: 291_971_000 picoseconds. + Weight::from_parts(301_738_000, 219984) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + fn remove_vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `19968` + // Estimated: `219984` + // Minimum execution time: 262_582_000 picoseconds. + Weight::from_parts(270_955_000, 219984) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + fn remove_other_vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `12675` + // Estimated: `30706` + // Minimum execution time: 52_909_000 picoseconds. + Weight::from_parts(56_365_000, 30706) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: ConvictionVoting VotingFor (r:2 w:2) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// The range of component `r` is `[0, 1]`. + fn delegate(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `240 + r * (1627 ±0)` + // Estimated: `109992 + r * (109992 ±0)` + // Minimum execution time: 54_640_000 picoseconds. + Weight::from_parts(57_185_281, 109992) + // Standard Error: 193_362 + .saturating_add(Weight::from_parts(44_897_418, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) + } + /// Storage: ConvictionVoting VotingFor (r:2 w:2) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// The range of component `r` is `[0, 1]`. + fn undelegate(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `406 + r * (1376 ±0)` + // Estimated: `109992 + r * (109992 ±0)` + // Minimum execution time: 26_514_000 picoseconds. + Weight::from_parts(28_083_732, 109992) + // Standard Error: 104_905 + .saturating_add(Weight::from_parts(40_722_467, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) + } + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + fn unlock() -> Weight { + // Proof Size summary in bytes: + // Measured: `11734` + // Estimated: `30706` + // Minimum execution time: 71_140_000 picoseconds. + Weight::from_parts(77_388_000, 30706) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + fn vote_new() -> Weight { + // Proof Size summary in bytes: + // Measured: `13074` + // Estimated: `219984` + // Minimum execution time: 112_936_000 picoseconds. + Weight::from_parts(116_972_000, 219984) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + fn vote_existing() -> Weight { + // Proof Size summary in bytes: + // Measured: `20216` + // Estimated: `219984` + // Minimum execution time: 291_971_000 picoseconds. + Weight::from_parts(301_738_000, 219984) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + fn remove_vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `19968` + // Estimated: `219984` + // Minimum execution time: 262_582_000 picoseconds. + Weight::from_parts(270_955_000, 219984) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:0) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + fn remove_other_vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `12675` + // Estimated: `30706` + // Minimum execution time: 52_909_000 picoseconds. + Weight::from_parts(56_365_000, 30706) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: ConvictionVoting VotingFor (r:2 w:2) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// The range of component `r` is `[0, 1]`. + fn delegate(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `240 + r * (1627 ±0)` + // Estimated: `109992 + r * (109992 ±0)` + // Minimum execution time: 54_640_000 picoseconds. + Weight::from_parts(57_185_281, 109992) + // Standard Error: 193_362 + .saturating_add(Weight::from_parts(44_897_418, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) + } + /// Storage: ConvictionVoting VotingFor (r:2 w:2) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: Referenda ReferendumInfoFor (r:1 w:1) + /// Proof: Referenda ReferendumInfoFor (max_values: None, max_size: Some(366), added: 2841, mode: MaxEncodedLen) + /// Storage: Scheduler Agenda (r:2 w:2) + /// Proof: Scheduler Agenda (max_values: None, max_size: Some(107022), added: 109497, mode: MaxEncodedLen) + /// The range of component `r` is `[0, 1]`. + fn undelegate(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `406 + r * (1376 ±0)` + // Estimated: `109992 + r * (109992 ±0)` + // Minimum execution time: 26_514_000 picoseconds. + Weight::from_parts(28_083_732, 109992) + // Standard Error: 104_905 + .saturating_add(Weight::from_parts(40_722_467, 0).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(r.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 109992).saturating_mul(r.into())) + } + /// Storage: ConvictionVoting VotingFor (r:1 w:1) + /// Proof: ConvictionVoting VotingFor (max_values: None, max_size: Some(27241), added: 29716, mode: MaxEncodedLen) + /// Storage: ConvictionVoting ClassLocksFor (r:1 w:1) + /// Proof: ConvictionVoting ClassLocksFor (max_values: None, max_size: Some(59), added: 2534, mode: MaxEncodedLen) + /// Storage: Balances Locks (r:1 w:1) + /// Proof: Balances Locks (max_values: None, max_size: Some(1299), added: 3774, mode: MaxEncodedLen) + /// Storage: Balances Freezes (r:1 w:0) + /// Proof: Balances Freezes (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + fn unlock() -> Weight { + // Proof Size summary in bytes: + // Measured: `11734` + // Estimated: `30706` + // Minimum execution time: 71_140_000 picoseconds. + Weight::from_parts(77_388_000, 30706) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } +} diff --git a/pallets/dca/Cargo.toml b/pallets/dca/Cargo.toml index 03d8fe8ac..eef6231b7 100644 --- a/pallets/dca/Cargo.toml +++ b/pallets/dca/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-dca' -version = "1.6.1" +version = "1.6.2" description = 'A pallet to manage DCA scheduling' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/dca/src/lib.rs b/pallets/dca/src/lib.rs index b230239cd..e8d19973c 100644 --- a/pallets/dca/src/lib.rs +++ b/pallets/dca/src/lib.rs @@ -57,7 +57,7 @@ //! //! ## Terminating a Schedule //! -//! Both users and technical origin can terminate a DCA schedule. However, users can only terminate schedules that they own. +//! Both users and TerminateOrigin can terminate a DCA schedule. However, users can only terminate schedules that they own. //! //! Once a schedule is terminated, it is completely and permanently removed from the blockchain. @@ -213,7 +213,7 @@ pub mod pallet { type AssetId: Parameter + Member + Copy + MaybeSerializeDeserialize + MaxEncodedLen; /// Origin able to terminate schedules - type TechnicalOrigin: EnsureOrigin; + type TerminateOrigin: EnsureOrigin; ///For named-reserving user's assets type Currencies: NamedMultiReservableCurrency< @@ -559,7 +559,7 @@ pub mod pallet { /// Terminates a DCA schedule and remove it completely from the chain. /// - /// This can be called by both schedule owner or the configured `T::TechnicalOrigin` + /// This can be called by both schedule owner or the configured `T::TerminateOrigin` /// /// Parameters: /// - `origin`: schedule owner @@ -578,7 +578,7 @@ pub mod pallet { ) -> DispatchResult { let schedule = Schedules::::get(schedule_id).ok_or(Error::::ScheduleNotFound)?; - if T::TechnicalOrigin::ensure_origin(origin.clone()).is_err() { + if T::TerminateOrigin::ensure_origin(origin.clone()).is_err() { let who = ensure_signed(origin)?; ensure!(who == schedule.owner, Error::::Forbidden); } diff --git a/pallets/dca/src/tests/mock.rs b/pallets/dca/src/tests/mock.rs index 0b7f8cd1d..c712184ea 100644 --- a/pallets/dca/src/tests/mock.rs +++ b/pallets/dca/src/tests/mock.rs @@ -254,7 +254,7 @@ impl pallet_omnipool::Config for Test { type AssetRegistry = DummyRegistry; type MinimumTradingLimit = MinTradeAmount; type MinimumPoolLiquidity = MinAddedLiquidity; - type TechnicalOrigin = EnsureRoot; + type UpdateTradabilityOrigin = EnsureRoot; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; type CollectionId = u32; @@ -367,7 +367,7 @@ impl pallet_route_executor::Config for Test { type InspectRegistry = DummyRegistry; type DefaultRoutePoolType = DefaultRoutePoolType; type WeightInfo = (); - type TechnicalOrigin = EnsureRoot; + type ForceInsertOrigin = EnsureRoot; type EdToRefundCalculator = MockedEdCalculator; type OraclePriceProvider = PriceProviderMock; type OraclePeriod = RouteValidationOraclePeriod; @@ -691,7 +691,7 @@ impl Config for Test { type BumpChance = BumpChance; type NamedReserveId = NamedReserveId; type MaxNumberOfRetriesOnError = MaxNumberOfRetriesOnError; - type TechnicalOrigin = EnsureRoot; + type TerminateOrigin = EnsureRoot; type RelayChainBlockHashProvider = ParentHashGetterMock; type AmmTradeWeights = (); type MinimumTradingLimit = MinTradeAmount; diff --git a/pallets/omnipool-liquidity-mining/Cargo.toml b/pallets/omnipool-liquidity-mining/Cargo.toml index 91ac3bd29..a8c753cdf 100644 --- a/pallets/omnipool-liquidity-mining/Cargo.toml +++ b/pallets/omnipool-liquidity-mining/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool-liquidity-mining" -version = "2.2.1" +version = "2.2.2" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/omnipool-liquidity-mining/src/tests/mock.rs b/pallets/omnipool-liquidity-mining/src/tests/mock.rs index 8c9175f02..19eeefbe2 100644 --- a/pallets/omnipool-liquidity-mining/src/tests/mock.rs +++ b/pallets/omnipool-liquidity-mining/src/tests/mock.rs @@ -295,7 +295,7 @@ impl pallet_omnipool::Config for Test { type AssetRegistry = DummyRegistry; type MinimumTradingLimit = MinTradeAmount; type MinimumPoolLiquidity = MinAddedLiquidity; - type TechnicalOrigin = EnsureRoot; + type UpdateTradabilityOrigin = EnsureRoot; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; type CollectionId = u128; diff --git a/pallets/omnipool/Cargo.toml b/pallets/omnipool/Cargo.toml index 504ec6d9f..7aadbf39f 100644 --- a/pallets/omnipool/Cargo.toml +++ b/pallets/omnipool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool" -version = "4.3.4" +version = "4.4.0" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 245e458dc..a362d3399 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -156,11 +156,11 @@ pub mod pallet { /// Multi currency mechanism type Currency: MultiCurrency; - /// Origin that can add token, refund refused asset and withdraw protocol liquidity. + /// Origin that can add token, refund refused asset, withdraw protocol liquidity and set the weight of assets. type AuthorityOrigin: EnsureOrigin; - /// Origin that can change asset's tradability and weight. - type TechnicalOrigin: EnsureOrigin; + /// Security origin that can set asset's tradability. + type UpdateTradabilityOrigin: EnsureOrigin; /// Asset Registry mechanism - used to check if asset is correctly registered in asset registry type AssetRegistry: RegistryInspect; @@ -1432,7 +1432,7 @@ pub mod pallet { asset_id: T::AssetId, state: Tradability, ) -> DispatchResult { - T::TechnicalOrigin::ensure_origin(origin)?; + T::UpdateTradabilityOrigin::ensure_origin(origin)?; if asset_id == T::HubAssetId::get() { // Atm omnipool does not allow adding/removing liquidity of hub asset. @@ -1513,7 +1513,7 @@ pub mod pallet { #[pallet::weight(::WeightInfo::set_asset_weight_cap())] #[transactional] pub fn set_asset_weight_cap(origin: OriginFor, asset_id: T::AssetId, cap: Permill) -> DispatchResult { - T::TechnicalOrigin::ensure_origin(origin)?; + T::AuthorityOrigin::ensure_origin(origin)?; Assets::::try_mutate(asset_id, |maybe_asset| -> DispatchResult { let asset_state = maybe_asset.as_mut().ok_or(Error::::AssetNotFound)?; diff --git a/pallets/omnipool/src/tests/mock.rs b/pallets/omnipool/src/tests/mock.rs index adca0cbfb..a1ddd1a9e 100644 --- a/pallets/omnipool/src/tests/mock.rs +++ b/pallets/omnipool/src/tests/mock.rs @@ -194,7 +194,7 @@ impl Config for Test { type AssetRegistry = DummyRegistry; type MinimumTradingLimit = MinTradeAmount; type MinimumPoolLiquidity = MinAddedLiquidity; - type TechnicalOrigin = EnsureRoot; + type UpdateTradabilityOrigin = EnsureRoot; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; type CollectionId = u32; diff --git a/pallets/otc-settlements/Cargo.toml b/pallets/otc-settlements/Cargo.toml index 9c9f14002..a4e5c86a6 100644 --- a/pallets/otc-settlements/Cargo.toml +++ b/pallets/otc-settlements/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-otc-settlements' -version = '1.0.4' +version = '1.0.5' description = 'A pallet with offchain worker closing OTC arbs' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/otc-settlements/src/mock.rs b/pallets/otc-settlements/src/mock.rs index 3f327c971..11552aa41 100644 --- a/pallets/otc-settlements/src/mock.rs +++ b/pallets/otc-settlements/src/mock.rs @@ -155,7 +155,7 @@ impl pallet_route_executor::Config for Test { type OraclePriceProvider = PriceProviderMock; type OraclePeriod = RouteValidationOraclePeriod; type DefaultRoutePoolType = DefaultRoutePoolType; - type TechnicalOrigin = EnsureRoot; + type ForceInsertOrigin = EnsureRoot; type WeightInfo = (); } @@ -333,7 +333,7 @@ impl pallet_omnipool::Config for Test { type AssetRegistry = AssetRegistry; type MinimumTradingLimit = MinTradeAmount; type MinimumPoolLiquidity = MinAddedLiquidity; - type TechnicalOrigin = EnsureRoot; + type UpdateTradabilityOrigin = EnsureRoot; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; type CollectionId = u32; diff --git a/pallets/otc/Cargo.toml b/pallets/otc/Cargo.toml index 2e938e458..f6b8259b6 100644 --- a/pallets/otc/Cargo.toml +++ b/pallets/otc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-otc' -version = '2.0.2' +version = '2.0.3' description = 'A pallet for trustless over-the-counter trading' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/route-executor/Cargo.toml b/pallets/route-executor/Cargo.toml index 8ce1e6bb3..2f1d2f155 100644 --- a/pallets/route-executor/Cargo.toml +++ b/pallets/route-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-route-executor' -version = '2.6.1' +version = '2.6.2' description = 'A pallet to execute a route containing a sequence of trades' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/route-executor/src/lib.rs b/pallets/route-executor/src/lib.rs index ac64928dd..980543694 100644 --- a/pallets/route-executor/src/lib.rs +++ b/pallets/route-executor/src/lib.rs @@ -124,7 +124,7 @@ pub mod pallet { type DefaultRoutePoolType: Get>; /// Origin able to set route without validation - type TechnicalOrigin: EnsureOrigin; + type ForceInsertOrigin: EnsureOrigin; /// Weight information for the extrinsics. type WeightInfo: AmmTradeWeights>; @@ -386,7 +386,7 @@ pub mod pallet { /// Force inserts the on-chain route for a given asset pair, so there is no any validation for the route /// - /// Can only be called by technical origin + /// Can only be called by T::ForceInsertOrigin /// /// The route is stored in an ordered manner, based on the oder of the ids in the asset pair. /// @@ -406,7 +406,7 @@ pub mod pallet { mut asset_pair: AssetPair, mut new_route: Vec>, ) -> DispatchResultWithPostInfo { - T::TechnicalOrigin::ensure_origin(origin)?; + T::ForceInsertOrigin::ensure_origin(origin)?; if !asset_pair.is_ordered() { asset_pair = asset_pair.ordered_pair(); diff --git a/pallets/route-executor/src/tests/mock.rs b/pallets/route-executor/src/tests/mock.rs index 40eda43ca..6b2bff9b9 100644 --- a/pallets/route-executor/src/tests/mock.rs +++ b/pallets/route-executor/src/tests/mock.rs @@ -162,7 +162,7 @@ impl Config for Test { type OraclePriceProvider = PriceProviderMock; type OraclePeriod = RouteValidationOraclePeriod; type DefaultRoutePoolType = DefaultRoutePoolType; - type TechnicalOrigin = EnsureRoot; + type ForceInsertOrigin = EnsureRoot; type WeightInfo = (); } diff --git a/pallets/stableswap/Cargo.toml b/pallets/stableswap/Cargo.toml index 897272638..18858fd0c 100644 --- a/pallets/stableswap/Cargo.toml +++ b/pallets/stableswap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-stableswap' -version = '3.6.5' +version = '3.7.0' description = 'AMM for correlated assets' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/stableswap/src/lib.rs b/pallets/stableswap/src/lib.rs index 13cf9caeb..69d2c460e 100644 --- a/pallets/stableswap/src/lib.rs +++ b/pallets/stableswap/src/lib.rs @@ -140,6 +140,9 @@ pub mod pallet { /// The origin which can create a new pool type AuthorityOrigin: EnsureOrigin; + /// Security origin which can set the asset tradable state + type UpdateTradabilityOrigin: EnsureOrigin; + /// Account whitelist manager to exclude pool accounts from dusting mechanism. type DustAccountHandler: DustRemovalAccountWhitelist; @@ -861,7 +864,7 @@ pub mod pallet { asset_id: T::AssetId, state: Tradability, ) -> DispatchResult { - T::AuthorityOrigin::ensure_origin(origin)?; + T::UpdateTradabilityOrigin::ensure_origin(origin)?; let pool = Pools::::get(pool_id).ok_or(Error::::PoolNotFound)?; let _ = pool.find_asset(asset_id).ok_or(Error::::AssetNotInPool)?; diff --git a/pallets/stableswap/src/tests/mock.rs b/pallets/stableswap/src/tests/mock.rs index f6a447582..83b1169cb 100644 --- a/pallets/stableswap/src/tests/mock.rs +++ b/pallets/stableswap/src/tests/mock.rs @@ -179,6 +179,7 @@ impl Config for Test { type ShareAccountId = AccountIdConstructor; type AssetInspection = DummyRegistry; type AuthorityOrigin = EnsureRoot; + type UpdateTradabilityOrigin = EnsureRoot; type MinPoolLiquidity = MinimumLiquidity; type AmplificationRange = AmplificationRange; type MinTradingLimit = MinimumTradingLimit; diff --git a/pallets/staking/Cargo.toml b/pallets/staking/Cargo.toml index fe0c2ae6c..4d3034e16 100644 --- a/pallets/staking/Cargo.toml +++ b/pallets/staking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-staking" -version = "3.1.2" +version = "4.0.0" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" @@ -31,7 +31,8 @@ sp-core = { workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } frame-benchmarking = { workspace = true, optional = true } -pallet-democracy = { workspace = true } +pallet-conviction-voting= { workspace = true } +pallet-referenda = { workspace = true } [dev-dependencies] pallet-uniques = { workspace = true } @@ -55,7 +56,8 @@ std = [ "pallet-balances/std", "pallet-uniques/std", "orml-tokens/std", - "pallet-democracy/std", + "pallet-conviction-voting/std", + "pallet-referenda/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -63,6 +65,8 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", - "pallet-democracy/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + ] try-runtime = ["frame-support/try-runtime",] diff --git a/pallets/staking/src/integrations.rs b/pallets/staking/src/integrations.rs index 458491b1e..0e2cefb3b 100644 --- a/pallets/staking/src/integrations.rs +++ b/pallets/staking/src/integrations.rs @@ -1 +1 @@ -pub mod democracy; +pub mod conviction_voting; diff --git a/pallets/staking/src/integrations/democracy.rs b/pallets/staking/src/integrations/conviction_voting.rs similarity index 69% rename from pallets/staking/src/integrations/democracy.rs rename to pallets/staking/src/integrations/conviction_voting.rs index c4078e8cd..d3a2e2ce0 100644 --- a/pallets/staking/src/integrations/democracy.rs +++ b/pallets/staking/src/integrations/conviction_voting.rs @@ -1,18 +1,19 @@ use crate::pallet::{PositionVotes, Positions, ProcessedVotes}; -use crate::traits::{DemocracyReferendum, VestingDetails}; -use crate::types::{Action, Balance, Conviction, Vote}; +use crate::traits::{GetReferendumState, VestingDetails}; +use crate::types::{Action, Balance, Conviction, ReferendumIndex, Vote}; use crate::{Config, Error, Pallet}; use frame_support::defensive; use frame_support::dispatch::DispatchResult; +use frame_support::traits::{PollStatus, Polling}; use orml_traits::{MultiCurrency, MultiCurrencyExtended}; -use pallet_democracy::traits::DemocracyHooks; -use pallet_democracy::{AccountVote, ReferendumIndex, ReferendumInfo}; +use pallet_conviction_voting::traits::VotingHooks; +use pallet_conviction_voting::AccountVote; use sp_core::Get; use sp_runtime::FixedPointNumber; -pub struct StakingDemocracy(sp_std::marker::PhantomData); +pub struct StakingConvictionVoting(sp_std::marker::PhantomData); -impl DemocracyHooks for StakingDemocracy +impl VotingHooks for StakingConvictionVoting where T::Currency: MultiCurrencyExtended, { @@ -41,13 +42,13 @@ where let amount = vote.balance(); let conviction = if let AccountVote::Standard { vote, .. } = vote { match vote.conviction { - pallet_democracy::Conviction::None => Conviction::None, - pallet_democracy::Conviction::Locked1x => Conviction::Locked1x, - pallet_democracy::Conviction::Locked2x => Conviction::Locked2x, - pallet_democracy::Conviction::Locked3x => Conviction::Locked3x, - pallet_democracy::Conviction::Locked4x => Conviction::Locked4x, - pallet_democracy::Conviction::Locked5x => Conviction::Locked5x, - pallet_democracy::Conviction::Locked6x => Conviction::Locked6x, + pallet_conviction_voting::Conviction::None => Conviction::None, + pallet_conviction_voting::Conviction::Locked1x => Conviction::Locked1x, + pallet_conviction_voting::Conviction::Locked2x => Conviction::Locked2x, + pallet_conviction_voting::Conviction::Locked3x => Conviction::Locked3x, + pallet_conviction_voting::Conviction::Locked4x => Conviction::Locked4x, + pallet_conviction_voting::Conviction::Locked5x => Conviction::Locked5x, + pallet_conviction_voting::Conviction::Locked6x => Conviction::Locked6x, } } else { Conviction::default() @@ -84,7 +85,7 @@ where }) } - fn on_remove_vote(who: &T::AccountId, ref_index: ReferendumIndex, is_finished: Option) { + fn on_remove_vote(who: &T::AccountId, ref_index: ReferendumIndex, ongoing: Option) { let Some(maybe_position_id) = Pallet::::get_user_position_id(who).ok() else { return; }; @@ -113,8 +114,8 @@ where let points = Pallet::::calculate_points_for_action(Action::DemocracyVote, vote, max_position_vote); // Add points only if referendum is finished - if let Some(is_finished) = is_finished { - if is_finished { + if let Some(is_ongoing) = ongoing { + if !is_ongoing { position.action_points = position.action_points.saturating_add(points); } } @@ -127,8 +128,10 @@ where }); } - fn remove_vote_locks_if_needed(who: &T::AccountId, ref_index: ReferendumIndex) -> Option { - let position_id = Pallet::::get_user_position_id(who).ok()??; + fn locked_if_unsuccessful_vote(who: &T::AccountId, ref_index: ReferendumIndex) -> Option { + let Some(position_id) = Pallet::::get_user_position_id(who).ok()? else { + return None; + }; if let Some(vote) = ProcessedVotes::::get(who, ref_index) { return Some(vote.amount); @@ -159,22 +162,13 @@ where ) .unwrap(); Pallet::::initialize_staking(Origin::::Root.into()).unwrap(); - T::Currency::update_balance(T::NativeAssetId::get(), who, 1_000_000_000_000_000i128).unwrap(); - Pallet::::stake(Origin::::Signed(who.clone()).into(), 1_000_000_000_000_000u128).unwrap(); - let position_id = Pallet::::get_user_position_id(&who.clone()).unwrap().unwrap(); - - let mut votes = sp_std::vec::Vec::<(u32, Vote)>::new(); - for i in 0..::MaxVotes::get() { - votes.push(( - i, - Vote { - amount: 20_000_000_000_000_000, - conviction: Conviction::Locked1x, - }, - )); + if T::Currency::free_balance(T::NativeAssetId::get(), who) <= 1_000_000_000_000_000 { + T::Currency::update_balance(T::NativeAssetId::get(), who, 1_000_000_000_000_000i128).unwrap(); } + Pallet::::stake(Origin::::Signed(who.clone()).into(), 1_000_000_000_000_000u128).unwrap(); + for i in 0..::MaxLocks::get() - 5 { let id: LockIdentifier = scale_info::prelude::format!("{:a>8}", i.to_string()) .as_bytes() @@ -183,12 +177,6 @@ where T::Currency::set_lock(id, T::NativeAssetId::get(), who, 10_000_000_000_000_u128).unwrap(); } - - let voting = crate::types::Voting:: { - votes: votes.try_into().unwrap(), - }; - - crate::PositionVotes::::insert(position_id, voting); } #[cfg(feature = "runtime-benchmarks")] @@ -202,16 +190,31 @@ where ) .unwrap(); Pallet::::initialize_staking(Origin::::Root.into()).unwrap(); - T::Currency::update_balance(T::NativeAssetId::get(), who, 1_000_000_000_000_000i128).unwrap(); + if T::Currency::free_balance(T::NativeAssetId::get(), who) <= 1_000_000_000_000_000 { + T::Currency::update_balance(T::NativeAssetId::get(), who, 1_000_000_000_000_000i128).unwrap(); + } Pallet::::stake(Origin::::Signed(who.clone()).into(), 1_000_000_000_000_000u128).unwrap(); } } -pub struct ReferendumStatus(sp_std::marker::PhantomData); +pub struct ReferendumStatus(sp_std::marker::PhantomData<(P, T)>); -impl DemocracyReferendum for ReferendumStatus { - fn is_referendum_finished(index: ReferendumIndex) -> bool { - let maybe_info = pallet_democracy::Pallet::::referendum_info(index); - matches!(maybe_info, Some(ReferendumInfo::Finished { .. })) +impl> GetReferendumState for ReferendumStatus +where +

>::Index: From, +{ + fn is_referendum_finished(_index: ReferendumIndex) -> bool { + let r =

>::try_access_poll::(_index.into(), |status| { + let r = match status { + PollStatus::Completed(_, _) => true, + PollStatus::Ongoing(_, _) => false, + PollStatus::None => false, + }; + Ok(r) + }); + debug_assert!(r.is_ok(), "Failed to access poll"); + // If we failed to access poll, we assume that referendum is not finished - this should never be the case + // Note: we cant return true, because it would reward points. + r.unwrap_or(false) } } diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index 4ae7f71b3..cc9840629 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -17,7 +17,7 @@ #![recursion_limit = "256"] #![cfg_attr(not(feature = "std"), no_std)] -use crate::traits::{ActionData, DemocracyReferendum, PayablePercentage, VestingDetails}; +use crate::traits::{ActionData, GetReferendumState, PayablePercentage, VestingDetails}; use crate::types::{Action, Balance, Period, Point, Position, StakingData, Voting}; use frame_support::ensure; use frame_support::{ @@ -45,6 +45,7 @@ mod tests; mod benchmarks; pub mod integrations; +pub mod migration; pub mod traits; pub mod types; pub mod weights; @@ -59,7 +60,7 @@ pub const STAKING_LOCK_ID: LockIdentifier = *b"stk_stks"; #[frame_support::pallet] pub mod pallet { use super::*; - use crate::traits::{DemocracyReferendum, Freeze}; + use crate::traits::Freeze; use crate::types::Voting; use codec::HasCompact; use frame_support::PalletId; @@ -68,7 +69,7 @@ pub mod pallet { use sp_runtime::traits::AtLeast32BitUnsigned; /// Current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -170,7 +171,7 @@ pub mod pallet { type MaxPointsPerAction: GetByKey; /// Democracy referendum state. - type ReferendumInfo: DemocracyReferendum; + type ReferendumInfo: GetReferendumState; /// Provides information about amount of vested tokens. type Vesting: VestingDetails; @@ -213,8 +214,8 @@ pub mod pallet { Blake2_128Concat, T::AccountId, Blake2_128Concat, - pallet_democracy::ReferendumIndex, - crate::types::Vote, + types::ReferendumIndex, + types::Vote, OptionQuery, >; diff --git a/pallets/staking/src/migration.rs b/pallets/staking/src/migration.rs new file mode 100644 index 000000000..1b5f6a072 --- /dev/null +++ b/pallets/staking/src/migration.rs @@ -0,0 +1,78 @@ +use frame_support::migrations::VersionedMigration; +use frame_support::{traits::Get, weights::Weight}; +use sp_std::vec::Vec; + +use crate::*; + +pub mod versioned { + use super::*; + + pub type V1ToV2 = VersionedMigration< + 1, + 2, + v2::VersionUncheckedMigrateV1ToV2, + crate::pallet::Pallet, + ::DbWeight, + >; +} + +// This migration clear existing staking votes. +// It is necessary to clear the votes because staking supports opengov from v2.0.0. +pub mod v2 { + use super::*; + use frame_support::traits::OnRuntimeUpgrade; + #[cfg(feature = "try-runtime")] + use sp_runtime::TryRuntimeError; + + const TARGET: &str = "runtime::staking::migration::v2"; + + pub struct VersionUncheckedMigrateV1ToV2(PhantomData); + + impl OnRuntimeUpgrade for VersionUncheckedMigrateV1ToV2 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + let existing_votes = PositionVotes::::iter().count(); + let processed_votes = ProcessedVotes::::iter().count(); + log::info!( + target: TARGET, + "pre-upgrade state contains '{}' existing votes and '{} processed votes.", + existing_votes, + processed_votes + + ); + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + log::info!( + target: TARGET, + "running storage migration from version 1 to version 2." + ); + + let existing_votes = PositionVotes::::iter().count(); + let processed_votes = ProcessedVotes::::iter().count(); + + let mut weight = T::DbWeight::get().reads_writes(1, 1); + let _ = PositionVotes::::clear(existing_votes as u32, None); + let _ = ProcessedVotes::::clear(processed_votes as u32, None); + + weight.saturating_accrue(T::DbWeight::get().reads(existing_votes.saturating_add(processed_votes) as u64)); + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { + assert_eq!( + PositionVotes::::iter().count(), + 0, + "PositionVotes storage is not empty" + ); + assert_eq!( + ProcessedVotes::::iter().count(), + 0, + "ProcessedVotes storage is not empty" + ); + Ok(()) + } + } +} diff --git a/pallets/staking/src/tests/mock.rs b/pallets/staking/src/tests/mock.rs index 7e0a7ef22..cc8f2cfdc 100644 --- a/pallets/staking/src/tests/mock.rs +++ b/pallets/staking/src/tests/mock.rs @@ -26,7 +26,6 @@ use frame_support::{ }; use frame_system::{EnsureRoot, RawOrigin}; use orml_traits::{parameter_type_with_key, LockIdentifier, MultiCurrencyExtended}; -use pallet_democracy::ReferendumIndex; use sp_core::H256; use sp_runtime::{ traits::{BlakeTwo256, BlockNumberProvider, IdentityLookup}, @@ -258,8 +257,8 @@ impl GetByKey for DummyMaxPointsPerAction { pub struct DummyReferendumStatus; -impl DemocracyReferendum for DummyReferendumStatus { - fn is_referendum_finished(index: pallet_democracy::ReferendumIndex) -> bool { +impl GetReferendumState for DummyReferendumStatus { + fn is_referendum_finished(index: types::ReferendumIndex) -> bool { index % 2 == 0 } } @@ -295,7 +294,7 @@ pub struct ExtBuilder { //(who, staked maount, created_at, pendig_rewards) stakes: Vec<(AccountId, Balance, BlockNumber, Balance)>, init_staking: bool, - with_votings: Vec<(PositionId, Vec<(ReferendumIndex, Vote)>)>, + with_votings: Vec<(PositionId, Vec<(types::ReferendumIndex, Vote)>)>, } impl ExtBuilder { @@ -320,7 +319,7 @@ impl ExtBuilder { self } - pub fn with_votings(mut self, votings: Vec<(u128, Vec<(ReferendumIndex, Vote)>)>) -> Self { + pub fn with_votings(mut self, votings: Vec<(u128, Vec<(types::ReferendumIndex, Vote)>)>) -> Self { self.with_votings = votings; self } diff --git a/pallets/staking/src/tests/tests.rs b/pallets/staking/src/tests/tests.rs index 0779a916b..b389d777f 100644 --- a/pallets/staking/src/tests/tests.rs +++ b/pallets/staking/src/tests/tests.rs @@ -1,5 +1,5 @@ use crate::{ - integrations::democracy::StakingDemocracy, + integrations::conviction_voting::StakingConvictionVoting, types::{Conviction, Vote}, }; @@ -7,7 +7,8 @@ use super::*; use frame_system::RawOrigin; use mock::Staking; -use pallet_democracy::{traits::DemocracyHooks, AccountVote}; +use pallet_conviction_voting::traits::VotingHooks; +use pallet_conviction_voting::AccountVote; use pretty_assertions::assert_eq; //NOTE: Referendums with even indexes are finished. @@ -337,14 +338,14 @@ fn process_votes_should_work_when_on_vote_is_called() { let position_before = Staking::positions(position_id).unwrap(); //Act - assert_ok!(StakingDemocracy::::on_vote( + assert_ok!(StakingConvictionVoting::::on_vote( &BOB, 7, AccountVote::Standard { balance: 1_000 * ONE, - vote: pallet_democracy::Vote { + vote: pallet_conviction_voting::Vote { aye: true, - conviction: pallet_democracy::Conviction::None + conviction: pallet_conviction_voting::Conviction::None } } )); @@ -749,9 +750,9 @@ fn process_votes_should_calculate_action_points_corectly_when_referendum_is_fini } #[test] -fn democracy_hook_vote_cap_should_work() { +fn conviction_voting_hook_vote_cap_should_work() { //Locks OVERLAY so 1000 tokens lock and 100 tokens lock results in 1000 tokens locked in total. - use pallet_democracy::{Conviction as Dconviction, Vote as Dvote}; + use pallet_conviction_voting::{Conviction as Dconviction, Vote as Dvote}; ExtBuilder::default() .with_endowed_accounts(vec![ (ALICE, HDX, 150_000 * ONE), @@ -785,7 +786,7 @@ fn democracy_hook_vote_cap_should_work() { assert_eq!(Staking::position_votes(vested_position_id).votes.len(), 0); //Act - happy path, user have enough token for staking and vesting. - assert_ok!(StakingDemocracy::::on_vote(&VESTED_100K, ref_idx, v)); + assert_ok!(StakingConvictionVoting::::on_vote(&VESTED_100K, ref_idx, v)); //Assert let staking_votes = Staking::position_votes(vested_position_id).votes; @@ -798,7 +799,7 @@ fn democracy_hook_vote_cap_should_work() { Tokens::transfer(RuntimeOrigin::signed(VESTED_100K), ALICE, HDX, 1).unwrap(); //Act - assert_ok!(StakingDemocracy::::on_vote(&VESTED_100K, ref_idx, v)); + assert_ok!(StakingConvictionVoting::::on_vote(&VESTED_100K, ref_idx, v)); //Assert let staking_votes = Staking::position_votes(vested_position_id).votes; @@ -813,7 +814,7 @@ fn democracy_hook_vote_cap_should_work() { assert_eq!(Tokens::free_balance(HDX, &VESTED_100K), 100_000 * ONE); //Act 3 - assert_ok!(StakingDemocracy::::on_vote(&VESTED_100K, ref_idx, v)); + assert_ok!(StakingConvictionVoting::::on_vote(&VESTED_100K, ref_idx, v)); //Assert let staking_votes = Staking::position_votes(vested_position_id).votes; @@ -838,13 +839,13 @@ fn democracy_hook_vote_cap_should_work() { let v = AccountVote::Standard { vote: Dvote { aye: true, - conviction: Dconviction::Locked6x, + conviction: pallet_conviction_voting::Conviction::Locked6x, }, balance: 120_000 * ONE, }; //Act 4 - assert_ok!(StakingDemocracy::::on_vote(&VESTED_100K, ref_idx, v)); + assert_ok!(StakingConvictionVoting::::on_vote(&VESTED_100K, ref_idx, v)); //Assert let staking_votes = Staking::position_votes(vested_position_id).votes; @@ -869,13 +870,13 @@ fn democracy_hook_vote_cap_should_work() { let v = AccountVote::Standard { vote: Dvote { aye: true, - conviction: Dconviction::Locked6x, + conviction: pallet_conviction_voting::Conviction::Locked6x, }, balance: 100_000 * ONE, }; //Act 5 - assert_ok!(StakingDemocracy::::on_vote(&VESTED_100K, ref_idx, v)); + assert_ok!(StakingConvictionVoting::::on_vote(&VESTED_100K, ref_idx, v)); //Assert let staking_votes = Staking::position_votes(vested_position_id).votes; diff --git a/pallets/staking/src/traits.rs b/pallets/staking/src/traits.rs index 1a845a980..004cabc65 100644 --- a/pallets/staking/src/traits.rs +++ b/pallets/staking/src/traits.rs @@ -1,6 +1,5 @@ use crate::types::Balance; use frame_support::dispatch::DispatchResult; -use pallet_democracy::ReferendumIndex; use sp_runtime::FixedU128; pub trait PayablePercentage { @@ -8,8 +7,8 @@ pub trait PayablePercentage { fn get(points: Point) -> Option; } -pub trait DemocracyReferendum { - fn is_referendum_finished(index: ReferendumIndex) -> bool; +pub trait GetReferendumState { + fn is_referendum_finished(index: Index) -> bool; } pub(crate) trait ActionData { diff --git a/pallets/staking/src/types.rs b/pallets/staking/src/types.rs index a4fc3964b..83c5e6680 100644 --- a/pallets/staking/src/types.rs +++ b/pallets/staking/src/types.rs @@ -1,6 +1,5 @@ use crate::traits::ActionData; use codec::{Decode, Encode, MaxEncodedLen}; -use pallet_democracy::ReferendumIndex; use scale_info::TypeInfo; use sp_core::bounded::BoundedVec; use sp_core::Get; @@ -11,6 +10,8 @@ pub type Balance = u128; pub type Point = u128; pub type Period = u128; +pub type ReferendumIndex = u32; + pub enum Action { DemocracyVote, } diff --git a/pallets/transaction-pause/Cargo.toml b/pallets/transaction-pause/Cargo.toml index 95cfc0734..5ec81f358 100644 --- a/pallets/transaction-pause/Cargo.toml +++ b/pallets/transaction-pause/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-transaction-pause" -version = "1.0.4" +version = "1.1.0" authors = ["Acala Developers", "GalacticCouncil"] edition = "2021" diff --git a/pallets/transaction-pause/src/lib.rs b/pallets/transaction-pause/src/lib.rs index 03b14881c..4853c73bb 100644 --- a/pallets/transaction-pause/src/lib.rs +++ b/pallets/transaction-pause/src/lib.rs @@ -56,7 +56,7 @@ pub mod pallet { pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The origin which may set filter. + /// The origin which may set the transaction pause filter. type UpdateOrigin: EnsureOrigin; /// Weight information for the extrinsics in this module. diff --git a/pallets/xcm-rate-limiter/src/tests/mock.rs b/pallets/xcm-rate-limiter/src/tests/mock.rs index ec5fdd071..914d3d4ed 100644 --- a/pallets/xcm-rate-limiter/src/tests/mock.rs +++ b/pallets/xcm-rate-limiter/src/tests/mock.rs @@ -250,7 +250,7 @@ impl pallet_omnipool::Config for Test { type AssetRegistry = DummyRegistry; type MinimumTradingLimit = MinTradeAmount; type MinimumPoolLiquidity = MinAddedLiquidity; - type TechnicalOrigin = EnsureRoot; + type UpdateTradabilityOrigin = EnsureRoot; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; type CollectionId = u32; diff --git a/runtime/adapters/Cargo.toml b/runtime/adapters/Cargo.toml index 0cf8fee70..2cec31fd3 100644 --- a/runtime/adapters/Cargo.toml +++ b/runtime/adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-adapters" -version = "1.3.6" +version = "1.3.7" description = "Structs and other generic types for building runtimes." authors = ["GalacticCouncil"] edition = "2021" diff --git a/runtime/adapters/src/tests/mock.rs b/runtime/adapters/src/tests/mock.rs index f4f0bd1f5..132a3300e 100644 --- a/runtime/adapters/src/tests/mock.rs +++ b/runtime/adapters/src/tests/mock.rs @@ -205,7 +205,7 @@ impl pallet_omnipool::Config for Test { type AssetRegistry = DummyRegistry; type MinimumTradingLimit = MinTradeAmount; type MinimumPoolLiquidity = MinAddedLiquidity; - type TechnicalOrigin = EnsureRoot; + type UpdateTradabilityOrigin = EnsureRoot; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; type CollectionId = u32; @@ -338,7 +338,7 @@ impl pallet_route_executor::Config for Test { type EdToRefundCalculator = MockedEdCalculator; type OraclePriceProvider = PriceProviderMock; type DefaultRoutePoolType = DefaultRoutePoolType; - type TechnicalOrigin = EnsureRoot; + type ForceInsertOrigin = EnsureRoot; type OraclePeriod = RouteValidationOraclePeriod; type WeightInfo = (); } diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index ef58b2c8f..4e3f5e743 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "261.0.0" +version = "262.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" @@ -45,6 +45,7 @@ pallet-xyk-liquidity-mining = { workspace = true } # pallets pallet-bags-list = { workspace = true } pallet-balances = { workspace = true } +pallet-conviction-voting = { workspace = true } pallet-transaction-payment = { workspace = true } pallet-transaction-payment-rpc-runtime-api = { workspace = true } pallet-treasury = { workspace = true } @@ -59,9 +60,11 @@ pallet-collective = { workspace = true } pallet-tips = { workspace = true } pallet-proxy = { workspace = true } pallet-multisig = { workspace = true } +pallet-referenda = { workspace = true } pallet-democracy = { workspace = true } pallet-elections-phragmen = { workspace = true } pallet-uniques = { workspace = true } +pallet-whitelist = { workspace = true } pallet-message-queue = { workspace = true } pallet-state-trie-migration = { workspace = true } @@ -129,6 +132,7 @@ frame-system-rpc-runtime-api = { workspace = true } frame-try-runtime = { workspace = true, optional = true } frame-metadata-hash-extension = { workspace = true } sp-api = { workspace = true } +sp-arithmetic = { workspace = true } sp-block-builder = { workspace = true } sp-genesis-builder = { workspace = true } sp-consensus-aura = { workspace = true } @@ -215,6 +219,9 @@ runtime-benchmarks = [ "pallet-evm-accounts/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-whitelist/runtime-benchmarks", ] std = [ "codec/std", @@ -291,6 +298,7 @@ std = [ "pallet-duster/std", "warehouse-liquidity-mining/std", "sp-api/std", + "sp-arithmetic/std", "sp-block-builder/std", "sp-genesis-builder/std", "sp-consensus-aura/std", @@ -325,6 +333,9 @@ std = [ "parachains-common/std", "polkadot-runtime-common/std", "pallet-state-trie-migration/std", + "pallet-conviction-voting/std", + "pallet-referenda/std", + "pallet-whitelist/std", ] try-runtime = [ "frame-try-runtime", @@ -396,6 +407,9 @@ try-runtime = [ "pallet-xyk-liquidity-mining/try-runtime", "pallet-message-queue/try-runtime", "pallet-state-trie-migration/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-referenda/try-runtime", + "pallet-whitelist/try-runtime", ] metadata-hash = [ diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 4d01b10ed..57fef7572 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -16,6 +16,7 @@ // limitations under the License. use super::*; +use crate::origins::{GeneralAdmin, OmnipoolAdmin}; use crate::system::NativeAssetId; use hydradx_adapters::{ @@ -51,8 +52,8 @@ use frame_support::{ sp_runtime::traits::{One, PhantomData}, sp_runtime::{FixedU128, Perbill, Permill}, traits::{ - AsEnsureOriginWithArg, ConstU32, Contains, Currency, Defensive, EnsureOrigin, Imbalance, LockIdentifier, - NeverEnsureOrigin, OnUnbalanced, + AsEnsureOriginWithArg, ConstU32, Contains, Currency, Defensive, EitherOf, EnsureOrigin, Imbalance, + LockIdentifier, NeverEnsureOrigin, OnUnbalanced, }, BoundedVec, PalletId, }; @@ -429,8 +430,8 @@ parameter_types! { impl pallet_asset_registry::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type RegistryOrigin = EnsureRoot; - type UpdateOrigin = SuperMajorityTechCommittee; + type RegistryOrigin = EitherOf, GeneralAdmin>; + type UpdateOrigin = EitherOf, GeneralAdmin>; type Currency = pallet_currencies::fungibles::FungibleCurrencies; type AssetId = AssetId; type AssetNativeLocation = AssetLocation; @@ -457,7 +458,7 @@ impl pallet_uniques::Config for Runtime { type CollectionId = CollectionId; type ItemId = ItemId; type Currency = Balances; - type ForceOrigin = MajorityOfCouncil; + type ForceOrigin = EnsureRoot; // Standard collection creation is disallowed type CreateOrigin = AsEnsureOriginWithArg>; type Locker = (); @@ -491,8 +492,9 @@ impl pallet_omnipool::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; type Currency = Currencies; - type AuthorityOrigin = EnsureRoot; - type TechnicalOrigin = SuperMajorityTechCommittee; + type AuthorityOrigin = EitherOf, OmnipoolAdmin>; + type UpdateTradabilityOrigin = + EitherOf, EitherOf>; type AssetRegistry = AssetRegistry; type HdxAssetId = NativeAssetId; type HubAssetId = LRNA; @@ -544,7 +546,8 @@ impl pallet_circuit_breaker::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; type Balance = Balance; - type TechnicalOrigin = SuperMajorityTechCommittee; + type UpdateLimitsOrigin = + EitherOf, EitherOf>; type WhitelistedAccounts = CircuitBreakerWhitelist; type DefaultMaxNetTradeVolumeLimitPerBlock = DefaultMaxNetTradeVolumeLimitPerBlock; type DefaultMaxAddLiquidityLimitPerBlock = DefaultMaxLiquidityLimitPerBlock; @@ -572,7 +575,7 @@ where impl pallet_ema_oracle::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AuthorityOrigin = SuperMajorityTechCommittee; + type AuthorityOrigin = EitherOf, GeneralAdmin>; /// The definition of the oracle time periods currently assumes a 6 second block time. /// We use the parachain blocks anyway, because we want certain guarantees over how many blocks correspond /// to which smoothing factor. @@ -609,7 +612,7 @@ impl pallet_duster::Config for Runtime { type MinCurrencyDeposits = AssetRegistry; type Reward = DustingReward; type NativeCurrencyId = NativeAssetId; - type BlacklistUpdateOrigin = SuperMajorityTechCommittee; + type BlacklistUpdateOrigin = EitherOf, GeneralAdmin>; type TreasuryAccountId = TreasuryAccount; type WeightInfo = weights::pallet_duster::HydraWeight; } @@ -652,7 +655,7 @@ parameter_types! { impl pallet_omnipool_liquidity_mining::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Currencies; - type CreateOrigin = AllTechnicalCommitteeMembers; + type CreateOrigin = EitherOf, OmnipoolAdmin>; type PalletId = OmniLMPalletId; type NFTCollectionId = OmnipoolLMCollectionId; type NFTHandler = Uniques; @@ -699,7 +702,7 @@ parameter_types! { impl pallet_xyk_liquidity_mining::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currencies = Currencies; - type CreateOrigin = AllTechnicalCommitteeMembers; + type CreateOrigin = EitherOf, GeneralAdmin>; type PalletId = XYKLmPalletId; type NFTCollectionId = XYKLmCollectionId; type NFTHandler = Uniques; @@ -818,7 +821,7 @@ impl Contains for RetryOnErrorForDca { impl pallet_dca::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssetId = AssetId; - type TechnicalOrigin = SuperMajorityTechCommittee; + type TerminateOrigin = EitherOf, EitherOf>; type Currencies = Currencies; type RelayChainBlockHashProvider = RelayChainBlockHashProviderAdapter; type RandomnessProvider = DCA; @@ -1203,7 +1206,7 @@ impl pallet_route_executor::Config for Runtime { type DefaultRoutePoolType = DefaultRoutePoolType; type NativeAssetId = NativeAssetId; type InspectRegistry = AssetRegistry; - type TechnicalOrigin = SuperMajorityTechCommittee; + type ForceInsertOrigin = EitherOf, EitherOf>; type EdToRefundCalculator = RefundAndLockedEdCalculator; type OraclePriceProvider = hydradx_adapters::OraclePriceProvider; type OraclePeriod = RouteValidationOraclePeriod; @@ -1380,7 +1383,8 @@ impl pallet_stableswap::Config for Runtime { type Currency = Currencies; type ShareAccountId = StableswapAccountIdConstructor; type AssetInspection = AssetRegistry; - type AuthorityOrigin = EnsureRoot; + type AuthorityOrigin = EitherOf, OmnipoolAdmin>; + type UpdateTradabilityOrigin = EitherOf, TechCommitteeSuperMajority>; type DustAccountHandler = Duster; type Hooks = StableswapHooksAdapter; type MinPoolLiquidity = MinPoolLiquidity; @@ -1450,9 +1454,13 @@ impl GetByKey for StakingMinSlash { } } +parameter_types! { + pub const MaxVotes: u32 = 25; +} + impl pallet_staking::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AuthorityOrigin = MajorityOfCouncil; + type AuthorityOrigin = EitherOf, GeneralAdmin>; type AssetId = AssetId; type Currency = Currencies; type PeriodLength = PeriodLength; @@ -1472,7 +1480,10 @@ impl pallet_staking::Config for Runtime { type Collections = FreezableNFT; type NFTHandler = Uniques; type MaxVotes = MaxVotes; - type ReferendumInfo = pallet_staking::integrations::democracy::ReferendumStatus; + type ReferendumInfo = pallet_staking::integrations::conviction_voting::ReferendumStatus< + Referenda, + pallet_conviction_voting::TallyOf, + >; type MaxPointsPerAction = PointsPerAction; type Vesting = VestingInfo; type WeightInfo = weights::pallet_staking::HydraWeight; @@ -1506,7 +1517,7 @@ impl pallet_lbp::Config for Runtime { type RuntimeEvent = RuntimeEvent; type MultiCurrency = Currencies; type LockedBalance = MultiCurrencyLockedBalance; - type CreatePoolOrigin = SuperMajorityTechCommittee; + type CreatePoolOrigin = EitherOf, GeneralAdmin>; type LBPWeightFunction = pallet_lbp::LBPWeightFunction; type AssetPairAccountId = AssetPairAccountId; type WeightInfo = weights::pallet_lbp::HydraWeight; @@ -1554,7 +1565,7 @@ parameter_types! { impl pallet_referrals::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AuthorityOrigin = EnsureRoot; + type AuthorityOrigin = EitherOf, GeneralAdmin>; type AssetId = AssetId; type Currency = FungibleCurrencies; type Convert = ConvertViaOmnipool; diff --git a/runtime/hydradx/src/evm/mod.rs b/runtime/hydradx/src/evm/mod.rs index 165815e58..8f10750a3 100644 --- a/runtime/hydradx/src/evm/mod.rs +++ b/runtime/hydradx/src/evm/mod.rs @@ -21,6 +21,7 @@ use crate::evm::evm_fee::FeeCurrencyOverrideOrDefault; use crate::evm::runner::WrapRunner; +use crate::origins::GeneralAdmin; use crate::types::ShortOraclePrice; pub use crate::{ evm::accounts_conversion::{ExtendedAddressMapping, FindAuthorTruncated}, @@ -30,10 +31,11 @@ use crate::{NativeAssetId, LRNA}; pub use fp_evm::GenesisAccount as EvmGenesisAccount; use frame_support::{ parameter_types, - traits::{Defensive, FindAuthor}, + traits::{Defensive, EitherOf, FindAuthor}, weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, ConsensusEngineId, }; +use frame_system::EnsureRoot; use hex_literal::hex; use hydradx_adapters::price::ConvertAmount; use hydradx_adapters::{AssetFeeOraclePriceProvider, OraclePriceProvider}; @@ -187,7 +189,7 @@ impl pallet_evm_accounts::Config for crate::Runtime { type RuntimeEvent = crate::RuntimeEvent; type FeeMultiplier = sp_core::ConstU32<50>; type EvmNonceProvider = EvmNonceProvider; - type ControllerOrigin = crate::SuperMajorityTechCommittee; + type ControllerOrigin = EitherOf, GeneralAdmin>; type WeightInfo = crate::weights::pallet_evm_accounts::HydraWeight; } diff --git a/runtime/hydradx/src/governance.rs b/runtime/hydradx/src/governance.rs deleted file mode 100644 index dd462e301..000000000 --- a/runtime/hydradx/src/governance.rs +++ /dev/null @@ -1,399 +0,0 @@ -// This file is part of HydraDX-node. - -// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::*; -use primitives::constants::{ - currency::{deposit, CENTS, DOLLARS, UNITS}, - time::{DAYS, HOURS}, -}; - -use frame_support::{ - parameter_types, - sp_runtime::{Perbill, Percent, Permill}, - traits::{ - fungible::HoldConsideration, tokens::UnityAssetBalanceConversion, ConstU32, EitherOfDiverse, - LinearStoragePrice, LockIdentifier, PrivilegeCmp, - }, - PalletId, -}; -use frame_system::{EnsureRoot, EnsureSigned}; -use sp_runtime::traits::IdentityLookup; -use sp_staking::currency_to_vote::U128CurrencyToVote; -use sp_std::cmp::Ordering; - -parameter_types! { - pub TreasuryAccount: AccountId = Treasury::account_id(); - pub const ProposalBond: Permill = Permill::from_percent(3); - pub const ProposalBondMinimum: Balance = 100 * DOLLARS; - pub const ProposalBondMaximum: Balance = 500 * DOLLARS; - pub const SpendPeriod: BlockNumber = DAYS; - pub const Burn: Permill = Permill::from_percent(0); - pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); - pub const MaxApprovals: u32 = 100; - pub const TreasuryPayoutPeriod: u32 = 30 * DAYS; -} - -pub struct PayFromTreasuryAccount; - -impl frame_support::traits::tokens::Pay for PayFromTreasuryAccount { - type Balance = Balance; - type Beneficiary = AccountId; - type AssetKind = (); - type Id = (); - type Error = sp_runtime::DispatchError; - - #[cfg(not(feature = "runtime-benchmarks"))] - fn pay( - who: &Self::Beneficiary, - _asset_kind: Self::AssetKind, - amount: Self::Balance, - ) -> Result { - let _ = >::transfer( - &TreasuryAccount::get(), - who, - amount, - frame_support::traits::tokens::Preservation::Expendable, - )?; - Ok(()) - } - - #[cfg(feature = "runtime-benchmarks")] - fn pay( - who: &Self::Beneficiary, - _asset_kind: Self::AssetKind, - amount: Self::Balance, - ) -> Result { - // In case of benchmarks, we adjust the value by multiplying it by 1_000_000_000_000, otherwise it fails with BelowMinimum limit error, because - // treasury benchmarks uses only 100 as the amount. - let _ = >::transfer( - &TreasuryAccount::get(), - who, - amount * 1_000_000_000_000, - frame_support::traits::tokens::Preservation::Expendable, - )?; - Ok(()) - } - - fn check_payment(_id: Self::Id) -> frame_support::traits::tokens::PaymentStatus { - frame_support::traits::tokens::PaymentStatus::Success - } - - #[cfg(feature = "runtime-benchmarks")] - fn ensure_successful(_: &Self::Beneficiary, _: Self::AssetKind, amount: Self::Balance) { - >::mint_into( - &TreasuryAccount::get(), - amount * 1_000_000_000_000, - ) - .unwrap(); - } - #[cfg(feature = "runtime-benchmarks")] - fn ensure_concluded(_: Self::Id) {} -} - -impl pallet_treasury::Config for Runtime { - type Currency = Balances; - type ApproveOrigin = TreasuryApproveOrigin; - type RejectOrigin = MoreThanHalfCouncil; - type RuntimeEvent = RuntimeEvent; - type OnSlash = Treasury; - type ProposalBond = ProposalBond; - type ProposalBondMinimum = ProposalBondMinimum; - type ProposalBondMaximum = ProposalBondMaximum; - type SpendPeriod = SpendPeriod; - type Burn = Burn; - type PalletId = TreasuryPalletId; - type BurnDestination = (); - type WeightInfo = weights::pallet_treasury::HydraWeight; - type SpendFunds = (); - type MaxApprovals = MaxApprovals; - #[cfg(not(feature = "runtime-benchmarks"))] - type SpendOrigin = frame_support::traits::NeverEnsureOrigin; - #[cfg(feature = "runtime-benchmarks")] - type SpendOrigin = - frame_system::EnsureWithSuccess, AccountId, crate::benches::BenchmarkMaxBalance>; - type AssetKind = (); // set to () to support only the native currency - type Beneficiary = AccountId; - type BeneficiaryLookup = IdentityLookup; - type Paymaster = PayFromTreasuryAccount; - type BalanceConverter = UnityAssetBalanceConversion; - type PayoutPeriod = TreasuryPayoutPeriod; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); // default impl is enough because we support only the native currency -} - -parameter_types! { - pub PreimageBaseDeposit: Balance = deposit(2, 64); - pub PreimageByteDeposit: Balance = deposit(0, 1); - pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); -} - -impl pallet_preimage::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = weights::pallet_preimage::HydraWeight; - type Currency = Balances; - type ManagerOrigin = EnsureRoot; - type Consideration = HoldConsideration< - AccountId, - Balances, - PreimageHoldReason, - LinearStoragePrice, - >; -} - -/// Used the compare the privilege of an origin inside the scheduler. -pub struct OriginPrivilegeCmp; - -impl PrivilegeCmp for OriginPrivilegeCmp { - fn cmp_privilege(left: &OriginCaller, right: &OriginCaller) -> Option { - if left == right { - return Some(Ordering::Equal); - } - - match (left, right) { - // Root is greater than anything. - (OriginCaller::system(frame_system::RawOrigin::Root), _) => Some(Ordering::Greater), - // Check which one has more yes votes. - ( - OriginCaller::Council(pallet_collective::RawOrigin::Members(l_yes_votes, l_count)), - OriginCaller::Council(pallet_collective::RawOrigin::Members(r_yes_votes, r_count)), - ) => Some((l_yes_votes * r_count).cmp(&(r_yes_votes * l_count))), - // For every other origin we don't care, as they are not used for `ScheduleOrigin`. - _ => None, - } - } -} - -parameter_types! { - pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; - pub const MaxScheduledPerBlock: u32 = 50; -} -impl pallet_scheduler::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type PalletsOrigin = OriginCaller; - type RuntimeCall = RuntimeCall; - type MaximumWeight = MaximumSchedulerWeight; - type ScheduleOrigin = MoreThanHalfCouncil; - type OriginPrivilegeCmp = OriginPrivilegeCmp; - type MaxScheduledPerBlock = MaxScheduledPerBlock; - type WeightInfo = weights::pallet_scheduler::HydraWeight; - type Preimages = Preimage; -} - -parameter_types! { - pub const CouncilMaxProposals: u32 = 30; - pub const CouncilMaxMembers: u32 = 13; - pub const CouncilMotionDuration: BlockNumber = 5 * DAYS; - pub MaxProposalWeight: Weight = Perbill::from_percent(50) * BlockWeights::get().max_block; -} - -pub type CouncilCollective = pallet_collective::Instance1; -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = CouncilMotionDuration; - type MaxProposals = CouncilMaxProposals; - type MaxMembers = CouncilMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type WeightInfo = weights::pallet_collective_council::HydraWeight; - type MaxProposalWeight = MaxProposalWeight; - type SetMembersOrigin = EnsureRoot; -} - -parameter_types! { - pub const TechnicalMaxProposals: u32 = 20; - pub const TechnicalMaxMembers: u32 = 10; - pub const TechnicalMotionDuration: BlockNumber = 5 * DAYS; -} - -pub type TechnicalCollective = pallet_collective::Instance2; -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = TechnicalMotionDuration; - type MaxProposals = TechnicalMaxProposals; - type MaxMembers = TechnicalMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type WeightInfo = weights::pallet_collective_technical_committee::HydraWeight; - type MaxProposalWeight = MaxProposalWeight; - type SetMembersOrigin = EnsureRoot; -} - -#[cfg(test)] -mod tests { - use super::{EnactmentPeriod, VoteLockingPeriod}; - - #[test] - fn democracy_periods() { - // Make sure VoteLockingPeriod > EnactmentPeriod - assert!(VoteLockingPeriod::get() > EnactmentPeriod::get()); - } -} - -pub type TreasuryApproveOrigin = EitherOfDiverse< - EnsureRoot, - pallet_collective::EnsureProportionAtLeast, ->; - -pub type MoreThanHalfCouncil = EitherOfDiverse< - EnsureRoot, - pallet_collective::EnsureProportionMoreThan, ->; - -pub type MajorityOfCouncil = EitherOfDiverse< - pallet_collective::EnsureProportionAtLeast, - EnsureRoot, ->; - -pub type AllCouncilMembers = EitherOfDiverse< - pallet_collective::EnsureProportionAtLeast, - EnsureRoot, ->; - -pub type MoreThanHalfTechCommittee = EitherOfDiverse< - pallet_collective::EnsureProportionAtLeast, - EnsureRoot, ->; - -pub type SuperMajorityTechCommittee = EitherOfDiverse< - pallet_collective::EnsureProportionAtLeast, - EnsureRoot, ->; - -pub type AllTechnicalCommitteeMembers = EitherOfDiverse< - pallet_collective::EnsureProportionAtLeast, - EnsureRoot, ->; - -parameter_types! { - pub const LaunchPeriod: BlockNumber = 3 * DAYS; - pub const VotingPeriod: BlockNumber = 3 * DAYS; - pub const FastTrackVotingPeriod: BlockNumber = 3 * HOURS; - pub const MinimumDeposit: Balance = 1000 * DOLLARS; - pub const EnactmentPeriod: BlockNumber = 24 * HOURS; - // Make sure VoteLockingPeriod > EnactmentPeriod - pub const VoteLockingPeriod: BlockNumber = 6 * DAYS; - pub const CooloffPeriod: BlockNumber = 7 * DAYS; - pub const InstantAllowed: bool = true; - pub const MaxVotes: u32 = 100; - pub const MaxProposals: u32 = 100; -} - -impl pallet_democracy::Config for Runtime { - type WeightInfo = weights::pallet_democracy::HydraWeight; - type RuntimeEvent = RuntimeEvent; - type Scheduler = Scheduler; - type Preimages = Preimage; - type Currency = Balances; - type EnactmentPeriod = EnactmentPeriod; - type LaunchPeriod = LaunchPeriod; - type VotingPeriod = VotingPeriod; - type VoteLockingPeriod = VoteLockingPeriod; - type MinimumDeposit = MinimumDeposit; - type InstantAllowed = InstantAllowed; - type FastTrackVotingPeriod = FastTrackVotingPeriod; - type CooloffPeriod = CooloffPeriod; - type MaxVotes = MaxVotes; - type MaxProposals = MaxProposals; - type MaxDeposits = ConstU32<100>; - type MaxBlacklisted = ConstU32<100>; - /// A straight majority of the council can decide what their next motion is. - type ExternalOrigin = MoreThanHalfCouncil; - type ExternalMajorityOrigin = MoreThanHalfCouncil; - /// A unanimous council can have the next scheduled referendum be a straight default-carries - /// (NTB) vote. - type ExternalDefaultOrigin = AllCouncilMembers; - type SubmitOrigin = EnsureSigned; - type FastTrackOrigin = MoreThanHalfTechCommittee; - type InstantOrigin = AllTechnicalCommitteeMembers; - // To cancel a proposal which has been passed, 2/3 of the council must agree to it. - type CancellationOrigin = MajorityOfCouncil; - type BlacklistOrigin = EnsureRoot; - // To cancel a proposal before it has been passed, the technical committee must be unanimous or - // Root must agree. - type CancelProposalOrigin = AllTechnicalCommitteeMembers; - // Any single technical committee member may veto a coming council proposal, however they can - // only do it once and it lasts only for the cooloff period. - type VetoOrigin = pallet_collective::EnsureMember; - type PalletsOrigin = OriginCaller; - type Slash = Treasury; - type DemocracyHooks = pallet_staking::integrations::democracy::StakingDemocracy; - // Any single technical committee member may remove a vote. - type VoteRemovalOrigin = frame_system::EnsureSignedBy; -} - -parameter_types! { - // Bond for candidacy into governance - pub const CandidacyBond: Balance = 5 * DOLLARS; - // 1 storage item created, key size is 32 bytes, value size is 16+16. - pub const VotingBondBase: Balance = CENTS; - // additional data per vote is 32 bytes (account id). - pub const VotingBondFactor: Balance = CENTS; - pub const TermDuration: BlockNumber = 7 * DAYS; - pub const DesiredMembers: u32 = 13; - pub const DesiredRunnersUp: u32 = 15; - pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect"; - pub const MaxElectionCandidates: u32 = 100; - pub const MaxElectionVoters: u32 = 768; - pub const MaxVotesPerVoter: u32 = 10; -} - -impl pallet_elections_phragmen::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type PalletId = ElectionsPhragmenPalletId; - type Currency = Balances; - type ChangeMembers = Council; - type InitializeMembers = (); - // Set to () if defined in chain spec - type CurrencyToVote = U128CurrencyToVote; - type CandidacyBond = CandidacyBond; - type VotingBondBase = VotingBondBase; - type VotingBondFactor = VotingBondFactor; - type LoserCandidate = Treasury; - type KickedMember = Treasury; - type DesiredMembers = DesiredMembers; - type DesiredRunnersUp = DesiredRunnersUp; - type TermDuration = TermDuration; - type MaxCandidates = MaxElectionCandidates; - type MaxVoters = MaxElectionVoters; - type WeightInfo = weights::pallet_elections_phragmen::HydraWeight; - type MaxVotesPerVoter = MaxVotesPerVoter; -} - -parameter_types! { - pub const DataDepositPerByte: Balance = CENTS; - pub const TipCountdown: BlockNumber = 24 * HOURS; - pub const TipFindersFee: Percent = Percent::from_percent(1); - pub const TipReportDepositBase: Balance = 10 * DOLLARS; - pub const TipReportDepositPerByte: Balance = CENTS; - pub const MaximumReasonLength: u32 = 1024; - pub const MaxTipAmount: u128 = 5_000_000 * UNITS; // ~$100k -} - -impl pallet_tips::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type MaximumReasonLength = MaximumReasonLength; - type DataDepositPerByte = DataDepositPerByte; - type TipCountdown = TipCountdown; - type TipFindersFee = TipFindersFee; - type TipReportDepositBase = TipReportDepositBase; - type MaxTipAmount = MaxTipAmount; - type Tippers = Elections; - type WeightInfo = weights::pallet_tips::HydraWeight; -} diff --git a/runtime/hydradx/src/governance/mod.rs b/runtime/hydradx/src/governance/mod.rs new file mode 100644 index 000000000..1b0f2cca0 --- /dev/null +++ b/runtime/hydradx/src/governance/mod.rs @@ -0,0 +1,231 @@ +// This file is part of https://github.com/galacticcouncil/* +// +// $$$$$$$ Licensed under the Apache License, Version 2.0 (the "License") +// $$$$$$$$$$$$$ you may only use this file in compliance with the License +// $$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$ Copyright (C) 2021-2024 Intergalactic, Limited (GIB) +// $$$$$$$$$$$ $$$$$$$$$$ SPDX-License-Identifier: Apache-2.0 +// $$$$$$$$$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$$$$$$$$$$$$$$$ $ Built with <3 for decentralisation +// $$$$$$$$$$$$$$$$$$$ $$$$$$$ +// $$$$$$$ $$$$$$$$$$$$$$$$$$ Unless required by applicable law or agreed to in +// $ $$$$$$$$$$$$$$$$$$$$$$$ writing, software distributed under the License is +// $$$$$$$$$$$$$$$$$$$$$$$$$$ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES +// $$$$$$$$$ $$$$$$$$$$$ OR CONDITIONS OF ANY KIND, either express or implied. +// $$$$$$$$ +// $$$$$$$$$$$$$$$$$$ See the License for the specific language governing +// $$$$$$$$$$$$$ permissions and limitations under the License. +// $$$$$$$ +// $$ +// $$$$$ $$$$$ $$ $ +// $$$ $$$ $$$ $$ $$$$$ $$ $$$ $$$$ $$$$$$$ $$$$ $$$ $$$$$$ $$ $$$$$$ +// $$$ $$$ $$$ $$ $$$ $$$ $$$ $ $$ $$ $$ $$ $$ $$ $$$ $$$ +// $$$$$$$$$$$ $$ $$ $$$ $$ $$ $$$$$$$ $$ $$ $$ $$$ $$ $$ +// $$$ $$$ $$$$ $$$ $$ $$ $$$ $$ $$ $$ $$ $$ $$ $$ +// $$$$$ $$$$$ $$ $$$$$$$$ $ $$$ $$$$$$$$ $$$ $$$$ $$$$$$$ $$$$ $$$$ +// $$$ + +// Gov V1 (to be deprecated) +pub mod old; + +pub mod origins; +pub mod tracks; + +use super::*; +use crate::governance::{ + origins::{GeneralAdmin, ReferendumCanceller, ReferendumKiller, Spender, Treasurer, WhitelistedCaller}, + tracks::TracksInfo, +}; +use frame_support::{ + parameter_types, + sp_runtime::Permill, + traits::{tokens::UnityAssetBalanceConversion, EitherOf}, + PalletId, +}; +use frame_system::{EnsureRoot, EnsureRootWithSuccess}; +use pallet_collective::EnsureProportionAtLeast; +use primitives::constants::{currency::DOLLARS, time::DAYS}; +use sp_arithmetic::Perbill; +use sp_core::ConstU32; +use sp_runtime::traits::IdentityLookup; + +pub type TechCommitteeMajority = EnsureProportionAtLeast; +pub type TechCommitteeSuperMajority = EnsureProportionAtLeast; + +parameter_types! { + pub const TechnicalMaxProposals: u32 = 20; + pub const TechnicalMaxMembers: u32 = 10; + pub const TechnicalMotionDuration: BlockNumber = 5 * DAYS; + pub MaxProposalWeight: Weight = Perbill::from_percent(50) * BlockWeights::get().max_block; +} + +pub type TechnicalCollective = pallet_collective::Instance2; +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = TechnicalMotionDuration; + type MaxProposals = TechnicalMaxProposals; + type MaxMembers = TechnicalMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = weights::pallet_collective_technical_committee::HydraWeight; + type MaxProposalWeight = MaxProposalWeight; + type SetMembersOrigin = EitherOf, GeneralAdmin>; +} + +parameter_types! { + pub TreasuryAccount: AccountId = Treasury::account_id(); + pub const ProposalBond: Permill = Permill::from_percent(3); + pub const ProposalBondMinimum: Balance = 100 * DOLLARS; + pub const ProposalBondMaximum: Balance = 500 * DOLLARS; + pub const SpendPeriod: BlockNumber = DAYS; + pub const Burn: Permill = Permill::from_percent(0); + pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); + pub const MaxApprovals: u32 = 100; + pub const TreasuryPayoutPeriod: u32 = 30 * DAYS; +} + +pub struct PayFromTreasuryAccount; + +impl frame_support::traits::tokens::Pay for PayFromTreasuryAccount { + type Balance = Balance; + type Beneficiary = AccountId; + type AssetKind = (); + type Id = (); + type Error = sp_runtime::DispatchError; + + #[cfg(not(feature = "runtime-benchmarks"))] + fn pay( + who: &Self::Beneficiary, + _asset_kind: Self::AssetKind, + amount: Self::Balance, + ) -> Result { + let _ = >::transfer( + &TreasuryAccount::get(), + who, + amount, + frame_support::traits::tokens::Preservation::Expendable, + )?; + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn pay( + who: &Self::Beneficiary, + _asset_kind: Self::AssetKind, + amount: Self::Balance, + ) -> Result { + // In case of benchmarks, we adjust the value by multiplying it by 1_000_000_000_000, otherwise it fails with BelowMinimum limit error, because + // treasury benchmarks uses only 100 as the amount. + let _ = >::transfer( + &TreasuryAccount::get(), + who, + amount * 1_000_000_000_000, + frame_support::traits::tokens::Preservation::Expendable, + )?; + Ok(()) + } + + fn check_payment(_id: Self::Id) -> frame_support::traits::tokens::PaymentStatus { + frame_support::traits::tokens::PaymentStatus::Success + } + + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful(_: &Self::Beneficiary, _: Self::AssetKind, amount: Self::Balance) { + >::mint_into( + &TreasuryAccount::get(), + amount * 1_000_000_000_000, + ) + .unwrap(); + } + #[cfg(feature = "runtime-benchmarks")] + fn ensure_concluded(_: Self::Id) {} +} + +impl pallet_treasury::Config for Runtime { + type Currency = Balances; + type ApproveOrigin = EitherOf, Treasurer>; + type RejectOrigin = EitherOf, Treasurer>; + type RuntimeEvent = RuntimeEvent; + type OnSlash = Treasury; + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ProposalBondMinimum; + type ProposalBondMaximum = ProposalBondMaximum; + type SpendPeriod = SpendPeriod; + type Burn = Burn; + type PalletId = TreasuryPalletId; + type BurnDestination = (); + type WeightInfo = weights::pallet_treasury::HydraWeight; + type SpendFunds = (); + type MaxApprovals = MaxApprovals; + #[cfg(not(feature = "runtime-benchmarks"))] + type SpendOrigin = TreasurySpender; + #[cfg(feature = "runtime-benchmarks")] + type SpendOrigin = + frame_system::EnsureWithSuccess, AccountId, crate::benches::BenchmarkMaxBalance>; + type AssetKind = (); // set to () to support only the native currency + type Beneficiary = AccountId; + type BeneficiaryLookup = IdentityLookup; + type Paymaster = PayFromTreasuryAccount; + type BalanceConverter = UnityAssetBalanceConversion; + type PayoutPeriod = TreasuryPayoutPeriod; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); // default impl is enough because we support only the native currency +} + +parameter_types! { + pub const VoteLockingPeriod: BlockNumber = 7 * DAYS; +} + +impl pallet_conviction_voting::Config for Runtime { + type WeightInfo = weights::pallet_conviction_voting::HydraWeight; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type VoteLockingPeriod = VoteLockingPeriod; + type MaxVotes = ConstU32<25>; + type MaxTurnout = frame_support::traits::tokens::currency::ActiveIssuanceOf; + type Polls = Referenda; + type VotingHooks = pallet_staking::integrations::conviction_voting::StakingConvictionVoting; +} + +parameter_types! { + pub const MaxBalance: Balance = Balance::max_value(); +} +pub type TreasurySpender = EitherOf, Spender>; + +impl pallet_whitelist::Config for Runtime { + type WeightInfo = weights::pallet_whitelist::HydraWeight; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WhitelistOrigin = EitherOf, TechCommitteeMajority>; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type Preimages = Preimage; +} + +parameter_types! { + pub const AlarmInterval: BlockNumber = 1; + pub const SubmissionDeposit: Balance = DOLLARS; + pub const UndecidingTimeout: BlockNumber = 14 * DAYS; +} + +impl pallet_referenda::Config for Runtime { + type WeightInfo = weights::pallet_referenda::HydraWeight; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = Balances; + type SubmitOrigin = frame_system::EnsureSigned; + type CancelOrigin = EitherOf, ReferendumCanceller>; + type KillOrigin = EitherOf, ReferendumKiller>; + type Slash = Treasury; + type Votes = pallet_conviction_voting::VotesOf; + type Tally = pallet_conviction_voting::TallyOf; + type SubmissionDeposit = SubmissionDeposit; + type MaxQueued = ConstU32<100>; + type UndecidingTimeout = UndecidingTimeout; + type AlarmInterval = AlarmInterval; + type Tracks = TracksInfo; + type Preimages = Preimage; +} + +impl origins::pallet_custom_origins::Config for Runtime {} diff --git a/runtime/hydradx/src/governance/old.rs b/runtime/hydradx/src/governance/old.rs new file mode 100644 index 000000000..a3cf51336 --- /dev/null +++ b/runtime/hydradx/src/governance/old.rs @@ -0,0 +1,224 @@ +// This file is part of https://github.com/galacticcouncil/* +// +// $$$$$$$ Licensed under the Apache License, Version 2.0 (the "License") +// $$$$$$$$$$$$$ you may only use this file in compliance with the License +// $$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$ Copyright (C) 2021-2024 Intergalactic, Limited (GIB) +// $$$$$$$$$$$ $$$$$$$$$$ SPDX-License-Identifier: Apache-2.0 +// $$$$$$$$$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$$$$$$$$$$$$$$$ $ Built with <3 for decentralisation +// $$$$$$$$$$$$$$$$$$$ $$$$$$$ +// $$$$$$$ $$$$$$$$$$$$$$$$$$ Unless required by applicable law or agreed to in +// $ $$$$$$$$$$$$$$$$$$$$$$$ writing, software distributed under the License is +// $$$$$$$$$$$$$$$$$$$$$$$$$$ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES +// $$$$$$$$$ $$$$$$$$$$$ OR CONDITIONS OF ANY KIND, either express or implied. +// $$$$$$$$ +// $$$$$$$$$$$$$$$$$$ See the License for the specific language governing +// $$$$$$$$$$$$$ permissions and limitations under the License. +// $$$$$$$ +// $$ +// $$$$$ $$$$$ $$ $ +// $$$ $$$ $$$ $$ $$$$$ $$ $$$ $$$$ $$$$$$$ $$$$ $$$ $$$$$$ $$ $$$$$$ +// $$$ $$$ $$$ $$ $$$ $$$ $$$ $ $$ $$ $$ $$ $$ $$ $$$ $$$ +// $$$$$$$$$$$ $$ $$ $$$ $$ $$ $$$$$$$ $$ $$ $$ $$$ $$ $$ +// $$$ $$$ $$$$ $$$ $$ $$ $$$ $$ $$ $$ $$ $$ $$ $$ +// $$$$$ $$$$$ $$ $$$$$$$$ $ $$$ $$$$$$$$ $$$ $$$$ $$$$$$$ $$$$ $$$$ +// $$$ + +use super::*; +use primitives::constants::{ + currency::{CENTS, DOLLARS, UNITS}, + time::{DAYS, HOURS}, +}; + +use frame_support::{ + parameter_types, + sp_runtime::{Perbill, Percent}, + traits::{ConstU32, EitherOfDiverse, LockIdentifier}, +}; +use frame_system::{EnsureRoot, EnsureSigned}; +use sp_staking::currency_to_vote::U128CurrencyToVote; + +#[cfg(test)] +mod tests { + use super::{EnactmentPeriod, VoteLockingPeriod}; + + #[test] + fn democracy_periods() { + // Make sure VoteLockingPeriod > EnactmentPeriod + assert!(VoteLockingPeriod::get() > EnactmentPeriod::get()); + } +} + +pub type TreasuryApproveOrigin = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionAtLeast, +>; + +pub type MoreThanHalfCouncil = EitherOfDiverse< + EnsureRoot, + pallet_collective::EnsureProportionMoreThan, +>; + +pub type MajorityOfCouncil = EitherOfDiverse< + pallet_collective::EnsureProportionAtLeast, + EnsureRoot, +>; + +pub type AllCouncilMembers = EitherOfDiverse< + pallet_collective::EnsureProportionAtLeast, + EnsureRoot, +>; + +pub type MoreThanHalfTechCommittee = EitherOfDiverse< + pallet_collective::EnsureProportionAtLeast, + EnsureRoot, +>; + +pub type SuperMajorityTechCommittee = EitherOfDiverse< + pallet_collective::EnsureProportionAtLeast, + EnsureRoot, +>; + +pub type AllTechnicalCommitteeMembers = EitherOfDiverse< + pallet_collective::EnsureProportionAtLeast, + EnsureRoot, +>; + +parameter_types! { + pub const CouncilMaxProposals: u32 = 30; + pub const CouncilMaxMembers: u32 = 13; + pub const CouncilMotionDuration: BlockNumber = 5 * DAYS; + pub MaxProposalWeight: Weight = Perbill::from_percent(50) * BlockWeights::get().max_block; +} + +pub type CouncilCollective = pallet_collective::Instance1; +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = CouncilMotionDuration; + type MaxProposals = CouncilMaxProposals; + type MaxMembers = CouncilMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = weights::pallet_collective_council::HydraWeight; + type MaxProposalWeight = MaxProposalWeight; + type SetMembersOrigin = EnsureRoot; +} + +parameter_types! { + pub const LaunchPeriod: BlockNumber = 3 * DAYS; + pub const VotingPeriod: BlockNumber = 3 * DAYS; + pub const FastTrackVotingPeriod: BlockNumber = 3 * HOURS; + pub const MinimumDeposit: Balance = 1000 * DOLLARS; + pub const EnactmentPeriod: BlockNumber = 24 * HOURS; + // Make sure VoteLockingPeriod > EnactmentPeriod + pub const VoteLockingPeriod: BlockNumber = 6 * DAYS; + pub const CooloffPeriod: BlockNumber = 7 * DAYS; + pub const InstantAllowed: bool = true; + pub const MaxVotes: u32 = 100; + pub const MaxProposals: u32 = 100; +} + +impl pallet_democracy::Config for Runtime { + type WeightInfo = weights::pallet_democracy::HydraWeight; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Preimages = Preimage; + type Currency = Balances; + type EnactmentPeriod = EnactmentPeriod; + type LaunchPeriod = LaunchPeriod; + type VotingPeriod = VotingPeriod; + type VoteLockingPeriod = VoteLockingPeriod; + type MinimumDeposit = MinimumDeposit; + type InstantAllowed = InstantAllowed; + type FastTrackVotingPeriod = FastTrackVotingPeriod; + type CooloffPeriod = CooloffPeriod; + type MaxVotes = MaxVotes; + type MaxProposals = MaxProposals; + type MaxDeposits = ConstU32<100>; + type MaxBlacklisted = ConstU32<100>; + /// A straight majority of the council can decide what their next motion is. + type ExternalOrigin = MoreThanHalfCouncil; + type ExternalMajorityOrigin = MoreThanHalfCouncil; + /// A unanimous council can have the next scheduled referendum be a straight default-carries + /// (NTB) vote. + type ExternalDefaultOrigin = AllCouncilMembers; + type SubmitOrigin = EnsureSigned; + type FastTrackOrigin = MoreThanHalfTechCommittee; + type InstantOrigin = AllTechnicalCommitteeMembers; + // To cancel a proposal which has been passed, 2/3 of the council must agree to it. + type CancellationOrigin = MajorityOfCouncil; + type BlacklistOrigin = EnsureRoot; + // To cancel a proposal before it has been passed, the technical committee must be unanimous or + // Root must agree. + type CancelProposalOrigin = AllTechnicalCommitteeMembers; + // Any single technical committee member may veto a coming council proposal, however they can + // only do it once and it lasts only for the cooloff period. + type VetoOrigin = pallet_collective::EnsureMember; + type PalletsOrigin = OriginCaller; + type Slash = Treasury; + type DemocracyHooks = (); + // Any single technical committee member may remove a vote. + type VoteRemovalOrigin = frame_system::EnsureSignedBy; +} + +parameter_types! { + // Bond for candidacy into governance + pub const CandidacyBond: Balance = 5 * DOLLARS; + // 1 storage item created, key size is 32 bytes, value size is 16+16. + pub const VotingBondBase: Balance = CENTS; + // additional data per vote is 32 bytes (account id). + pub const VotingBondFactor: Balance = CENTS; + pub const TermDuration: BlockNumber = 7 * DAYS; + pub const DesiredMembers: u32 = 13; + pub const DesiredRunnersUp: u32 = 15; + pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect"; + pub const MaxElectionCandidates: u32 = 100; + pub const MaxElectionVoters: u32 = 768; + pub const MaxVotesPerVoter: u32 = 10; +} + +impl pallet_elections_phragmen::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type PalletId = ElectionsPhragmenPalletId; + type Currency = Balances; + type ChangeMembers = Council; + type InitializeMembers = (); + // Set to () if defined in chain spec + type CurrencyToVote = U128CurrencyToVote; + type CandidacyBond = CandidacyBond; + type VotingBondBase = VotingBondBase; + type VotingBondFactor = VotingBondFactor; + type LoserCandidate = Treasury; + type KickedMember = Treasury; + type DesiredMembers = DesiredMembers; + type DesiredRunnersUp = DesiredRunnersUp; + type TermDuration = TermDuration; + type MaxCandidates = MaxElectionCandidates; + type MaxVoters = MaxElectionVoters; + type WeightInfo = weights::pallet_elections_phragmen::HydraWeight; + type MaxVotesPerVoter = MaxVotesPerVoter; +} + +parameter_types! { + pub const DataDepositPerByte: Balance = CENTS; + pub const TipCountdown: BlockNumber = 24 * HOURS; + pub const TipFindersFee: Percent = Percent::from_percent(1); + pub const TipReportDepositBase: Balance = 10 * DOLLARS; + pub const TipReportDepositPerByte: Balance = CENTS; + pub const MaximumReasonLength: u32 = 1024; + pub const MaxTipAmount: u128 = 5_000_000 * UNITS; // ~$100k +} + +impl pallet_tips::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MaximumReasonLength = MaximumReasonLength; + type DataDepositPerByte = DataDepositPerByte; + type TipCountdown = TipCountdown; + type TipFindersFee = TipFindersFee; + type TipReportDepositBase = TipReportDepositBase; + type MaxTipAmount = MaxTipAmount; + type Tippers = Elections; + type WeightInfo = weights::pallet_tips::HydraWeight; +} diff --git a/runtime/hydradx/src/governance/origins.rs b/runtime/hydradx/src/governance/origins.rs new file mode 100644 index 000000000..dc65f901e --- /dev/null +++ b/runtime/hydradx/src/governance/origins.rs @@ -0,0 +1,145 @@ +// This file is part of https://github.com/galacticcouncil/* +// +// $$$$$$$ Licensed under the Apache License, Version 2.0 (the "License") +// $$$$$$$$$$$$$ you may only use this file in compliance with the License +// $$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$ Copyright (C) 2021-2024 Intergalactic, Limited (GIB) +// $$$$$$$$$$$ $$$$$$$$$$ SPDX-License-Identifier: Apache-2.0 +// $$$$$$$$$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$$$$$$$$$$$$$$$ $ Built with <3 for decentralisation +// $$$$$$$$$$$$$$$$$$$ $$$$$$$ +// $$$$$$$ $$$$$$$$$$$$$$$$$$ Unless required by applicable law or agreed to in +// $ $$$$$$$$$$$$$$$$$$$$$$$ writing, software distributed under the License is +// $$$$$$$$$$$$$$$$$$$$$$$$$$ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES +// $$$$$$$$$ $$$$$$$$$$$ OR CONDITIONS OF ANY KIND, either express or implied. +// $$$$$$$$ +// $$$$$$$$$$$$$$$$$$ See the License for the specific language governing +// $$$$$$$$$$$$$ permissions and limitations under the License. +// $$$$$$$ +// $$ +// $$$$$ $$$$$ $$ $ +// $$$ $$$ $$$ $$ $$$$$ $$ $$$ $$$$ $$$$$$$ $$$$ $$$ $$$$$$ $$ $$$$$$ +// $$$ $$$ $$$ $$ $$$ $$$ $$$ $ $$ $$ $$ $$ $$ $$ $$$ $$$ +// $$$$$$$$$$$ $$ $$ $$$ $$ $$ $$$$$$$ $$ $$ $$ $$$ $$ $$ +// $$$ $$$ $$$$ $$$ $$ $$ $$$ $$ $$ $$ $$ $$ $$ $$ +// $$$$$ $$$$$ $$ $$$$$$$$ $ $$$ $$$$$$$$ $$$ $$$$ $$$$$$$ $$$$ $$$$ +// $$$ + +//! Custom origins for governance interventions. +pub use pallet_custom_origins::*; + +#[frame_support::pallet] +pub mod pallet_custom_origins { + use crate::Balance; + use frame_support::pallet_prelude::*; + use primitives::constants::currency::UNITS; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[derive(PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug)] + #[pallet::origin] + pub enum Origin { + /// Origin able to dispatch a whitelisted call. + WhitelistedCaller, + /// Origin able to cancel referenda. + ReferendumCanceller, + /// Origin able to kill referenda. + ReferendumKiller, + /// Origin for managing general stuff such as the registrar and permissioned HRMP + /// channel operations. + GeneralAdmin, + /// Origin able to perform specific Omnipool-related actions. + OmnipoolAdmin, + /// Origin for spending very high amounts of BSX from the treasury as well as generally + /// administering it. + Treasurer, + /// Origin able to spend up to roughly $5,000 from the treasury at once. + Spender, + /// Origin able to spend up to roughly $500 from the treasury at once. + Tipper, + } + + macro_rules! decl_unit_ensures { + ( $name:ident: $success_type:ty = $success:expr ) => { + pub struct $name; + impl> + From> + EnsureOrigin for $name + { + type Success = $success_type; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + Origin::$name => Ok($success), + r => Err(O::from(r)), + }) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(O::from(Origin::$name)) + } + } + }; + ( $name:ident ) => { decl_unit_ensures! { $name : () = () } }; + ( $name:ident: $success_type:ty = $success:expr, $( $rest:tt )* ) => { + decl_unit_ensures! { $name: $success_type = $success } + decl_unit_ensures! { $( $rest )* } + }; + ( $name:ident, $( $rest:tt )* ) => { + decl_unit_ensures! { $name } + decl_unit_ensures! { $( $rest )* } + }; + () => {} + } + decl_unit_ensures!( + GeneralAdmin, + OmnipoolAdmin, + WhitelistedCaller, + ReferendumCanceller, + ReferendumKiller, + Treasurer, + ); + + macro_rules! decl_ensure { + ( + $vis:vis type $name:ident: EnsureOrigin { + $( $item:ident = $success:expr, )* + } + ) => { + $vis struct $name; + impl> + From> + EnsureOrigin for $name + { + type Success = $success_type; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + $( + Origin::$item => Ok($success), + )* + r => Err(O::from(r)), + }) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + // By convention the more privileged origins go later, so for greatest chance + // of success, we want the last one. + let _result: Result = Err(()); + $( + let _result: Result = Ok(O::from(Origin::$item)); + )* + _result + } + } + } + } + + decl_ensure! { + pub type Spender: EnsureOrigin { + Tipper = 50_000 * UNITS, // ~= $500 + Spender = 2_222_222 * UNITS, // ~= $22,222 + Treasurer = 50_000_000 * UNITS, // ~= $500,000 + } + } +} diff --git a/runtime/hydradx/src/governance/tracks.rs b/runtime/hydradx/src/governance/tracks.rs new file mode 100644 index 000000000..560807637 --- /dev/null +++ b/runtime/hydradx/src/governance/tracks.rs @@ -0,0 +1,206 @@ +// This file is part of https://github.com/galacticcouncil/* +// +// $$$$$$$ Licensed under the Apache License, Version 2.0 (the "License") +// $$$$$$$$$$$$$ you may only use this file in compliance with the License +// $$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$ Copyright (C) 2021-2024 Intergalactic, Limited (GIB) +// $$$$$$$$$$$ $$$$$$$$$$ SPDX-License-Identifier: Apache-2.0 +// $$$$$$$$$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$$$$$$$$$$$$$$$ $ Built with <3 for decentralisation +// $$$$$$$$$$$$$$$$$$$ $$$$$$$ +// $$$$$$$ $$$$$$$$$$$$$$$$$$ Unless required by applicable law or agreed to in +// $ $$$$$$$$$$$$$$$$$$$$$$$ writing, software distributed under the License is +// $$$$$$$$$$$$$$$$$$$$$$$$$$ distributed on an "AS IS" BASIS, WITHOUT WARRANTIES +// $$$$$$$$$ $$$$$$$$$$$ OR CONDITIONS OF ANY KIND, either express or implied. +// $$$$$$$$ +// $$$$$$$$$$$$$$$$$$ See the License for the specific language governing +// $$$$$$$$$$$$$ permissions and limitations under the License. +// $$$$$$$ +// $$ +// $$$$$ $$$$$ $$ $ +// $$$ $$$ $$$ $$ $$$$$ $$ $$$ $$$$ $$$$$$$ $$$$ $$$ $$$$$$ $$ $$$$$$ +// $$$ $$$ $$$ $$ $$$ $$$ $$$ $ $$ $$ $$ $$ $$ $$ $$$ $$$ +// $$$$$$$$$$$ $$ $$ $$$ $$ $$ $$$$$$$ $$ $$ $$ $$$ $$ $$ +// $$$ $$$ $$$$ $$$ $$ $$ $$$ $$ $$ $$ $$ $$ $$ $$ +// $$$$$ $$$$$ $$ $$$$$$$$ $ $$$ $$$$$$$$ $$$ $$$$ $$$$$$$ $$$$ $$$$ +// $$$ + +//! Track configurations for governance. + +use super::*; +use primitives::constants::{ + currency::UNITS, + time::{HOURS, MINUTES}, +}; +const fn percent(x: i32) -> sp_arithmetic::FixedI64 { + sp_arithmetic::FixedI64::from_rational(x as u128, 100) +} + +use pallet_referenda::Curve; +const APP_LINEAR: Curve = Curve::make_linear(7, 7, percent(50), percent(100)); +const APP_LINEAR_FLAT: Curve = Curve::make_linear(4, 7, percent(50), percent(100)); +const APP_RECIP: Curve = Curve::make_reciprocal(1, 7, percent(80), percent(50), percent(100)); +const SUP_LINEAR: Curve = Curve::make_linear(7, 7, percent(0), percent(50)); +const SUP_RECIP: Curve = Curve::make_reciprocal(5, 7, percent(1), percent(0), percent(50)); +const SUP_FAST_RECIP: Curve = Curve::make_reciprocal(3, 7, percent(1), percent(0), percent(50)); +const SUP_WHITELISTED_CALLER: Curve = Curve::make_reciprocal(1, 28, percent(3), percent(2), percent(50)); + +const TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 9] = [ + ( + 0, + pallet_referenda::TrackInfo { + name: "root", + max_deciding: 1, + decision_deposit: 5_000_000 * UNITS, + prepare_period: HOURS, + decision_period: 7 * DAYS, + confirm_period: 24 * HOURS, + min_enactment_period: 4 * HOURS, + min_approval: APP_RECIP, + min_support: SUP_LINEAR, + }, + ), + ( + 1, + pallet_referenda::TrackInfo { + name: "whitelisted_caller", + max_deciding: 3, + decision_deposit: 50_000 * UNITS, + prepare_period: 10 * MINUTES, + decision_period: 3 * DAYS, + confirm_period: 4 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: APP_RECIP, + min_support: SUP_WHITELISTED_CALLER, + }, + ), + ( + 2, + pallet_referenda::TrackInfo { + name: "referendum_canceller", + max_deciding: 3, + decision_deposit: 500_000 * UNITS, + prepare_period: 60 * MINUTES, + decision_period: 3 * DAYS, + confirm_period: 60 * MINUTES, + min_enactment_period: 10 * MINUTES, + min_approval: APP_LINEAR_FLAT, + min_support: SUP_FAST_RECIP, + }, + ), + ( + 3, + pallet_referenda::TrackInfo { + name: "referendum_killer", + max_deciding: 3, + decision_deposit: 2_500_000 * UNITS, + prepare_period: 60 * MINUTES, + decision_period: 3 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: APP_LINEAR_FLAT, + min_support: SUP_FAST_RECIP, + }, + ), + ( + 4, + pallet_referenda::TrackInfo { + name: "general_admin", + max_deciding: 3, + decision_deposit: 500_000 * UNITS, + prepare_period: 60 * MINUTES, + decision_period: 7 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: APP_RECIP, + min_support: SUP_RECIP, + }, + ), + ( + 5, + pallet_referenda::TrackInfo { + name: "treasurer", + max_deciding: 3, + decision_deposit: 1_250_000 * UNITS, + prepare_period: 60 * MINUTES, + decision_period: 7 * DAYS, + confirm_period: 12 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: APP_RECIP, + min_support: SUP_LINEAR, + }, + ), + ( + 6, + pallet_referenda::TrackInfo { + name: "spender", + max_deciding: 3, + decision_deposit: 100_000 * UNITS, + prepare_period: 60 * MINUTES, + decision_period: 7 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: APP_LINEAR, + min_support: SUP_RECIP, + }, + ), + ( + 7, + pallet_referenda::TrackInfo { + name: "tipper", + max_deciding: 3, + decision_deposit: 10_000 * UNITS, + prepare_period: 60 * MINUTES, + decision_period: 7 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: APP_LINEAR_FLAT, + min_support: SUP_FAST_RECIP, + }, + ), + ( + 8, + pallet_referenda::TrackInfo { + name: "omnipool_admin", + max_deciding: 3, + decision_deposit: 500_000 * UNITS, + prepare_period: 60 * MINUTES, + decision_period: 7 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: APP_RECIP, + min_support: SUP_RECIP, + }, + ), +]; + +pub struct TracksInfo; +impl pallet_referenda::TracksInfo for TracksInfo { + type Id = u16; + type RuntimeOrigin = ::PalletsOrigin; + fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo)] { + &TRACKS_DATA[..] + } + fn track_for(id: &Self::RuntimeOrigin) -> Result { + if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => Ok(0), + _ => Err(()), + } + } else if let Ok(custom_origin) = origins::Origin::try_from(id.clone()) { + match custom_origin { + origins::Origin::WhitelistedCaller => Ok(1), + origins::Origin::ReferendumCanceller => Ok(2), + origins::Origin::ReferendumKiller => Ok(3), + origins::Origin::GeneralAdmin => Ok(4), + origins::Origin::Treasurer => Ok(5), + origins::Origin::Spender => Ok(6), + origins::Origin::Tipper => Ok(7), + origins::Origin::OmnipoolAdmin => Ok(8), + } + } else { + Err(()) + } + } +} +pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index 334e65987..a33ed2a2d 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -34,12 +34,13 @@ pub mod weights; pub mod apis; mod assets; pub mod evm; -mod governance; +pub mod governance; mod system; pub mod types; pub mod xcm; pub use assets::*; +pub use governance::origins::pallet_custom_origins; pub use governance::*; pub use system::*; pub use xcm::*; @@ -108,7 +109,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 261, + spec_version: 262, impl_version: 0, apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, @@ -158,6 +159,12 @@ construct_runtime!( Uniques: pallet_uniques = 32, StateTrieMigration: pallet_state_trie_migration = 35, + // OpenGov + ConvictionVoting: pallet_conviction_voting = 36, + Referenda: pallet_referenda = 37, + Origins: pallet_custom_origins = 38, + Whitelist: pallet_whitelist = 39, + // HydraDX related modules AssetRegistry: pallet_asset_registry = 51, Claims: pallet_claims = 53, @@ -318,6 +325,9 @@ mod benches { [cumulus_pallet_parachain_system, ParachainSystem] [pallet_collator_selection, CollatorSelection] [pallet_xcm, PalletXcmExtrinsiscsBenchmark::] + [pallet_conviction_voting, ConvictionVoting] + [pallet_referenda, Referenda] + [pallet_whitelist, Whitelist] ); } diff --git a/runtime/hydradx/src/system.rs b/runtime/hydradx/src/system.rs index f0da22297..a2403e600 100644 --- a/runtime/hydradx/src/system.rs +++ b/runtime/hydradx/src/system.rs @@ -17,6 +17,7 @@ use super::*; +use crate::origins::GeneralAdmin; use pallet_transaction_multi_payment::{DepositAll, TransferFees, WeightInfo}; use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; use primitives::constants::{ @@ -26,6 +27,7 @@ use primitives::constants::{ }; use codec::{Decode, Encode, MaxEncodedLen}; +use core::cmp::Ordering; use frame_support::{ dispatch::DispatchClass, parameter_types, @@ -33,7 +35,10 @@ use frame_support::{ traits::{ConstU32, ConstU64, IdentityLookup}, FixedPointNumber, Perbill, Perquintill, RuntimeDebug, }, - traits::{ConstBool, Contains, InstanceFilter, SortedMembers}, + traits::{ + fungible::HoldConsideration, ConstBool, Contains, EitherOf, InstanceFilter, LinearStoragePrice, PrivilegeCmp, + SortedMembers, + }, weights::{ constants::{BlockExecutionWeight, RocksDbWeight}, ConstantMultiplier, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, @@ -49,7 +54,14 @@ impl Contains for CallFilter { fn contains(call: &RuntimeCall) -> bool { if matches!( call, - RuntimeCall::System(_) | RuntimeCall::Timestamp(_) | RuntimeCall::ParachainSystem(_) + RuntimeCall::System(_) + | RuntimeCall::ConvictionVoting(_) + | RuntimeCall::Timestamp(_) + | RuntimeCall::ParachainSystem(_) + | RuntimeCall::Preimage(_) + | RuntimeCall::Referenda(_) + | RuntimeCall::TransactionPause(_) + | RuntimeCall::Whitelist(_) ) { // always allow // Note: this is done to avoid unnecessary check of paused storage. @@ -265,7 +277,7 @@ parameter_types! { impl pallet_collator_selection::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type UpdateOrigin = MoreThanHalfCouncil; + type UpdateOrigin = EitherOf, GeneralAdmin>; type PotId = PotId; #[cfg(not(feature = "runtime-benchmarks"))] type MaxCandidates = MaxCandidates; @@ -281,6 +293,65 @@ impl pallet_collator_selection::Config for Runtime { type MinEligibleCollators = ConstU32<4>; } +parameter_types! { + pub PreimageBaseDeposit: Balance = deposit(2, 64); + pub PreimageByteDeposit: Balance = deposit(0, 1); + pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage); +} + +impl pallet_preimage::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_preimage::HydraWeight; + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = HoldConsideration< + AccountId, + Balances, + PreimageHoldReason, + LinearStoragePrice, + >; +} + +parameter_types! { + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; + pub const MaxScheduledPerBlock: u32 = 50; +} +impl pallet_scheduler::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EitherOf, GeneralAdmin>; + type OriginPrivilegeCmp = OriginPrivilegeCmp; + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type WeightInfo = weights::pallet_scheduler::HydraWeight; + type Preimages = Preimage; +} + +/// Used the compare the privilege of an origin inside the scheduler. +pub struct OriginPrivilegeCmp; + +impl PrivilegeCmp for OriginPrivilegeCmp { + fn cmp_privilege(left: &OriginCaller, right: &OriginCaller) -> Option { + if left == right { + return Some(Ordering::Equal); + } + + match (left, right) { + // Root is greater than anything. + (OriginCaller::system(frame_system::RawOrigin::Root), _) => Some(Ordering::Greater), + // Check which one has more yes votes. + ( + OriginCaller::Council(pallet_collective::RawOrigin::Members(l_yes_votes, l_count)), + OriginCaller::Council(pallet_collective::RawOrigin::Members(r_yes_votes, r_count)), + ) => Some((l_yes_votes * r_count).cmp(&(r_yes_votes * l_count))), + // For every other origin we don't care, as they are not used for `ScheduleOrigin`. + _ => None, + } + } +} + parameter_types! { pub const Period: u32 = 4 * HOURS; pub const Offset: u32 = 0; @@ -330,8 +401,8 @@ impl pallet_identity::Config for Runtime { type IdentityInformation = pallet_identity::legacy::IdentityInfo; type MaxRegistrars = MaxRegistrars; type Slashed = Treasury; - type ForceOrigin = MoreThanHalfCouncil; - type RegistrarOrigin = MoreThanHalfCouncil; + type ForceOrigin = EitherOf, GeneralAdmin>; + type RegistrarOrigin = EitherOf, GeneralAdmin>; type OffchainSignature = Signature; type SigningPublicKey = ::Signer; type UsernameAuthorityOrigin = EnsureRoot; @@ -507,7 +578,7 @@ impl pallet_transaction_payment::Config for Runtime { impl pallet_transaction_multi_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type AcceptedCurrencyOrigin = SuperMajorityTechCommittee; + type AcceptedCurrencyOrigin = EitherOf, GeneralAdmin>; type Currencies = Currencies; type RouteProvider = Router; type OraclePriceProvider = OraclePriceProvider; @@ -560,7 +631,7 @@ impl pallet_collator_rewards::Config for Runtime { impl pallet_transaction_pause::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type UpdateOrigin = SuperMajorityTechCommittee; + type UpdateOrigin = EitherOf, EitherOf>; type WeightInfo = weights::pallet_transaction_pause::HydraWeight; } @@ -579,7 +650,7 @@ parameter_types! { } impl pallet_state_trie_migration::Config for Runtime { - type ControlOrigin = SuperMajorityTechCommittee; + type ControlOrigin = EnsureRoot; #[cfg(not(feature = "runtime-benchmarks"))] type SignedFilter = frame_system::EnsureSignedBy; #[cfg(feature = "runtime-benchmarks")] diff --git a/runtime/hydradx/src/weights/mod.rs b/runtime/hydradx/src/weights/mod.rs index 46314002c..c15fda1c3 100644 --- a/runtime/hydradx/src/weights/mod.rs +++ b/runtime/hydradx/src/weights/mod.rs @@ -11,6 +11,7 @@ pub mod pallet_claims; pub mod pallet_collator_selection; pub mod pallet_collective_council; pub mod pallet_collective_technical_committee; +pub mod pallet_conviction_voting; pub mod pallet_currencies; pub mod pallet_dca; pub mod pallet_democracy; @@ -29,6 +30,7 @@ pub mod pallet_otc; pub mod pallet_otc_settlements; pub mod pallet_preimage; pub mod pallet_proxy; +pub mod pallet_referenda; pub mod pallet_referrals; pub mod pallet_route_executor; pub mod pallet_scheduler; @@ -41,6 +43,7 @@ pub mod pallet_transaction_multi_payment; pub mod pallet_transaction_pause; pub mod pallet_treasury; pub mod pallet_utility; +pub mod pallet_whitelist; pub mod pallet_xcm; pub mod pallet_xyk; pub mod pallet_xyk_liquidity_mining; diff --git a/runtime/hydradx/src/weights/pallet_conviction_voting.rs b/runtime/hydradx/src/weights/pallet_conviction_voting.rs new file mode 100644 index 000000000..8beff3391 --- /dev/null +++ b/runtime/hydradx/src/weights/pallet_conviction_voting.rs @@ -0,0 +1,218 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//! Autogenerated weights for `pallet_conviction_voting` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-07-31, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/release/hydradx +// benchmark +// pallet +// --chain=dev +// --steps=10 +// --repeat=30 +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=scripts/pallet-weight-template.hbs +// --pallet=pallet-conviction-voting +// --output=pallet_conviction_voting.rs +// --extrinsic=* + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weights for `pallet_conviction_voting`. +pub struct WeightInfo(PhantomData); + +/// Weights for `pallet_conviction_voting` using the HydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl pallet_conviction_voting::WeightInfo for HydraWeight { + /// Storage: `Referenda::ReferendumInfoFor` (r:25 w:25) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(203), added: 2678, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:2 w:0) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Staking::Positions` (r:1 w:1) + /// Proof: `Staking::Positions` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `Staking::PositionVotes` (r:1 w:1) + /// Proof: `Staking::PositionVotes` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn vote_new() -> Weight { + // Proof Size summary in bytes: + // Measured: `12365` + // Estimated: `86265` + // Minimum execution time: 867_120_000 picoseconds. + Weight::from_parts(871_610_000, 86265) + .saturating_add(T::DbWeight::get().reads(35_u64)) + .saturating_add(T::DbWeight::get().writes(32_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:25 w:25) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(203), added: 2678, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:2 w:0) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Staking::Positions` (r:1 w:1) + /// Proof: `Staking::Positions` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `Staking::PositionVotes` (r:1 w:1) + /// Proof: `Staking::PositionVotes` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn vote_existing() -> Weight { + // Proof Size summary in bytes: + // Measured: `12434` + // Estimated: `86265` + // Minimum execution time: 900_865_000 picoseconds. + Weight::from_parts(906_943_000, 86265) + .saturating_add(T::DbWeight::get().reads(35_u64)) + .saturating_add(T::DbWeight::get().writes(32_u64)) + } + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:2 w:0) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Staking::ProcessedVotes` (r:1 w:0) + /// Proof: `Staking::ProcessedVotes` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `Staking::Positions` (r:1 w:1) + /// Proof: `Staking::Positions` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + /// Storage: `Staking::PositionVotes` (r:1 w:1) + /// Proof: `Staking::PositionVotes` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn remove_vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `3710` + // Estimated: `83866` + // Minimum execution time: 100_301_000 picoseconds. + Weight::from_parts(101_319_000, 83866) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Uniques::Account` (r:2 w:0) + /// Proof: `Uniques::Account` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`) + /// Storage: `Staking::ProcessedVotes` (r:1 w:0) + /// Proof: `Staking::ProcessedVotes` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `Staking::PositionVotes` (r:1 w:1) + /// Proof: `Staking::PositionVotes` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`) + /// Storage: `Staking::Positions` (r:1 w:1) + /// Proof: `Staking::Positions` (`max_values`: None, `max_size`: Some(132), added: 2607, mode: `MaxEncodedLen`) + fn remove_other_vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `3184` + // Estimated: `6164` + // Minimum execution time: 75_474_000 picoseconds. + Weight::from_parts(76_658_000, 6164) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:3 w:3) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(203), added: 2678, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + /// The range of component `r` is `[0, 3]`. + fn delegate(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `216 + r * (945 ±0)` + // Estimated: `83866 + r * (3411 ±0)` + // Minimum execution time: 47_619_000 picoseconds. + Weight::from_parts(53_590_250, 83866) + // Standard Error: 447_289 + .saturating_add(Weight::from_parts(29_376_750, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 3411).saturating_mul(r.into())) + } + /// Storage: `ConvictionVoting::VotingFor` (r:2 w:2) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:3 w:3) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// The range of component `r` is `[0, 3]`. + fn undelegate(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `444 + r * (808 ±0)` + // Estimated: `83866 + r * (3411 ±0)` + // Minimum execution time: 24_546_000 picoseconds. + Weight::from_parts(28_692_195, 83866) + // Standard Error: 331_935 + .saturating_add(Weight::from_parts(27_465_902, 0).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r.into()))) + .saturating_add(Weight::from_parts(0, 3411).saturating_mul(r.into())) + } + /// Storage: `ConvictionVoting::VotingFor` (r:1 w:1) + /// Proof: `ConvictionVoting::VotingFor` (`max_values`: None, `max_size`: Some(1429), added: 3904, mode: `MaxEncodedLen`) + /// Storage: `ConvictionVoting::ClassLocksFor` (r:1 w:1) + /// Proof: `ConvictionVoting::ClassLocksFor` (`max_values`: None, `max_size`: Some(203), added: 2678, mode: `MaxEncodedLen`) + /// Storage: `Balances::Locks` (r:1 w:1) + /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`) + /// Storage: `Balances::Freezes` (r:1 w:0) + /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) + fn unlock() -> Weight { + // Proof Size summary in bytes: + // Measured: `1406` + // Estimated: `4894` + // Minimum execution time: 47_369_000 picoseconds. + Weight::from_parts(48_332_000, 4894) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} \ No newline at end of file diff --git a/runtime/hydradx/src/weights/pallet_referenda.rs b/runtime/hydradx/src/weights/pallet_referenda.rs new file mode 100644 index 000000000..fc0c44d0d --- /dev/null +++ b/runtime/hydradx/src/weights/pallet_referenda.rs @@ -0,0 +1,499 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//! Autogenerated weights for `pallet_referenda` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-07-30, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/release/hydradx +// benchmark +// pallet +// --chain=dev +// --steps=10 +// --repeat=30 +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=scripts/pallet-weight-template.hbs +// --pallet=pallet-referenda +// --output=pallet_referenda.rs +// --extrinsic=* + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weights for `pallet_referenda`. +pub struct WeightInfo(PhantomData); + +/// Weights for `pallet_referenda` using the HydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl pallet_referenda::WeightInfo for HydraWeight { + /// Storage: `Referenda::ReferendumCount` (r:1 w:1) + /// Proof: `Referenda::ReferendumCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:0 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + fn submit() -> Weight { + // Proof Size summary in bytes: + // Measured: `258` + // Estimated: `42428` + // Minimum execution time: 42_788_000 picoseconds. + Weight::from_parts(43_444_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_preparing() -> Weight { + // Proof Size summary in bytes: + // Measured: `511` + // Estimated: `83866` + // Minimum execution time: 54_709_000 picoseconds. + Weight::from_parts(55_371_000, 83866) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_queued() -> Weight { + // Proof Size summary in bytes: + // Measured: `3298` + // Estimated: `42428` + // Minimum execution time: 65_002_000 picoseconds. + Weight::from_parts(65_880_000, 42428) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_not_queued() -> Weight { + // Proof Size summary in bytes: + // Measured: `3318` + // Estimated: `42428` + // Minimum execution time: 64_442_000 picoseconds. + Weight::from_parts(65_228_000, 42428) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_passing() -> Weight { + // Proof Size summary in bytes: + // Measured: `511` + // Estimated: `83866` + // Minimum execution time: 65_625_000 picoseconds. + Weight::from_parts(66_128_000, 83866) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn place_decision_deposit_failing() -> Weight { + // Proof Size summary in bytes: + // Measured: `511` + // Estimated: `83866` + // Minimum execution time: 64_713_000 picoseconds. + Weight::from_parts(65_366_000, 83866) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + fn refund_decision_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `389` + // Estimated: `4401` + // Minimum execution time: 34_880_000 picoseconds. + Weight::from_parts(35_186_000, 4401) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + fn refund_submission_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `379` + // Estimated: `4401` + // Minimum execution time: 34_863_000 picoseconds. + Weight::from_parts(35_309_000, 4401) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn cancel() -> Weight { + // Proof Size summary in bytes: + // Measured: `419` + // Estimated: `83866` + // Minimum execution time: 38_964_000 picoseconds. + Weight::from_parts(39_306_000, 83866) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:1 w:0) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn kill() -> Weight { + // Proof Size summary in bytes: + // Measured: `763` + // Estimated: `83866` + // Minimum execution time: 105_099_000 picoseconds. + Weight::from_parts(105_893_000, 83866) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Referenda::TrackQueue` (r:1 w:0) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + fn one_fewer_deciding_queue_empty() -> Weight { + // Proof Size summary in bytes: + // Measured: `212` + // Estimated: `5477` + // Minimum execution time: 13_356_000 picoseconds. + Weight::from_parts(13_548_000, 5477) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn one_fewer_deciding_failing() -> Weight { + // Proof Size summary in bytes: + // Measured: `3188` + // Estimated: `42428` + // Minimum execution time: 44_751_000 picoseconds. + Weight::from_parts(45_406_000, 42428) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn one_fewer_deciding_passing() -> Weight { + // Proof Size summary in bytes: + // Measured: `3188` + // Estimated: `42428` + // Minimum execution time: 47_122_000 picoseconds. + Weight::from_parts(47_714_000, 42428) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + fn nudge_referendum_requeued_insertion() -> Weight { + // Proof Size summary in bytes: + // Measured: `3049` + // Estimated: `5477` + // Minimum execution time: 22_886_000 picoseconds. + Weight::from_parts(23_180_000, 5477) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + fn nudge_referendum_requeued_slide() -> Weight { + // Proof Size summary in bytes: + // Measured: `3049` + // Estimated: `5477` + // Minimum execution time: 23_174_000 picoseconds. + Weight::from_parts(23_374_000, 5477) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + fn nudge_referendum_queued() -> Weight { + // Proof Size summary in bytes: + // Measured: `3053` + // Estimated: `5477` + // Minimum execution time: 27_949_000 picoseconds. + Weight::from_parts(28_365_000, 5477) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:0) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Referenda::TrackQueue` (r:1 w:1) + /// Proof: `Referenda::TrackQueue` (`max_values`: None, `max_size`: Some(2012), added: 4487, mode: `MaxEncodedLen`) + fn nudge_referendum_not_queued() -> Weight { + // Proof Size summary in bytes: + // Measured: `3073` + // Estimated: `5477` + // Minimum execution time: 27_720_000 picoseconds. + Weight::from_parts(28_069_000, 5477) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_no_deposit() -> Weight { + // Proof Size summary in bytes: + // Measured: `371` + // Estimated: `42428` + // Minimum execution time: 27_309_000 picoseconds. + Weight::from_parts(27_680_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_preparing() -> Weight { + // Proof Size summary in bytes: + // Measured: `419` + // Estimated: `42428` + // Minimum execution time: 27_553_000 picoseconds. + Weight::from_parts(28_228_000, 42428) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + fn nudge_referendum_timed_out() -> Weight { + // Proof Size summary in bytes: + // Measured: `316` + // Estimated: `4401` + // Minimum execution time: 18_789_000 picoseconds. + Weight::from_parts(19_190_000, 4401) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_begin_deciding_failing() -> Weight { + // Proof Size summary in bytes: + // Measured: `419` + // Estimated: `42428` + // Minimum execution time: 36_770_000 picoseconds. + Weight::from_parts(37_417_000, 42428) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::DecidingCount` (r:1 w:1) + /// Proof: `Referenda::DecidingCount` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_begin_deciding_passing() -> Weight { + // Proof Size summary in bytes: + // Measured: `419` + // Estimated: `42428` + // Minimum execution time: 38_288_000 picoseconds. + Weight::from_parts(38_837_000, 42428) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_begin_confirming() -> Weight { + // Proof Size summary in bytes: + // Measured: `472` + // Estimated: `42428` + // Minimum execution time: 31_243_000 picoseconds. + Weight::from_parts(31_697_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_end_confirming() -> Weight { + // Proof Size summary in bytes: + // Measured: `455` + // Estimated: `42428` + // Minimum execution time: 31_941_000 picoseconds. + Weight::from_parts(32_301_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_continue_not_confirming() -> Weight { + // Proof Size summary in bytes: + // Measured: `472` + // Estimated: `42428` + // Minimum execution time: 30_425_000 picoseconds. + Weight::from_parts(30_923_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_continue_confirming() -> Weight { + // Proof Size summary in bytes: + // Measured: `476` + // Estimated: `42428` + // Minimum execution time: 29_395_000 picoseconds. + Weight::from_parts(29_718_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:2 w:2) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Lookup` (r:1 w:1) + /// Proof: `Scheduler::Lookup` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`) + fn nudge_referendum_approved() -> Weight { + // Proof Size summary in bytes: + // Measured: `476` + // Estimated: `83866` + // Minimum execution time: 43_572_000 picoseconds. + Weight::from_parts(44_054_000, 83866) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:1) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Balances::InactiveIssuance` (r:1 w:0) + /// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `Scheduler::Agenda` (r:1 w:1) + /// Proof: `Scheduler::Agenda` (`max_values`: None, `max_size`: Some(38963), added: 41438, mode: `MaxEncodedLen`) + fn nudge_referendum_rejected() -> Weight { + // Proof Size summary in bytes: + // Measured: `472` + // Estimated: `42428` + // Minimum execution time: 31_209_000 picoseconds. + Weight::from_parts(31_680_000, 42428) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:0) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:0 w:1) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn set_some_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `527` + // Estimated: `4401` + // Minimum execution time: 25_585_000 picoseconds. + Weight::from_parts(26_017_000, 4401) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `Referenda::ReferendumInfoFor` (r:1 w:0) + /// Proof: `Referenda::ReferendumInfoFor` (`max_values`: None, `max_size`: Some(936), added: 3411, mode: `MaxEncodedLen`) + /// Storage: `Referenda::MetadataOf` (r:1 w:1) + /// Proof: `Referenda::MetadataOf` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + fn clear_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `393` + // Estimated: `4401` + // Minimum execution time: 20_838_000 picoseconds. + Weight::from_parts(21_218_000, 4401) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/runtime/hydradx/src/weights/pallet_whitelist.rs b/runtime/hydradx/src/weights/pallet_whitelist.rs new file mode 100644 index 000000000..df3e87f86 --- /dev/null +++ b/runtime/hydradx/src/weights/pallet_whitelist.rs @@ -0,0 +1,124 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2023 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//! Autogenerated weights for `pallet_whitelist` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-07-30, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// target/release/hydradx +// benchmark +// pallet +// --chain=dev +// --steps=10 +// --repeat=30 +// --wasm-execution=compiled +// --heap-pages=4096 +// --template=scripts/pallet-weight-template.hbs +// --pallet=pallet-whitelist +// --output=pallet_whitelist.rs +// --extrinsic=* + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weights for `pallet_whitelist`. +pub struct WeightInfo(PhantomData); + +/// Weights for `pallet_whitelist` using the HydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl pallet_whitelist::WeightInfo for HydraWeight { + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + fn whitelist_call() -> Weight { + // Proof Size summary in bytes: + // Measured: `218` + // Estimated: `3556` + // Minimum execution time: 23_242_000 picoseconds. + Weight::from_parts(23_861_000, 3556) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + fn remove_whitelisted_call() -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `3556` + // Minimum execution time: 21_663_000 picoseconds. + Weight::from_parts(22_035_000, 3556) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::PreimageFor` (r:1 w:1) + /// Proof: `Preimage::PreimageFor` (`max_values`: None, `max_size`: Some(4194344), added: 4196819, mode: `Measured`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 4194294]`. + fn dispatch_whitelisted_call(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `423 + n * (1 ±0)` + // Estimated: `3884 + n * (1 ±0)` + // Minimum execution time: 34_811_000 picoseconds. + Weight::from_parts(35_117_000, 3884) + // Standard Error: 10 + .saturating_add(Weight::from_parts(1_334, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Whitelist::WhitelistedCall` (r:1 w:1) + /// Proof: `Whitelist::WhitelistedCall` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`) + /// Storage: `Preimage::StatusFor` (r:1 w:0) + /// Proof: `Preimage::StatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// Storage: `Preimage::RequestStatusFor` (r:1 w:1) + /// Proof: `Preimage::RequestStatusFor` (`max_values`: None, `max_size`: Some(91), added: 2566, mode: `MaxEncodedLen`) + /// The range of component `n` is `[1, 10000]`. + fn dispatch_whitelisted_call_with_preimage(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `347` + // Estimated: `3556` + // Minimum execution time: 27_266_000 picoseconds. + Weight::from_parts(27_786_147, 3556) + // Standard Error: 4 + .saturating_add(Weight::from_parts(1_216, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} \ No newline at end of file diff --git a/runtime/hydradx/src/xcm.rs b/runtime/hydradx/src/xcm.rs index bd1af1b6d..222c01a74 100644 --- a/runtime/hydradx/src/xcm.rs +++ b/runtime/hydradx/src/xcm.rs @@ -1,18 +1,21 @@ use super::*; +use crate::origins::GeneralAdmin; +use sp_std::marker::PhantomData; + use codec::MaxEncodedLen; use hydradx_adapters::{MultiCurrencyTrader, ReroutingMultiCurrencyAdapter, ToFeeReceiver}; use pallet_transaction_multi_payment::DepositAll; use primitives::{AssetId, Price}; -use sp_std::marker::PhantomData; // shadow glob import of polkadot_xcm::v3::prelude::AssetId use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; use frame_support::{ parameter_types, sp_runtime::traits::{AccountIdConversion, Convert}, - traits::{ConstU32, Contains, ContainsPair, Everything, Get, Nothing, TransformOrigin}, + traits::{ConstU32, Contains, ContainsPair, EitherOf, Everything, Get, Nothing, TransformOrigin}, PalletId, }; +use frame_system::EnsureRoot; use hydradx_adapters::{xcm_exchange::XcmAssetExchanger, xcm_execute_filter::AllowTransferAndSwap}; use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key}; use orml_xcm_support::{DepositToAlternative, IsNativeConcrete, MultiNativeAsset}; @@ -228,7 +231,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ChannelInfo = ParachainSystem; type VersionWrapper = PolkadotXcm; - type ControllerOrigin = MoreThanHalfTechCommittee; + type ControllerOrigin = EitherOf, EitherOf>; type ControllerOriginConverter = XcmOriginToCallOrigin; type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; type WeightInfo = weights::cumulus_pallet_xcmp_queue::HydraWeight; @@ -273,7 +276,7 @@ impl orml_unknown_tokens::Config for Runtime { impl orml_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type SovereignOrigin = MoreThanHalfCouncil; + type SovereignOrigin = EnsureRoot; } impl pallet_xcm::Config for Runtime { @@ -297,7 +300,7 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = (); type MaxLockers = ConstU32<8>; type WeightInfo = weights::pallet_xcm::HydraWeight; - type AdminOrigin = MajorityOfCouncil; + type AdminOrigin = EitherOf, EitherOf>; type MaxRemoteLockConsumers = ConstU32<0>; type RemoteLockConsumerIdentifier = (); }