Migrate pallet-treasury to the new pallet attribute macro (#9197)

* Migrate pallet-treasury to the new pallet attribute macro

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Fix bounties/tips tests

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* fix

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Update frame/treasury/src/lib.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* Update frame/treasury/src/lib.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* use `GenesisBuild`

* fix imports

Co-authored-by: thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Qinxuan Chen
2021-07-13 15:26:58 +08:00
committed by GitHub
parent e01ac8cea0
commit e256877eb0
10 changed files with 278 additions and 241 deletions
+3 -3
View File
@@ -363,7 +363,7 @@ fn full_native_block_import_works() {
}, },
EventRecord { EventRecord {
phase: Phase::ApplyExtrinsic(1), phase: Phase::ApplyExtrinsic(1),
event: Event::Treasury(pallet_treasury::RawEvent::Deposit(fees * 8 / 10)), event: Event::Treasury(pallet_treasury::Event::Deposit(fees * 8 / 10)),
topics: vec![], topics: vec![],
}, },
EventRecord { EventRecord {
@@ -417,7 +417,7 @@ fn full_native_block_import_works() {
}, },
EventRecord { EventRecord {
phase: Phase::ApplyExtrinsic(1), phase: Phase::ApplyExtrinsic(1),
event: Event::Treasury(pallet_treasury::RawEvent::Deposit(fees * 8 / 10)), event: Event::Treasury(pallet_treasury::Event::Deposit(fees * 8 / 10)),
topics: vec![], topics: vec![],
}, },
EventRecord { EventRecord {
@@ -440,7 +440,7 @@ fn full_native_block_import_works() {
}, },
EventRecord { EventRecord {
phase: Phase::ApplyExtrinsic(2), phase: Phase::ApplyExtrinsic(2),
event: Event::Treasury(pallet_treasury::RawEvent::Deposit(fees * 8 / 10)), event: Event::Treasury(pallet_treasury::Event::Deposit(fees * 8 / 10)),
topics: vec![], topics: vec![],
}, },
EventRecord { EventRecord {
+1 -1
View File
@@ -27,7 +27,7 @@ use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark
use frame_support::traits::OnInitialize; use frame_support::traits::OnInitialize;
use crate::Module as Bounties; use crate::Module as Bounties;
use pallet_treasury::Module as Treasury; use pallet_treasury::Pallet as Treasury;
const SEED: u32 = 0; const SEED: u32 = 0;
+6 -6
View File
@@ -25,7 +25,7 @@ use std::cell::RefCell;
use frame_support::{ use frame_support::{
assert_noop, assert_ok, parameter_types, weights::Weight, traits::OnInitialize, assert_noop, assert_ok, parameter_types, weights::Weight, traits::OnInitialize,
PalletId PalletId, pallet_prelude::GenesisBuild,
}; };
use sp_core::H256; use sp_core::H256;
@@ -146,7 +146,7 @@ impl Config for Test {
type WeightInfo = (); type WeightInfo = ();
} }
type TreasuryError = pallet_treasury::Error::<Test, pallet_treasury::DefaultInstance>; type TreasuryError = pallet_treasury::Error::<Test>;
pub fn new_test_ext() -> sp_io::TestExternalities { pub fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap(); let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
@@ -154,7 +154,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
// Total issuance will be 200 with treasury account initialized at ED. // Total issuance will be 200 with treasury account initialized at ED.
balances: vec![(0, 100), (1, 98), (2, 1)], balances: vec![(0, 100), (1, 98), (2, 1)],
}.assimilate_storage(&mut t).unwrap(); }.assimilate_storage(&mut t).unwrap();
pallet_treasury::GenesisConfig::default().assimilate_storage::<Test, _>(&mut t).unwrap(); GenesisBuild::<Test>::assimilate_storage(&pallet_treasury::GenesisConfig, &mut t).unwrap();
t.into() t.into()
} }
@@ -268,7 +268,7 @@ fn reject_already_rejected_spend_proposal_fails() {
fn reject_non_existent_spend_proposal_fails() { fn reject_non_existent_spend_proposal_fails() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
assert_noop!(Treasury::reject_proposal(Origin::root(), 0), assert_noop!(Treasury::reject_proposal(Origin::root(), 0),
pallet_treasury::Error::<Test, pallet_treasury::DefaultInstance>::InvalidIndex); pallet_treasury::Error::<Test, _>::InvalidIndex);
}); });
} }
@@ -457,7 +457,7 @@ fn close_bounty_works() {
assert_eq!(Balances::free_balance(0), 100 - deposit); assert_eq!(Balances::free_balance(0), 100 - deposit);
assert_eq!(Bounties::bounties(0), None); assert_eq!(Bounties::bounties(0), None);
assert!(!pallet_treasury::Proposals::<Test>::contains_key(0)); assert!(!pallet_treasury::Proposals::<Test, _>::contains_key(0));
assert_eq!(Bounties::bounty_descriptions(0), None); assert_eq!(Bounties::bounty_descriptions(0), None);
}); });
@@ -897,7 +897,7 @@ fn genesis_funding_works() {
// Total issuance will be 200 with treasury account initialized with 100. // Total issuance will be 200 with treasury account initialized with 100.
balances: vec![(0, 100), (Treasury::account_id(), initial_funding)], balances: vec![(0, 100), (Treasury::account_id(), initial_funding)],
}.assimilate_storage(&mut t).unwrap(); }.assimilate_storage(&mut t).unwrap();
pallet_treasury::GenesisConfig::default().assimilate_storage::<Test, _>(&mut t).unwrap(); GenesisBuild::<Test>::assimilate_storage(&pallet_treasury::GenesisConfig, &mut t).unwrap();
let mut t: sp_io::TestExternalities = t.into(); let mut t: sp_io::TestExternalities = t.into();
t.execute_with(|| { t.execute_with(|| {
+1 -1
View File
@@ -501,7 +501,7 @@ impl<T: Config> Module<T> {
tips.sort_by_key(|i| i.1); tips.sort_by_key(|i| i.1);
let treasury = Self::account_id(); let treasury = Self::account_id();
let max_payout = pallet_treasury::Module::<T>::pot(); let max_payout = pallet_treasury::Pallet::<T>::pot();
let mut payout = tips[tips.len() / 2].1.min(max_payout); let mut payout = tips[tips.len() / 2].1.min(max_payout);
if !tip.deposit.is_zero() { if !tip.deposit.is_zero() {
+3 -3
View File
@@ -25,7 +25,7 @@ use std::cell::RefCell;
use frame_support::{ use frame_support::{
assert_noop, assert_ok, parameter_types, assert_noop, assert_ok, parameter_types,
weights::Weight, traits::SortedMembers, weights::Weight, traits::SortedMembers,
PalletId PalletId, pallet_prelude::GenesisBuild,
}; };
use sp_runtime::Permill; use sp_runtime::Permill;
use sp_core::H256; use sp_core::H256;
@@ -169,7 +169,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
// Total issuance will be 200 with treasury account initialized at ED. // Total issuance will be 200 with treasury account initialized at ED.
balances: vec![(0, 100), (1, 98), (2, 1)], balances: vec![(0, 100), (1, 98), (2, 1)],
}.assimilate_storage(&mut t).unwrap(); }.assimilate_storage(&mut t).unwrap();
pallet_treasury::GenesisConfig::default().assimilate_storage::<Test, _>(&mut t).unwrap(); GenesisBuild::<Test>::assimilate_storage(&pallet_treasury::GenesisConfig, &mut t).unwrap();
t.into() t.into()
} }
@@ -485,7 +485,7 @@ fn genesis_funding_works() {
// Total issuance will be 200 with treasury account initialized with 100. // Total issuance will be 200 with treasury account initialized with 100.
balances: vec![(0, 100), (Treasury::account_id(), initial_funding)], balances: vec![(0, 100), (Treasury::account_id(), initial_funding)],
}.assimilate_storage(&mut t).unwrap(); }.assimilate_storage(&mut t).unwrap();
pallet_treasury::GenesisConfig::default().assimilate_storage::<Test, _>(&mut t).unwrap(); GenesisBuild::<Test>::assimilate_storage(&pallet_treasury::GenesisConfig, &mut t).unwrap();
let mut t: sp_io::TestExternalities = t.into(); let mut t: sp_io::TestExternalities = t.into();
t.execute_with(|| { t.execute_with(|| {
+6 -4
View File
@@ -13,16 +13,18 @@ readme = "README.md"
targets = ["x86_64-unknown-linux-gnu"] targets = ["x86_64-unknown-linux-gnu"]
[dependencies] [dependencies]
serde = { version = "1.0.101", optional = true, features = ["derive"] }
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.101", features = ["derive"], optional = true }
impl-trait-for-tuples = "0.2.1"
sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primitives/std" } sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primitives/std" }
sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../primitives/runtime" } sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../primitives/runtime" }
frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../balances" } pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../balances" }
impl-trait-for-tuples = "0.2.1"
frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true }
[dev-dependencies] [dev-dependencies]
sp-io ={ version = "4.0.0-dev", path = "../../primitives/io" } sp-io ={ version = "4.0.0-dev", path = "../../primitives/io" }
@@ -32,8 +34,8 @@ sp-storage = { version = "4.0.0-dev", path = "../../primitives/storage" }
[features] [features]
default = ["std"] default = ["std"]
std = [ std = [
"serde",
"codec/std", "codec/std",
"serde",
"sp-std/std", "sp-std/std",
"sp-runtime/std", "sp-runtime/std",
"frame-support/std", "frame-support/std",
+4 -4
View File
@@ -1,11 +1,11 @@
# Treasury Module # Treasury Pallet
The Treasury module provides a "pot" of funds that can be managed by stakeholders in the system and The Treasury pallet provides a "pot" of funds that can be managed by stakeholders in the system and
a structure for making spending proposals from this pot. a structure for making spending proposals from this pot.
## Overview ## Overview
The Treasury Module itself provides the pot to store funds, and a means for stakeholders to propose, The Treasury Pallet itself provides the pot to store funds, and a means for stakeholders to propose,
approve, and deny expenditures. The chain will need to provide a method (e.g.inflation, fees) for approve, and deny expenditures. The chain will need to provide a method (e.g.inflation, fees) for
collecting funds. collecting funds.
@@ -19,7 +19,7 @@ and use the funds to pay developers.
approved. approved.
- **Deposit:** Funds that a proposer must lock when making a proposal. The deposit will be returned - **Deposit:** Funds that a proposer must lock when making a proposal. The deposit will be returned
or slashed if the proposal is approved or rejected respectively. or slashed if the proposal is approved or rejected respectively.
- **Pot:** Unspent funds accumulated by the treasury module. - **Pot:** Unspent funds accumulated by the treasury pallet.
## Interface ## Interface
+8 -11
View File
@@ -19,18 +19,16 @@
#![cfg(feature = "runtime-benchmarks")] #![cfg(feature = "runtime-benchmarks")]
use super::*; use super::{*, Pallet as Treasury};
use frame_benchmarking::{benchmarks_instance_pallet, account, impl_benchmark_test_suite};
use frame_support::{traits::OnInitialize, ensure};
use frame_system::RawOrigin; use frame_system::RawOrigin;
use frame_benchmarking::{benchmarks_instance, account, impl_benchmark_test_suite};
use frame_support::traits::OnInitialize;
use crate::Module as Treasury;
const SEED: u32 = 0; const SEED: u32 = 0;
// Create the pre-requisite information needed to create a treasury `propose_spend`. // Create the pre-requisite information needed to create a treasury `propose_spend`.
fn setup_proposal<T: Config<I>, I: Instance>(u: u32) -> ( fn setup_proposal<T: Config<I>, I: 'static>(u: u32) -> (
T::AccountId, T::AccountId,
BalanceOf<T, I>, BalanceOf<T, I>,
<T::Lookup as StaticLookup>::Source, <T::Lookup as StaticLookup>::Source,
@@ -44,7 +42,7 @@ fn setup_proposal<T: Config<I>, I: Instance>(u: u32) -> (
} }
// Create proposals that are approved for use in `on_initialize`. // Create proposals that are approved for use in `on_initialize`.
fn create_approved_proposals<T: Config<I>, I: Instance>(n: u32) -> Result<(), &'static str> { fn create_approved_proposals<T: Config<I>, I: 'static>(n: u32) -> Result<(), &'static str> {
for i in 0 .. n { for i in 0 .. n {
let (caller, value, lookup) = setup_proposal::<T, I>(i); let (caller, value, lookup) = setup_proposal::<T, I>(i);
Treasury::<T, I>::propose_spend( Treasury::<T, I>::propose_spend(
@@ -52,21 +50,20 @@ fn create_approved_proposals<T: Config<I>, I: Instance>(n: u32) -> Result<(), &'
value, value,
lookup lookup
)?; )?;
let proposal_id = <ProposalCount<I>>::get() - 1; let proposal_id = <ProposalCount<T, I>>::get() - 1;
Treasury::<T, I>::approve_proposal(RawOrigin::Root.into(), proposal_id)?; Treasury::<T, I>::approve_proposal(RawOrigin::Root.into(), proposal_id)?;
} }
ensure!(<Approvals<T, I>>::get().len() == n as usize, "Not all approved"); ensure!(<Approvals<T, I>>::get().len() == n as usize, "Not all approved");
Ok(()) Ok(())
} }
fn setup_pot_account<T: Config<I>, I: Instance>() { fn setup_pot_account<T: Config<I>, I: 'static>() {
let pot_account = Treasury::<T, I>::account_id(); let pot_account = Treasury::<T, I>::account_id();
let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000u32.into()); let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000u32.into());
let _ = T::Currency::make_free_balance_be(&pot_account, value); let _ = T::Currency::make_free_balance_be(&pot_account, value);
} }
benchmarks_instance! { benchmarks_instance_pallet! {
propose_spend { propose_spend {
let (caller, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED); let (caller, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED);
// Whitelist caller account from further DB operations. // Whitelist caller account from further DB operations.
+236 -200
View File
@@ -15,9 +15,9 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//! # Treasury Module //! # Treasury Pallet
//! //!
//! The Treasury module provides a "pot" of funds that can be managed by stakeholders in the system //! The Treasury pallet provides a "pot" of funds that can be managed by stakeholders in the system
//! and a structure for making spending proposals from this pot. //! and a structure for making spending proposals from this pot.
//! //!
//! - [`Config`] //! - [`Config`]
@@ -25,7 +25,7 @@
//! //!
//! ## Overview //! ## Overview
//! //!
//! The Treasury Module itself provides the pot to store funds, and a means for stakeholders to //! The Treasury Pallet itself provides the pot to store funds, and a means for stakeholders to
//! propose, approve, and deny expenditures. The chain will need to provide a method (e.g. //! propose, approve, and deny expenditures. The chain will need to provide a method (e.g.
//! inflation, fees) for collecting funds. //! inflation, fees) for collecting funds.
//! //!
@@ -40,7 +40,7 @@
//! approved. //! approved.
//! - **Deposit:** Funds that a proposer must lock when making a proposal. The deposit will be //! - **Deposit:** Funds that a proposer must lock when making a proposal. The deposit will be
//! returned or slashed if the proposal is approved or rejected respectively. //! returned or slashed if the proposal is approved or rejected respectively.
//! - **Pot:** Unspent funds accumulated by the treasury module. //! - **Pot:** Unspent funds accumulated by the treasury pallet.
//! //!
//! ## Interface //! ## Interface
//! //!
@@ -53,89 +53,42 @@
//! //!
//! ## GenesisConfig //! ## GenesisConfig
//! //!
//! The Treasury module depends on the [`GenesisConfig`]. //! The Treasury pallet depends on the [`GenesisConfig`].
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
mod benchmarking; mod benchmarking;
pub mod weights; pub mod weights;
use codec::{Encode, Decode};
use sp_std::prelude::*; use sp_std::prelude::*;
use frame_support::{
decl_module, decl_storage, decl_event, ensure, print, decl_error,
PalletId, BoundedVec, storage::TryAppendValue,
};
use frame_support::traits::{
Currency, Get, Imbalance, OnUnbalanced, ExistenceRequirement::KeepAlive,
ReservableCurrency, WithdrawReasons,
};
use sp_runtime::{ use sp_runtime::{
Permill, RuntimeDebug, Permill, RuntimeDebug,
traits::{ traits::{
Zero, StaticLookup, AccountIdConversion, Saturating Zero, StaticLookup, AccountIdConversion, Saturating
} }
}; };
use frame_support::weights::{Weight, DispatchClass};
use frame_support::traits::EnsureOrigin; use frame_support::{print, PalletId};
use codec::{Encode, Decode}; use frame_support::traits::{
use frame_system::ensure_signed; Currency, Get, Imbalance, OnUnbalanced, ExistenceRequirement::KeepAlive,
ReservableCurrency, WithdrawReasons
};
use frame_support::weights::Weight;
pub use weights::WeightInfo; pub use weights::WeightInfo;
pub use pallet::*;
pub type BalanceOf<T, I=DefaultInstance> = pub type BalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; <<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
pub type PositiveImbalanceOf<T, I=DefaultInstance> = pub type PositiveImbalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::PositiveImbalance; <<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::PositiveImbalance;
pub type NegativeImbalanceOf<T, I=DefaultInstance> = pub type NegativeImbalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance; <<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
pub trait Config<I=DefaultInstance>: frame_system::Config {
/// The treasury's module id, used for deriving its sovereign account ID.
type PalletId: Get<PalletId>;
/// The staking balance.
type Currency: Currency<Self::AccountId> + ReservableCurrency<Self::AccountId>;
/// Origin from which approvals must come.
type ApproveOrigin: EnsureOrigin<Self::Origin>;
/// Origin from which rejections must come.
type RejectOrigin: EnsureOrigin<Self::Origin>;
/// The overarching event type.
type Event: From<Event<Self, I>> + Into<<Self as frame_system::Config>::Event>;
/// Handler for the unbalanced decrease when slashing for a rejected proposal or bounty.
type OnSlash: OnUnbalanced<NegativeImbalanceOf<Self, I>>;
/// Fraction of a proposal's value that should be bonded in order to place the proposal.
/// An accepted proposal gets these back. A rejected proposal does not.
type ProposalBond: Get<Permill>;
/// Minimum amount of funds that should be placed in a deposit for making a proposal.
type ProposalBondMinimum: Get<BalanceOf<Self, I>>;
/// Period between successive spends.
type SpendPeriod: Get<Self::BlockNumber>;
/// Percentage of spare funds (if any) that are burnt per spend period.
type Burn: Get<Permill>;
/// Handler for the unbalanced decrease when treasury funds are burned.
type BurnDestination: OnUnbalanced<NegativeImbalanceOf<Self, I>>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
/// Runtime hooks to external pallet using treasury to compute spend funds.
type SpendFunds: SpendFunds<Self, I>;
/// The maximum number of approvals that can wait in the spending queue.
type MaxApprovals: Get<u32>;
}
/// A trait to allow the Treasury Pallet to spend it's funds for other purposes. /// A trait to allow the Treasury Pallet to spend it's funds for other purposes.
/// There is an expectation that the implementer of this trait will correctly manage /// There is an expectation that the implementer of this trait will correctly manage
/// the mutable variables passed to it: /// the mutable variables passed to it:
@@ -149,7 +102,7 @@ pub trait Config<I=DefaultInstance>: frame_system::Config {
/// not enough funds, mark this value as `true`. This will prevent the treasury /// not enough funds, mark this value as `true`. This will prevent the treasury
/// from burning the excess funds. /// from burning the excess funds.
#[impl_trait_for_tuples::impl_for_tuples(30)] #[impl_trait_for_tuples::impl_for_tuples(30)]
pub trait SpendFunds<T: Config<I>, I=DefaultInstance> { pub trait SpendFunds<T: Config<I>, I: 'static = ()> {
fn spend_funds( fn spend_funds(
budget_remaining: &mut BalanceOf<T, I>, budget_remaining: &mut BalanceOf<T, I>,
imbalance: &mut PositiveImbalanceOf<T, I>, imbalance: &mut PositiveImbalanceOf<T, I>,
@@ -175,58 +128,154 @@ pub struct Proposal<AccountId, Balance> {
bond: Balance, bond: Balance,
} }
decl_storage! { #[frame_support::pallet]
trait Store for Module<T: Config<I>, I: Instance=DefaultInstance> as Treasury { pub mod pallet {
/// Number of proposals that have been made. use frame_support::pallet_prelude::*;
ProposalCount get(fn proposal_count): ProposalIndex; use frame_system::pallet_prelude::*;
use super::*;
/// Proposals that have been made. #[pallet::pallet]
pub Proposals get(fn proposals): #[pallet::generate_store(pub(super) trait Store)]
map hasher(twox_64_concat) ProposalIndex pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
=> Option<Proposal<T::AccountId, BalanceOf<T, I>>>;
/// Proposal indices that have been approved but not yet awarded. #[pallet::config]
pub Approvals get(fn approvals): BoundedVec<ProposalIndex, T::MaxApprovals>; pub trait Config<I: 'static = ()>: frame_system::Config {
/// The staking balance.
type Currency: Currency<Self::AccountId> + ReservableCurrency<Self::AccountId>;
/// Origin from which approvals must come.
type ApproveOrigin: EnsureOrigin<Self::Origin>;
/// Origin from which rejections must come.
type RejectOrigin: EnsureOrigin<Self::Origin>;
/// The overarching event type.
type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>;
/// Handler for the unbalanced decrease when slashing for a rejected proposal or bounty.
type OnSlash: OnUnbalanced<NegativeImbalanceOf<Self, I>>;
/// Fraction of a proposal's value that should be bonded in order to place the proposal.
/// An accepted proposal gets these back. A rejected proposal does not.
#[pallet::constant]
type ProposalBond: Get<Permill>;
/// Minimum amount of funds that should be placed in a deposit for making a proposal.
#[pallet::constant]
type ProposalBondMinimum: Get<BalanceOf<Self, I>>;
/// Period between successive spends.
#[pallet::constant]
type SpendPeriod: Get<Self::BlockNumber>;
/// Percentage of spare funds (if any) that are burnt per spend period.
#[pallet::constant]
type Burn: Get<Permill>;
/// The treasury's pallet id, used for deriving its sovereign account ID.
#[pallet::constant]
type PalletId: Get<PalletId>;
/// Handler for the unbalanced decrease when treasury funds are burned.
type BurnDestination: OnUnbalanced<NegativeImbalanceOf<Self, I>>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
/// Runtime hooks to external pallet using treasury to compute spend funds.
type SpendFunds: SpendFunds<Self, I>;
/// The maximum number of approvals that can wait in the spending queue.
type MaxApprovals: Get<u32>;
} }
add_extra_genesis {
build(|_config| { /// Number of proposals that have been made.
#[pallet::storage]
#[pallet::getter(fn proposal_count)]
pub(crate) type ProposalCount<T, I = ()> = StorageValue<_, ProposalIndex, ValueQuery>;
/// Proposals that have been made.
#[pallet::storage]
#[pallet::getter(fn proposals)]
pub type Proposals<T: Config<I>, I: 'static = ()> = StorageMap<
_,
Twox64Concat,
ProposalIndex,
Proposal<T::AccountId, BalanceOf<T, I>>,
OptionQuery
>;
/// Proposal indices that have been approved but not yet awarded.
#[pallet::storage]
#[pallet::getter(fn approvals)]
pub type Approvals<T: Config<I>, I: 'static = ()> = StorageValue<
_,
BoundedVec<ProposalIndex, T::MaxApprovals>,
ValueQuery
>;
#[pallet::genesis_config]
pub struct GenesisConfig;
#[cfg(feature = "std")]
impl Default for GenesisConfig {
fn default() -> Self {
Self
}
}
#[cfg(feature = "std")]
impl GenesisConfig {
/// Direct implementation of `GenesisBuild::assimilate_storage`.
#[deprecated(note = "use `<GensisConfig<T, I> as GenesisBuild<T, I>>::assimilate_storage` instead")]
pub fn assimilate_storage<T: Config<I>, I: 'static>(
&self,
storage: &mut sp_runtime::Storage
) -> Result<(), String> {
<Self as GenesisBuild<T, I>>::assimilate_storage(self, storage)
}
}
#[pallet::genesis_build]
impl<T: Config<I>, I: 'static> GenesisBuild<T, I> for GenesisConfig {
fn build(&self) {
// Create Treasury account // Create Treasury account
let account_id = <Module<T, I>>::account_id(); let account_id = <Pallet<T, I>>::account_id();
let min = T::Currency::minimum_balance(); let min = T::Currency::minimum_balance();
if T::Currency::free_balance(&account_id) < min { if T::Currency::free_balance(&account_id) < min {
let _ = T::Currency::make_free_balance_be(&account_id, min); let _ = T::Currency::make_free_balance_be(&account_id, min);
} }
}); }
} }
}
decl_event!( #[pallet::event]
pub enum Event<T, I=DefaultInstance> #[pallet::generate_deposit(pub(super) fn deposit_event)]
where #[pallet::metadata(T::AccountId = "AccountId", BalanceOf<T, I> = "Balance")]
Balance = BalanceOf<T, I>, pub enum Event<T: Config<I>, I: 'static = ()> {
<T as frame_system::Config>::AccountId,
{
/// New proposal. \[proposal_index\] /// New proposal. \[proposal_index\]
Proposed(ProposalIndex), Proposed(ProposalIndex),
/// We have ended a spend period and will now allocate funds. \[budget_remaining\] /// We have ended a spend period and will now allocate funds. \[budget_remaining\]
Spending(Balance), Spending(BalanceOf<T, I>),
/// Some funds have been allocated. \[proposal_index, award, beneficiary\] /// Some funds have been allocated. \[proposal_index, award, beneficiary\]
Awarded(ProposalIndex, Balance, AccountId), Awarded(ProposalIndex, BalanceOf<T, I>, T::AccountId),
/// A proposal was rejected; funds were slashed. \[proposal_index, slashed\] /// A proposal was rejected; funds were slashed. \[proposal_index, slashed\]
Rejected(ProposalIndex, Balance), Rejected(ProposalIndex, BalanceOf<T, I>),
/// Some of our funds have been burnt. \[burn\] /// Some of our funds have been burnt. \[burn\]
Burnt(Balance), Burnt(BalanceOf<T, I>),
/// Spending has finished; this is the amount that rolls over until next spend. /// Spending has finished; this is the amount that rolls over until next spend.
/// \[budget_remaining\] /// \[budget_remaining\]
Rollover(Balance), Rollover(BalanceOf<T, I>),
/// Some funds have been deposited. \[deposit\] /// Some funds have been deposited. \[deposit\]
Deposit(Balance), Deposit(BalanceOf<T, I>),
} }
);
decl_error! { /// Old name generated by `decl_event`.
/// Error for the treasury module. #[deprecated(note = "use `Event` instead")]
pub enum Error for Module<T: Config<I>, I: Instance> { pub type RawEvent<T, I = ()> = Event<T, I>;
/// Error for the treasury pallet.
#[pallet::error]
pub enum Error<T, I = ()> {
/// Proposer's balance is too low. /// Proposer's balance is too low.
InsufficientProposersBalance, InsufficientProposersBalance,
/// No proposal or bounty at that index. /// No proposal or bounty at that index.
@@ -234,101 +283,9 @@ decl_error! {
/// Too many approvals in the queue. /// Too many approvals in the queue.
TooManyApprovals, TooManyApprovals,
} }
}
decl_module! {
pub struct Module<T: Config<I>, I: Instance=DefaultInstance>
for enum Call
where origin: T::Origin
{
/// Fraction of a proposal's value that should be bonded in order to place the proposal.
/// An accepted proposal gets these back. A rejected proposal does not.
const ProposalBond: Permill = T::ProposalBond::get();
/// Minimum amount of funds that should be placed in a deposit for making a proposal.
const ProposalBondMinimum: BalanceOf<T, I> = T::ProposalBondMinimum::get();
/// Period between successive spends.
const SpendPeriod: T::BlockNumber = T::SpendPeriod::get();
/// Percentage of spare funds (if any) that are burnt per spend period.
const Burn: Permill = T::Burn::get();
/// The treasury's module id, used for deriving its sovereign account ID.
const PalletId: PalletId = T::PalletId::get();
type Error = Error<T, I>;
fn deposit_event() = default;
/// Put forward a suggestion for spending. A deposit proportional to the value
/// is reserved and slashed if the proposal is rejected. It is returned once the
/// proposal is awarded.
///
/// # <weight>
/// - Complexity: O(1)
/// - DbReads: `ProposalCount`, `origin account`
/// - DbWrites: `ProposalCount`, `Proposals`, `origin account`
/// # </weight>
#[weight = T::WeightInfo::propose_spend()]
pub fn propose_spend(
origin,
#[compact] value: BalanceOf<T, I>,
beneficiary: <T::Lookup as StaticLookup>::Source
) {
let proposer = ensure_signed(origin)?;
let beneficiary = T::Lookup::lookup(beneficiary)?;
let bond = Self::calculate_bond(value);
T::Currency::reserve(&proposer, bond)
.map_err(|_| Error::<T, I>::InsufficientProposersBalance)?;
let c = Self::proposal_count();
<ProposalCount<I>>::put(c + 1);
<Proposals<T, I>>::insert(c, Proposal { proposer, value, beneficiary, bond });
Self::deposit_event(RawEvent::Proposed(c));
}
/// Reject a proposed spend. The original deposit will be slashed.
///
/// May only be called from `T::RejectOrigin`.
///
/// # <weight>
/// - Complexity: O(1)
/// - DbReads: `Proposals`, `rejected proposer account`
/// - DbWrites: `Proposals`, `rejected proposer account`
/// # </weight>
#[weight = (T::WeightInfo::reject_proposal(), DispatchClass::Operational)]
pub fn reject_proposal(origin, #[compact] proposal_id: ProposalIndex) {
T::RejectOrigin::ensure_origin(origin)?;
let proposal = <Proposals<T, I>>::take(&proposal_id).ok_or(Error::<T, I>::InvalidIndex)?;
let value = proposal.bond;
let imbalance = T::Currency::slash_reserved(&proposal.proposer, value).0;
T::OnSlash::on_unbalanced(imbalance);
Self::deposit_event(Event::<T, I>::Rejected(proposal_id, value));
}
/// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary
/// and the original deposit will be returned.
///
/// May only be called from `T::ApproveOrigin`.
///
/// # <weight>
/// - Complexity: O(1).
/// - DbReads: `Proposals`, `Approvals`
/// - DbWrite: `Approvals`
/// # </weight>
#[weight = (T::WeightInfo::approve_proposal(T::MaxApprovals::get()), DispatchClass::Operational)]
pub fn approve_proposal(origin, #[compact] proposal_id: ProposalIndex) {
T::ApproveOrigin::ensure_origin(origin)?;
ensure!(<Proposals<T, I>>::contains_key(proposal_id), Error::<T, I>::InvalidIndex);
Approvals::<T, I>::try_append(proposal_id).map_err(|_| Error::<T, I>::TooManyApprovals)?;
}
#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
/// # <weight> /// # <weight>
/// - Complexity: `O(A)` where `A` is the number of approvals /// - Complexity: `O(A)` where `A` is the number of approvals
/// - Db reads and writes: `Approvals`, `pot account data` /// - Db reads and writes: `Approvals`, `pot account data`
@@ -345,9 +302,89 @@ decl_module! {
} }
} }
} }
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Put forward a suggestion for spending. A deposit proportional to the value
/// is reserved and slashed if the proposal is rejected. It is returned once the
/// proposal is awarded.
///
/// # <weight>
/// - Complexity: O(1)
/// - DbReads: `ProposalCount`, `origin account`
/// - DbWrites: `ProposalCount`, `Proposals`, `origin account`
/// # </weight>
#[pallet::weight(T::WeightInfo::propose_spend())]
pub fn propose_spend(
origin: OriginFor<T>,
#[pallet::compact] value: BalanceOf<T, I>,
beneficiary: <T::Lookup as StaticLookup>::Source
) -> DispatchResult {
let proposer = ensure_signed(origin)?;
let beneficiary = T::Lookup::lookup(beneficiary)?;
let bond = Self::calculate_bond(value);
T::Currency::reserve(&proposer, bond)
.map_err(|_| Error::<T, I>::InsufficientProposersBalance)?;
let c = Self::proposal_count();
<ProposalCount<T, I>>::put(c + 1);
<Proposals<T, I>>::insert(c, Proposal { proposer, value, beneficiary, bond });
Self::deposit_event(Event::Proposed(c));
Ok(())
}
/// Reject a proposed spend. The original deposit will be slashed.
///
/// May only be called from `T::RejectOrigin`.
///
/// # <weight>
/// - Complexity: O(1)
/// - DbReads: `Proposals`, `rejected proposer account`
/// - DbWrites: `Proposals`, `rejected proposer account`
/// # </weight>
#[pallet::weight((T::WeightInfo::reject_proposal(), DispatchClass::Operational))]
pub fn reject_proposal(
origin: OriginFor<T>,
#[pallet::compact] proposal_id: ProposalIndex
) -> DispatchResult {
T::RejectOrigin::ensure_origin(origin)?;
let proposal = <Proposals<T, I>>::take(&proposal_id).ok_or(Error::<T, I>::InvalidIndex)?;
let value = proposal.bond;
let imbalance = T::Currency::slash_reserved(&proposal.proposer, value).0;
T::OnSlash::on_unbalanced(imbalance);
Self::deposit_event(Event::<T, I>::Rejected(proposal_id, value));
Ok(())
}
/// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary
/// and the original deposit will be returned.
///
/// May only be called from `T::ApproveOrigin`.
///
/// # <weight>
/// - Complexity: O(1).
/// - DbReads: `Proposals`, `Approvals`
/// - DbWrite: `Approvals`
/// # </weight>
#[pallet::weight((T::WeightInfo::approve_proposal(T::MaxApprovals::get()), DispatchClass::Operational))]
pub fn approve_proposal(
origin: OriginFor<T>,
#[pallet::compact] proposal_id: ProposalIndex
) -> DispatchResult {
T::ApproveOrigin::ensure_origin(origin)?;
ensure!(<Proposals<T, I>>::contains_key(proposal_id), Error::<T, I>::InvalidIndex);
Approvals::<T, I>::try_append(proposal_id).map_err(|_| Error::<T, I>::TooManyApprovals)?;
Ok(())
}
}
} }
impl<T: Config<I>, I: Instance> Module<T, I> { impl<T: Config<I>, I: 'static> Pallet<T, I> {
// Add public immutables and private mutables. // Add public immutables and private mutables.
/// The account ID of the treasury pot. /// The account ID of the treasury pot.
@@ -368,7 +405,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
let mut total_weight: Weight = Zero::zero(); let mut total_weight: Weight = Zero::zero();
let mut budget_remaining = Self::pot(); let mut budget_remaining = Self::pot();
Self::deposit_event(RawEvent::Spending(budget_remaining)); Self::deposit_event(Event::Spending(budget_remaining));
let account_id = Self::account_id(); let account_id = Self::account_id();
let mut missed_any = false; let mut missed_any = false;
@@ -389,7 +426,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
// provide the allocation. // provide the allocation.
imbalance.subsume(T::Currency::deposit_creating(&p.beneficiary, p.value)); imbalance.subsume(T::Currency::deposit_creating(&p.beneficiary, p.value));
Self::deposit_event(RawEvent::Awarded(index, p.value, p.beneficiary)); Self::deposit_event(Event::Awarded(index, p.value, p.beneficiary));
false false
} else { } else {
missed_any = true; missed_any = true;
@@ -415,7 +452,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
let (debit, credit) = T::Currency::pair(burn); let (debit, credit) = T::Currency::pair(burn);
imbalance.subsume(debit); imbalance.subsume(debit);
T::BurnDestination::on_unbalanced(credit); T::BurnDestination::on_unbalanced(credit);
Self::deposit_event(RawEvent::Burnt(burn)) Self::deposit_event(Event::Burnt(burn))
} }
// Must never be an error, but better to be safe. // Must never be an error, but better to be safe.
@@ -433,7 +470,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
drop(problem); drop(problem);
} }
Self::deposit_event(RawEvent::Rollover(budget_remaining)); Self::deposit_event(Event::Rollover(budget_remaining));
total_weight total_weight
} }
@@ -445,16 +482,15 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
// Must never be less than 0 but better be safe. // Must never be less than 0 but better be safe.
.saturating_sub(T::Currency::minimum_balance()) .saturating_sub(T::Currency::minimum_balance())
} }
} }
impl<T: Config<I>, I: Instance> OnUnbalanced<NegativeImbalanceOf<T, I>> for Module<T, I> { impl<T: Config<I>, I: 'static> OnUnbalanced<NegativeImbalanceOf<T, I>> for Pallet<T, I> {
fn on_nonzero_unbalanced(amount: NegativeImbalanceOf<T, I>) { fn on_nonzero_unbalanced(amount: NegativeImbalanceOf<T, I>) {
let numeric_amount = amount.peek(); let numeric_amount = amount.peek();
// Must resolve into existing but better to be safe. // Must resolve into existing but better to be safe.
let _ = T::Currency::resolve_creating(&Self::account_id(), amount); let _ = T::Currency::resolve_creating(&Self::account_id(), amount);
Self::deposit_event(RawEvent::Deposit(numeric_amount)); Self::deposit_event(Event::Deposit(numeric_amount));
} }
} }
+10 -8
View File
@@ -19,13 +19,7 @@
#![cfg(test)] #![cfg(test)]
use crate as treasury;
use super::*;
use std::cell::RefCell; use std::cell::RefCell;
use frame_support::{
assert_noop, assert_ok, parameter_types,
traits::OnInitialize, PalletId
};
use sp_core::H256; use sp_core::H256;
use sp_runtime::{ use sp_runtime::{
@@ -33,6 +27,14 @@ use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup}, traits::{BlakeTwo256, IdentityLookup},
}; };
use frame_support::{
assert_noop, assert_ok, parameter_types,
traits::OnInitialize, PalletId, pallet_prelude::GenesisBuild,
};
use crate as treasury;
use super::*;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>; type Block = frame_system::mocking::MockBlock<Test>;
@@ -129,7 +131,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
// Total issuance will be 200 with treasury account initialized at ED. // Total issuance will be 200 with treasury account initialized at ED.
balances: vec![(0, 100), (1, 98), (2, 1)], balances: vec![(0, 100), (1, 98), (2, 1)],
}.assimilate_storage(&mut t).unwrap(); }.assimilate_storage(&mut t).unwrap();
treasury::GenesisConfig::default().assimilate_storage::<Test, _>(&mut t).unwrap(); GenesisBuild::<Test>::assimilate_storage(&crate::GenesisConfig, &mut t).unwrap();
t.into() t.into()
} }
@@ -355,7 +357,7 @@ fn genesis_funding_works() {
// Total issuance will be 200 with treasury account initialized with 100. // Total issuance will be 200 with treasury account initialized with 100.
balances: vec![(0, 100), (Treasury::account_id(), initial_funding)], balances: vec![(0, 100), (Treasury::account_id(), initial_funding)],
}.assimilate_storage(&mut t).unwrap(); }.assimilate_storage(&mut t).unwrap();
treasury::GenesisConfig::default().assimilate_storage::<Test, _>(&mut t).unwrap(); GenesisBuild::<Test>::assimilate_storage(&crate::GenesisConfig, &mut t).unwrap();
let mut t: sp_io::TestExternalities = t.into(); let mut t: sp_io::TestExternalities = t.into();
t.execute_with(|| { t.execute_with(|| {