mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 01:38:04 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(|| {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(|| {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(|| {
|
||||
|
||||
Reference in New Issue
Block a user