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 {
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![],
},
EventRecord {
@@ -417,7 +417,7 @@ fn full_native_block_import_works() {
},
EventRecord {
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![],
},
EventRecord {
@@ -440,7 +440,7 @@ fn full_native_block_import_works() {
},
EventRecord {
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![],
},
EventRecord {
+1 -1
View File
@@ -27,7 +27,7 @@ use frame_benchmarking::{benchmarks, account, whitelisted_caller, impl_benchmark
use frame_support::traits::OnInitialize;
use crate::Module as Bounties;
use pallet_treasury::Module as Treasury;
use pallet_treasury::Pallet as Treasury;
const SEED: u32 = 0;
+6 -6
View File
@@ -25,7 +25,7 @@ use std::cell::RefCell;
use frame_support::{
assert_noop, assert_ok, parameter_types, weights::Weight, traits::OnInitialize,
PalletId
PalletId, pallet_prelude::GenesisBuild,
};
use sp_core::H256;
@@ -146,7 +146,7 @@ impl Config for Test {
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 {
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.
balances: vec![(0, 100), (1, 98), (2, 1)],
}.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()
}
@@ -268,7 +268,7 @@ fn reject_already_rejected_spend_proposal_fails() {
fn reject_non_existent_spend_proposal_fails() {
new_test_ext().execute_with(|| {
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!(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);
});
@@ -897,7 +897,7 @@ fn genesis_funding_works() {
// Total issuance will be 200 with treasury account initialized with 100.
balances: vec![(0, 100), (Treasury::account_id(), initial_funding)],
}.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();
t.execute_with(|| {
+1 -1
View File
@@ -501,7 +501,7 @@ impl<T: Config> Module<T> {
tips.sort_by_key(|i| i.1);
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);
if !tip.deposit.is_zero() {
+3 -3
View File
@@ -25,7 +25,7 @@ use std::cell::RefCell;
use frame_support::{
assert_noop, assert_ok, parameter_types,
weights::Weight, traits::SortedMembers,
PalletId
PalletId, pallet_prelude::GenesisBuild,
};
use sp_runtime::Permill;
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.
balances: vec![(0, 100), (1, 98), (2, 1)],
}.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()
}
@@ -485,7 +485,7 @@ fn genesis_funding_works() {
// Total issuance will be 200 with treasury account initialized with 100.
balances: vec![(0, 100), (Treasury::account_id(), initial_funding)],
}.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();
t.execute_with(|| {
+6 -4
View File
@@ -13,16 +13,18 @@ readme = "README.md"
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
serde = { version = "1.0.101", optional = true, 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-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-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
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]
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]
default = ["std"]
std = [
"serde",
"codec/std",
"serde",
"sp-std/std",
"sp-runtime/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.
## 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
collecting funds.
@@ -19,7 +19,7 @@ and use the funds to pay developers.
approved.
- **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.
- **Pot:** Unspent funds accumulated by the treasury module.
- **Pot:** Unspent funds accumulated by the treasury pallet.
## Interface
+8 -11
View File
@@ -19,18 +19,16 @@
#![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_benchmarking::{benchmarks_instance, account, impl_benchmark_test_suite};
use frame_support::traits::OnInitialize;
use crate::Module as Treasury;
const SEED: u32 = 0;
// 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,
BalanceOf<T, I>,
<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`.
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 {
let (caller, value, lookup) = setup_proposal::<T, I>(i);
Treasury::<T, I>::propose_spend(
@@ -52,21 +50,20 @@ fn create_approved_proposals<T: Config<I>, I: Instance>(n: u32) -> Result<(), &'
value,
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)?;
}
ensure!(<Approvals<T, I>>::get().len() == n as usize, "Not all approved");
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 value = T::Currency::minimum_balance().saturating_mul(1_000_000_000u32.into());
let _ = T::Currency::make_free_balance_be(&pot_account, value);
}
benchmarks_instance! {
benchmarks_instance_pallet! {
propose_spend {
let (caller, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED);
// 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
// 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.
//!
//! - [`Config`]
@@ -25,7 +25,7 @@
//!
//! ## 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.
//! inflation, fees) for collecting funds.
//!
@@ -40,7 +40,7 @@
//! approved.
//! - **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.
//! - **Pot:** Unspent funds accumulated by the treasury module.
//! - **Pot:** Unspent funds accumulated by the treasury pallet.
//!
//! ## Interface
//!
@@ -53,89 +53,42 @@
//!
//! ## GenesisConfig
//!
//! The Treasury module depends on the [`GenesisConfig`].
//! The Treasury pallet depends on the [`GenesisConfig`].
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod tests;
mod benchmarking;
pub mod weights;
use codec::{Encode, Decode};
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::{
Permill, RuntimeDebug,
traits::{
Zero, StaticLookup, AccountIdConversion, Saturating
}
};
use frame_support::weights::{Weight, DispatchClass};
use frame_support::traits::EnsureOrigin;
use codec::{Encode, Decode};
use frame_system::ensure_signed;
use frame_support::{print, PalletId};
use frame_support::traits::{
Currency, Get, Imbalance, OnUnbalanced, ExistenceRequirement::KeepAlive,
ReservableCurrency, WithdrawReasons
};
use frame_support::weights::Weight;
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;
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;
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;
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.
/// There is an expectation that the implementer of this trait will correctly manage
/// 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
/// from burning the excess funds.
#[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(
budget_remaining: &mut BalanceOf<T, I>,
imbalance: &mut PositiveImbalanceOf<T, I>,
@@ -175,58 +128,154 @@ pub struct Proposal<AccountId, Balance> {
bond: Balance,
}
decl_storage! {
trait Store for Module<T: Config<I>, I: Instance=DefaultInstance> as Treasury {
/// Number of proposals that have been made.
ProposalCount get(fn proposal_count): ProposalIndex;
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use super::*;
/// Proposals that have been made.
pub Proposals get(fn proposals):
map hasher(twox_64_concat) ProposalIndex
=> Option<Proposal<T::AccountId, BalanceOf<T, I>>>;
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
/// Proposal indices that have been approved but not yet awarded.
pub Approvals get(fn approvals): BoundedVec<ProposalIndex, T::MaxApprovals>;
#[pallet::config]
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
let account_id = <Module<T, I>>::account_id();
let account_id = <Pallet<T, I>>::account_id();
let min = T::Currency::minimum_balance();
if T::Currency::free_balance(&account_id) < min {
let _ = T::Currency::make_free_balance_be(&account_id, min);
}
});
}
}
}
decl_event!(
pub enum Event<T, I=DefaultInstance>
where
Balance = BalanceOf<T, I>,
<T as frame_system::Config>::AccountId,
{
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
#[pallet::metadata(T::AccountId = "AccountId", BalanceOf<T, I> = "Balance")]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// New proposal. \[proposal_index\]
Proposed(ProposalIndex),
/// 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\]
Awarded(ProposalIndex, Balance, AccountId),
Awarded(ProposalIndex, BalanceOf<T, I>, T::AccountId),
/// 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\]
Burnt(Balance),
Burnt(BalanceOf<T, I>),
/// Spending has finished; this is the amount that rolls over until next spend.
/// \[budget_remaining\]
Rollover(Balance),
Rollover(BalanceOf<T, I>),
/// Some funds have been deposited. \[deposit\]
Deposit(Balance),
Deposit(BalanceOf<T, I>),
}
);
decl_error! {
/// Error for the treasury module.
pub enum Error for Module<T: Config<I>, I: Instance> {
/// Old name generated by `decl_event`.
#[deprecated(note = "use `Event` instead")]
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.
InsufficientProposersBalance,
/// No proposal or bounty at that index.
@@ -234,101 +283,9 @@ decl_error! {
/// Too many approvals in the queue.
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>
/// - Complexity: `O(A)` where `A` is the number of approvals
/// - 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.
/// 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 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 mut missed_any = false;
@@ -389,7 +426,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
// provide the allocation.
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
} else {
missed_any = true;
@@ -415,7 +452,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
let (debit, credit) = T::Currency::pair(burn);
imbalance.subsume(debit);
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.
@@ -433,7 +470,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
drop(problem);
}
Self::deposit_event(RawEvent::Rollover(budget_remaining));
Self::deposit_event(Event::Rollover(budget_remaining));
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.
.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>) {
let numeric_amount = amount.peek();
// Must resolve into existing but better to be safe.
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)]
use crate as treasury;
use super::*;
use std::cell::RefCell;
use frame_support::{
assert_noop, assert_ok, parameter_types,
traits::OnInitialize, PalletId
};
use sp_core::H256;
use sp_runtime::{
@@ -33,6 +27,14 @@ use sp_runtime::{
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 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.
balances: vec![(0, 100), (1, 98), (2, 1)],
}.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()
}
@@ -355,7 +357,7 @@ fn genesis_funding_works() {
// Total issuance will be 200 with treasury account initialized with 100.
balances: vec![(0, 100), (Treasury::account_id(), initial_funding)],
}.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();
t.execute_with(|| {