diff --git a/.github/license-check/config.json b/.github/license-check/config.json index e3ff87c98f..2a02f38d86 100644 --- a/.github/license-check/config.json +++ b/.github/license-check/config.json @@ -6,7 +6,6 @@ "exclude": [ "pallets/collator-selection/**", "pallets/custom-signatures/**", - "pallets/treasury/**", "precompiles/utils/**", "precompiles/utils_v2/**", "vendor/**" diff --git a/Cargo.toml b/Cargo.toml index d9a5445977..7f984d4f13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ members = [ "vendor/rpc-core/types", "vendor/runtime/evm-tracer", "vendor/runtime/ext", + "vendor/treasury", ] resolver = "2" @@ -294,7 +295,7 @@ pallet-static-price-provider = { path = "./pallets/static-price-provider", defau pallet-price-aggregator = { path = "./pallets/price-aggregator", default-features = false } pallet-collective-proxy = { path = "./pallets/collective-proxy", default-features = false } vesting-mbm = { path = "./pallets/vesting-mbm", default-features = false } -pallet-treasury = { path = "./pallets/treasury", default-features = false } +pallet-treasury = { path = "./vendor/treasury", default-features = false } dapp-staking-runtime-api = { path = "./pallets/dapp-staking/rpc/runtime-api", default-features = false } diff --git a/pallets/treasury/Cargo.toml b/pallets/treasury/Cargo.toml deleted file mode 100644 index 92006364c9..0000000000 --- a/pallets/treasury/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "pallet-treasury" -version = "36.0.1" -authors.workspace = true -edition.workspace = true -license = "Apache-2.0" -homepage = "https://substrate.io" -repository.workspace = true -description = "FRAME pallet to manage treasury (polkadot v1.15.0)" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -docify = { workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support.workspace = true -frame-system.workspace = true -impl-trait-for-tuples = { workspace = true } -pallet-balances.workspace = true -parity-scale-codec = { features = ["derive", "max-encoded-len"], workspace = true } -scale-info = { features = ["derive"], workspace = true } -serde = { features = ["derive"], optional = true, workspace = true, default-features = true } -sp-core = { optional = true, workspace = true } -sp-runtime.workspace = true - -[dev-dependencies] -pallet-utility = { workspace = true } -sp-core = { workspace = true } -sp-io = { workspace = true } - -[features] -default = ["std"] -std = [ - "parity-scale-codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "scale-info/std", - "serde", - "sp-core?/std", - "sp-runtime/std", -] -runtime-benchmarks = [ - "dep:sp-core", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/pallets/treasury/README.md b/pallets/treasury/README.md deleted file mode 100644 index 48fd1e2699..0000000000 --- a/pallets/treasury/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Treasury Pallet - -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 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. - -By way of example, the Council could vote to fund the Treasury with a portion of the block reward -and use the funds to pay developers. - -### Terminology - -- **Proposal:** A suggestion to allocate funds from the pot to a beneficiary. -- **Beneficiary:** An account who will receive the funds from a proposal if the proposal is - 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 pallet. - -## Interface - -### Dispatchable Functions - -General spending/proposal protocol: -- `spend_local` - Propose and approve a spend of treasury funds, enables the - creation of spends using the native currency of the chain, utilizing the funds - stored in the pot -- `spend` - Propose and approve a spend of treasury funds, allows spending any - asset kind managed by the treasury -- `remove_approval` - Force a previously approved proposal to be removed from - the approval queue -- `payout` - Claim a spend -- `check_status` - Check the status of the spend and remove it from the storage - if processed -- `void_spend` - Void previously approved spend - - -## Release - -polkadot v1.15.0 -- `propose_spend` - Make a spending proposal and stake the required deposit. -- `reject_proposal` - Reject a proposal, slashing the deposit. -- `approve_proposal` - Accept the proposal, returning the deposit. diff --git a/pallets/treasury/src/benchmarking.rs b/pallets/treasury/src/benchmarking.rs deleted file mode 100644 index 2a8b080d3e..0000000000 --- a/pallets/treasury/src/benchmarking.rs +++ /dev/null @@ -1,426 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Treasury pallet benchmarking. - -#![cfg(feature = "runtime-benchmarks")] - -use super::{Pallet as Treasury, *}; - -use frame_benchmarking::{ - v1::{account, BenchmarkError}, - v2::*, -}; -use frame_support::{ - assert_err, assert_ok, ensure, - traits::{ - tokens::{ConversionFromAssetBalance, PaymentStatus}, - EnsureOrigin, OnInitialize, - }, -}; -use frame_system::RawOrigin; -use sp_core::crypto::FromEntropy; - -/// Trait describing factory functions for dispatchables' parameters. -pub trait ArgumentsFactory { - /// Factory function for an asset kind. - fn create_asset_kind(seed: u32) -> AssetKind; - /// Factory function for a beneficiary. - fn create_beneficiary(seed: [u8; 32]) -> Beneficiary; -} - -/// Implementation that expects the parameters implement the [`FromEntropy`] trait. -impl ArgumentsFactory for () -where - AssetKind: FromEntropy, - Beneficiary: FromEntropy, -{ - fn create_asset_kind(seed: u32) -> AssetKind { - AssetKind::from_entropy(&mut seed.encode().as_slice()).unwrap() - } - fn create_beneficiary(seed: [u8; 32]) -> Beneficiary { - Beneficiary::from_entropy(&mut seed.as_slice()).unwrap() - } -} - -const SEED: u32 = 0; - -// Create the pre-requisite information needed to create a treasury `propose_spend`. -fn setup_proposal, I: 'static>( - u: u32, -) -> (T::AccountId, BalanceOf, AccountIdLookupOf) { - let caller = account("caller", u, SEED); - let value: BalanceOf = T::ProposalBondMinimum::get().saturating_mul(100u32.into()); - let _ = T::Currency::make_free_balance_be(&caller, value); - let beneficiary = account("beneficiary", u, SEED); - let beneficiary_lookup = T::Lookup::unlookup(beneficiary); - (caller, value, beneficiary_lookup) -} - -// Create proposals that are approved for use in `on_initialize`. -fn create_approved_proposals, I: 'static>(n: u32) -> Result<(), &'static str> { - for i in 0..n { - let (caller, value, lookup) = setup_proposal::(i); - #[allow(deprecated)] - Treasury::::propose_spend(RawOrigin::Signed(caller).into(), value, lookup)?; - let proposal_id = >::get() - 1; - Approvals::::try_append(proposal_id).unwrap(); - } - - ensure!( - Approvals::::get().len() == n as usize, - "Not all approved" - ); - - Ok(()) -} - -fn setup_pot_account, I: 'static>() { - let pot_account = Treasury::::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); -} - -fn assert_last_event, I: 'static>(generic_event: >::RuntimeEvent) { - frame_system::Pallet::::assert_last_event(generic_event.into()); -} - -// Create the arguments for the `spend` dispatchable. -fn create_spend_arguments, I: 'static>( - seed: u32, -) -> ( - T::AssetKind, - AssetBalanceOf, - T::Beneficiary, - BeneficiaryLookupOf, -) { - let asset_kind = T::BenchmarkHelper::create_asset_kind(seed); - let beneficiary = T::BenchmarkHelper::create_beneficiary([seed.try_into().unwrap(); 32]); - let beneficiary_lookup = T::BeneficiaryLookup::unlookup(beneficiary.clone()); - (asset_kind, 100u32.into(), beneficiary, beneficiary_lookup) -} - -#[instance_benchmarks] -mod benchmarks { - use super::*; - - /// This benchmark is short-circuited if `SpendOrigin` cannot provide - /// a successful origin, in which case `spend` is un-callable and can use weight=0. - #[benchmark] - fn spend_local() -> Result<(), BenchmarkError> { - let (_, value, beneficiary_lookup) = setup_proposal::(SEED); - let origin = - T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let beneficiary = T::Lookup::lookup(beneficiary_lookup.clone()).unwrap(); - - #[extrinsic_call] - _(origin as T::RuntimeOrigin, value, beneficiary_lookup); - - assert_last_event::( - Event::SpendApproved { - proposal_index: 0, - amount: value, - beneficiary, - } - .into(), - ); - Ok(()) - } - - #[benchmark] - fn propose_spend() -> Result<(), BenchmarkError> { - let (caller, value, beneficiary_lookup) = setup_proposal::(SEED); - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - - #[extrinsic_call] - _(RawOrigin::Signed(caller), value, beneficiary_lookup); - - Ok(()) - } - - #[benchmark] - fn reject_proposal() -> Result<(), BenchmarkError> { - let (caller, value, beneficiary_lookup) = setup_proposal::(SEED); - #[allow(deprecated)] - Treasury::::propose_spend( - RawOrigin::Signed(caller).into(), - value, - beneficiary_lookup, - )?; - let proposal_id = Treasury::::proposal_count() - 1; - let reject_origin = - T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - #[extrinsic_call] - _(reject_origin as T::RuntimeOrigin, proposal_id); - Ok(()) - } - - #[benchmark] - fn approve_proposal( - p: Linear<0, { T::MaxApprovals::get() - 1 }>, - ) -> Result<(), BenchmarkError> { - let approve_origin = - T::ApproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - create_approved_proposals::(p)?; - let (caller, value, beneficiary_lookup) = setup_proposal::(SEED); - #[allow(deprecated)] - Treasury::::propose_spend( - RawOrigin::Signed(caller).into(), - value, - beneficiary_lookup, - )?; - let proposal_id = Treasury::::proposal_count() - 1; - - #[extrinsic_call] - _(approve_origin as T::RuntimeOrigin, proposal_id); - - Ok(()) - } - - #[benchmark] - fn remove_approval() -> Result<(), BenchmarkError> { - let (caller, value, beneficiary_lookup) = setup_proposal::(SEED); - #[allow(deprecated)] - Treasury::::propose_spend( - RawOrigin::Signed(caller).into(), - value, - beneficiary_lookup, - )?; - let proposal_id = Treasury::::proposal_count() - 1; - Approvals::::try_append(proposal_id).unwrap(); - let reject_origin = - T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - - #[extrinsic_call] - _(reject_origin as T::RuntimeOrigin, proposal_id); - - Ok(()) - } - - #[benchmark] - fn on_initialize_proposals( - p: Linear<0, { T::MaxApprovals::get() - 1 }>, - ) -> Result<(), BenchmarkError> { - setup_pot_account::(); - create_approved_proposals::(p)?; - - #[block] - { - Treasury::::on_initialize(0u32.into()); - } - - Ok(()) - } - - /// This benchmark is short-circuited if `SpendOrigin` cannot provide - /// a successful origin, in which case `spend` is un-callable and can use weight=0. - #[benchmark] - fn spend() -> Result<(), BenchmarkError> { - let origin = - T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let (asset_kind, amount, beneficiary, beneficiary_lookup) = - create_spend_arguments::(SEED); - T::BalanceConverter::ensure_successful(asset_kind.clone()); - - #[extrinsic_call] - _( - origin as T::RuntimeOrigin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - ); - - let valid_from = frame_system::Pallet::::block_number(); - let expire_at = valid_from.saturating_add(T::PayoutPeriod::get()); - assert_last_event::( - Event::AssetSpendApproved { - index: 0, - asset_kind, - amount, - beneficiary, - valid_from, - expire_at, - } - .into(), - ); - Ok(()) - } - - #[benchmark] - fn payout() -> Result<(), BenchmarkError> { - let (asset_kind, amount, beneficiary, beneficiary_lookup) = - create_spend_arguments::(SEED); - T::BalanceConverter::ensure_successful(asset_kind.clone()); - - let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; - - true - } else { - false - }; - - T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount); - let caller: T::AccountId = account("caller", 0, SEED); - - #[block] - { - let res = Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32); - - if spend_exists { - assert_ok!(res); - } else { - assert_err!(res, crate::Error::::InvalidIndex); - } - } - - if spend_exists { - let id = match Spends::::get(0).unwrap().status { - PaymentState::Attempted { id, .. } => { - assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); - id - } - _ => panic!("No payout attempt made"), - }; - assert_last_event::( - Event::Paid { - index: 0, - payment_id: id, - } - .into(), - ); - assert!(Treasury::::payout(RawOrigin::Signed(caller).into(), 0u32).is_err()); - } - - Ok(()) - } - - #[benchmark] - fn check_status() -> Result<(), BenchmarkError> { - let (asset_kind, amount, beneficiary, beneficiary_lookup) = - create_spend_arguments::(SEED); - - T::BalanceConverter::ensure_successful(asset_kind.clone()); - T::Paymaster::ensure_successful(&beneficiary, asset_kind.clone(), amount); - let caller: T::AccountId = account("caller", 0, SEED); - - let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { - Treasury::::spend( - origin, - Box::new(asset_kind), - amount, - Box::new(beneficiary_lookup), - None, - )?; - - Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32)?; - match Spends::::get(0).unwrap().status { - PaymentState::Attempted { id, .. } => { - T::Paymaster::ensure_concluded(id); - } - _ => panic!("No payout attempt made"), - }; - - true - } else { - false - }; - - #[block] - { - let res = - Treasury::::check_status(RawOrigin::Signed(caller.clone()).into(), 0u32); - - if spend_exists { - assert_ok!(res); - } else { - assert_err!(res, crate::Error::::InvalidIndex); - } - } - - if let Some(s) = Spends::::get(0) { - assert!(!matches!(s.status, PaymentState::Attempted { .. })); - } - - Ok(()) - } - - #[benchmark] - fn void_spend() -> Result<(), BenchmarkError> { - let (asset_kind, amount, _, beneficiary_lookup) = create_spend_arguments::(SEED); - T::BalanceConverter::ensure_successful(asset_kind.clone()); - let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; - assert!(Spends::::get(0).is_some()); - - true - } else { - false - }; - - let origin = - T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - - #[block] - { - let res = Treasury::::void_spend(origin as T::RuntimeOrigin, 0u32); - - if spend_exists { - assert_ok!(res); - } else { - assert_err!(res, crate::Error::::InvalidIndex); - } - } - - assert!(Spends::::get(0).is_none()); - Ok(()) - } - - impl_benchmark_test_suite!( - Treasury, - crate::tests::ExtBuilder::default().build(), - crate::tests::Test - ); - - mod no_spend_origin_tests { - use super::*; - - impl_benchmark_test_suite!( - Treasury, - crate::tests::ExtBuilder::default() - .spend_origin_succesful_origin_err() - .build(), - crate::tests::Test, - benchmarks_path = benchmarking - ); - } -} diff --git a/pallets/treasury/src/lib.rs b/pallets/treasury/src/lib.rs deleted file mode 100644 index fcf99f7188..0000000000 --- a/pallets/treasury/src/lib.rs +++ /dev/null @@ -1,1187 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! > Made with *Substrate*, for *Polkadot*. -//! -//! [![github]](https://github.com/paritytech/substrate/frame/fast-unstake) - -//! [![polkadot]](https://polkadot.network) -//! -//! [polkadot]: https://img.shields.io/badge/polkadot-E6007A?style=for-the-badge&logo=polkadot&logoColor=white -//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github -//! -//! # Treasury Pallet -//! -//! 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 Pallet itself provides the pot to store funds, and a means for stakeholders to -//! propose and claim expenditures (aka spends). The chain will need to provide a method to approve -//! spends (e.g. public referendum) and a method for collecting funds (e.g. inflation, fees). -//! -//! By way of example, stakeholders could vote to fund the Treasury with a portion of the block -//! reward and use the funds to pay developers. -//! -//! ### Terminology -//! -//! - **Proposal:** A suggestion to allocate funds from the pot to a beneficiary. -//! - **Beneficiary:** An account who will receive the funds from a proposal iff the proposal is -//! approved. -//! - **Pot:** Unspent funds accumulated by the treasury pallet. -//! - **Spend** An approved proposal for transferring a specific amount of funds to a designated -//! beneficiary. -//! -//! ### Example -//! -//! 1. Multiple local spends approved by spend origins and received by a beneficiary. -#![doc = docify::embed!("src/tests.rs", spend_local_origin_works)] -//! -//! 2. Approve a spend of some asset kind and claim it. -#![doc = docify::embed!("src/tests.rs", spend_payout_works)] -//! -//! ## Pallet API -//! -//! See the [`pallet`] module for more information about the interfaces this pallet exposes, -//! including its configuration trait, dispatchables, storage items, events and errors. -//! -//! ## Low Level / Implementation Details -//! -//! Spends can be initiated using either the `spend_local` or `spend` dispatchable. The -//! `spend_local` dispatchable enables the creation of spends using the native currency of the -//! chain, utilizing the funds stored in the pot. These spends are automatically paid out every -//! [`pallet::Config::SpendPeriod`]. On the other hand, the `spend` dispatchable allows spending of -//! any asset kind managed by the treasury, with payment facilitated by a designated -//! [`pallet::Config::Paymaster`]. To claim these spends, the `payout` dispatchable should be called -//! within some temporal bounds, starting from the moment they become valid and within one -//! [`pallet::Config::PayoutPeriod`]. - -#![cfg_attr(not(feature = "std"), no_std)] - -mod benchmarking; -#[cfg(test)] -mod tests; -pub mod weights; -use core::marker::PhantomData; - -#[cfg(feature = "runtime-benchmarks")] -pub use benchmarking::ArgumentsFactory; - -extern crate alloc; - -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; - -use alloc::{boxed::Box, collections::btree_map::BTreeMap}; -use sp_runtime::{ - traits::{AccountIdConversion, CheckedAdd, Saturating, StaticLookup, Zero}, - Permill, RuntimeDebug, -}; - -use frame_support::{ - dispatch::{DispatchResult, DispatchResultWithPostInfo}, - ensure, print, - traits::{ - tokens::Pay, Currency, ExistenceRequirement::KeepAlive, Get, Imbalance, OnUnbalanced, - ReservableCurrency, WithdrawReasons, - }, - weights::Weight, - PalletId, -}; - -pub use pallet::*; -pub use weights::WeightInfo; - -pub type BalanceOf = - <>::Currency as Currency<::AccountId>>::Balance; -pub type AssetBalanceOf = <>::Paymaster as Pay>::Balance; -pub type PositiveImbalanceOf = <>::Currency as Currency< - ::AccountId, ->>::PositiveImbalance; -pub type NegativeImbalanceOf = <>::Currency as Currency< - ::AccountId, ->>::NegativeImbalance; -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; -type BeneficiaryLookupOf = <>::BeneficiaryLookup as StaticLookup>::Source; - -/// 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: -/// * `budget_remaining`: How much available funds that can be spent by the treasury. As funds are -/// spent, you must correctly deduct from this value. -/// * `imbalance`: Any imbalances that you create should be subsumed in here to maximize efficiency -/// of updating the total issuance. (i.e. `deposit_creating`) -/// * `total_weight`: Track any weight that your `spend_fund` implementation uses by updating this -/// value. -/// * `missed_any`: If there were items that you want to spend on, but there were 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, I: 'static = ()> { - fn spend_funds( - budget_remaining: &mut BalanceOf, - imbalance: &mut PositiveImbalanceOf, - total_weight: &mut Weight, - missed_any: &mut bool, - ); -} - -/// An index of a proposal. Just a `u32`. -pub type ProposalIndex = u32; - -/// A spending proposal. -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo)] -pub struct Proposal { - /// The account proposing it. - proposer: AccountId, - /// The (total) amount that should be paid if the proposal is accepted. - value: Balance, - /// The account to whom the payment should be made if the proposal is accepted. - beneficiary: AccountId, - /// The amount held on deposit (reserved) for making this proposal. - bond: Balance, -} - -/// The state of the payment claim. -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo)] -pub enum PaymentState { - /// Pending claim. - Pending, - /// Payment attempted with a payment identifier. - Attempted { id: Id }, - /// Payment failed. - Failed, -} - -/// Info regarding an approved treasury spend. -#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -#[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo)] -pub struct SpendStatus { - // The kind of asset to be spent. - asset_kind: AssetKind, - /// The asset amount of the spend. - amount: AssetBalance, - /// The beneficiary of the spend. - beneficiary: Beneficiary, - /// The block number from which the spend can be claimed. - valid_from: BlockNumber, - /// The block number by which the spend has to be claimed. - expire_at: BlockNumber, - /// The status of the payout/claim. - status: PaymentState, -} - -/// Index of an approved treasury spend. -pub type SpendIndex = u32; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::{ - dispatch_context::with_context, - pallet_prelude::*, - traits::tokens::{ConversionFromAssetBalance, PaymentStatus}, - }; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The staking balance. - type Currency: Currency + ReservableCurrency; - - /// Origin from which approvals must come. - type ApproveOrigin: EnsureOrigin; - - /// Origin from which rejections must come. - type RejectOrigin: EnsureOrigin; - - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - - /// Handler for the unbalanced decrease when slashing for a rejected proposal or bounty. - type OnSlash: OnUnbalanced>; - - /// 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; - - /// Minimum amount of funds that should be placed in a deposit for making a proposal. - #[pallet::constant] - type ProposalBondMinimum: Get>; - - /// Maximum amount of funds that should be placed in a deposit for making a proposal. - #[pallet::constant] - type ProposalBondMaximum: Get>>; - - /// Period between successive spends. - #[pallet::constant] - type SpendPeriod: Get>; - - /// Percentage of spare funds (if any) that are burnt per spend period. - #[pallet::constant] - type Burn: Get; - - /// The treasury's pallet id, used for deriving its sovereign account ID. - #[pallet::constant] - type PalletId: Get; - - /// Handler for the unbalanced decrease when treasury funds are burned. - type BurnDestination: OnUnbalanced>; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; - - /// Runtime hooks to external pallet using treasury to compute spend funds. - type SpendFunds: SpendFunds; - - /// The maximum number of approvals that can wait in the spending queue. - /// - /// NOTE: This parameter is also used within the Bounties Pallet extension if enabled. - #[pallet::constant] - type MaxApprovals: Get; - - /// The origin required for approving spends from the treasury outside of the proposal - /// process. The `Success` value is the maximum amount in a native asset that this origin - /// is allowed to spend at a time. - type SpendOrigin: EnsureOrigin>; - - /// Type parameter representing the asset kinds to be spent from the treasury. - type AssetKind: Parameter + MaxEncodedLen; - - /// Type parameter used to identify the beneficiaries eligible to receive treasury spends. - type Beneficiary: Parameter + MaxEncodedLen; - - /// Converting trait to take a source type and convert to [`Self::Beneficiary`]. - type BeneficiaryLookup: StaticLookup; - - /// Type for processing spends of [Self::AssetKind] in favor of [`Self::Beneficiary`]. - type Paymaster: Pay; - - /// Type for converting the balance of an [Self::AssetKind] to the balance of the native - /// asset, solely for the purpose of asserting the result against the maximum allowed spend - /// amount of the [`Self::SpendOrigin`]. - type BalanceConverter: ConversionFromAssetBalance< - ::Balance, - Self::AssetKind, - BalanceOf, - >; - - /// The period during which an approved treasury spend has to be claimed. - #[pallet::constant] - type PayoutPeriod: Get>; - - /// Helper type for benchmarks. - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper: ArgumentsFactory; - } - - /// Number of proposals that have been made. - #[pallet::storage] - #[pallet::getter(fn proposal_count)] - pub(crate) type ProposalCount = StorageValue<_, ProposalIndex, ValueQuery>; - - /// Proposals that have been made. - #[pallet::storage] - #[pallet::getter(fn proposals)] - pub type Proposals, I: 'static = ()> = StorageMap< - _, - Twox64Concat, - ProposalIndex, - Proposal>, - OptionQuery, - >; - - /// The amount which has been reported as inactive to Currency. - #[pallet::storage] - pub type Deactivated, I: 'static = ()> = - StorageValue<_, BalanceOf, ValueQuery>; - - /// Proposal indices that have been approved but not yet awarded. - #[pallet::storage] - #[pallet::getter(fn approvals)] - pub type Approvals, I: 'static = ()> = - StorageValue<_, BoundedVec, ValueQuery>; - - /// The count of spends that have been made. - #[pallet::storage] - pub(crate) type SpendCount = StorageValue<_, SpendIndex, ValueQuery>; - - /// Spends that have been approved and being processed. - // Hasher: Twox safe since `SpendIndex` is an internal count based index. - #[pallet::storage] - pub type Spends, I: 'static = ()> = StorageMap< - _, - Twox64Concat, - SpendIndex, - SpendStatus< - T::AssetKind, - AssetBalanceOf, - T::Beneficiary, - BlockNumberFor, - ::Id, - >, - OptionQuery, - >; - - #[pallet::genesis_config] - #[derive(frame_support::DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - #[serde(skip)] - _config: core::marker::PhantomData<(T, I)>, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - // Create Treasury account - let account_id = >::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); - } - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// New proposal. - Proposed { proposal_index: ProposalIndex }, - /// We have ended a spend period and will now allocate funds. - Spending { budget_remaining: BalanceOf }, - /// Some funds have been allocated. - Awarded { - proposal_index: ProposalIndex, - award: BalanceOf, - account: T::AccountId, - }, - /// A proposal was rejected; funds were slashed. - Rejected { - proposal_index: ProposalIndex, - slashed: BalanceOf, - }, - /// Some of our funds have been burnt. - Burnt { burnt_funds: BalanceOf }, - /// Spending has finished; this is the amount that rolls over until next spend. - Rollover { rollover_balance: BalanceOf }, - /// Some funds have been deposited. - Deposit { value: BalanceOf }, - /// A new spend proposal has been approved. - SpendApproved { - proposal_index: ProposalIndex, - amount: BalanceOf, - beneficiary: T::AccountId, - }, - /// The inactive funds of the pallet have been updated. - UpdatedInactive { - reactivated: BalanceOf, - deactivated: BalanceOf, - }, - /// A new asset spend proposal has been approved. - AssetSpendApproved { - index: SpendIndex, - asset_kind: T::AssetKind, - amount: AssetBalanceOf, - beneficiary: T::Beneficiary, - valid_from: BlockNumberFor, - expire_at: BlockNumberFor, - }, - /// An approved spend was voided. - AssetSpendVoided { index: SpendIndex }, - /// A payment happened. - Paid { - index: SpendIndex, - payment_id: ::Id, - }, - /// A payment failed and can be retried. - PaymentFailed { - index: SpendIndex, - payment_id: ::Id, - }, - /// A spend was processed and removed from the storage. It might have been successfully - /// paid or it may have expired. - SpendProcessed { index: SpendIndex }, - } - - /// Error for the treasury pallet. - #[pallet::error] - pub enum Error { - /// Proposer's balance is too low. - InsufficientProposersBalance, - /// No proposal, bounty or spend at that index. - InvalidIndex, - /// Too many approvals in the queue. - TooManyApprovals, - /// The spend origin is valid but the amount it is allowed to spend is lower than the - /// amount to be spent. - InsufficientPermission, - /// Proposal has not been approved. - ProposalNotApproved, - /// The balance of the asset kind is not convertible to the balance of the native asset. - FailedToConvertBalance, - /// The spend has expired and cannot be claimed. - SpendExpired, - /// The spend is not yet eligible for payout. - EarlyPayout, - /// The payment has already been attempted. - AlreadyAttempted, - /// There was some issue with the mechanism of payment. - PayoutError, - /// The payout was not yet attempted/claimed. - NotAttempted, - /// The payment has neither failed nor succeeded yet. - Inconclusive, - } - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - /// ## Complexity - /// - `O(A)` where `A` is the number of approvals - fn on_initialize(n: frame_system::pallet_prelude::BlockNumberFor) -> Weight { - let pot = Self::pot(); - let deactivated = Deactivated::::get(); - if pot != deactivated { - T::Currency::reactivate(deactivated); - T::Currency::deactivate(pot); - Deactivated::::put(&pot); - Self::deposit_event(Event::::UpdatedInactive { - reactivated: deactivated, - deactivated: pot, - }); - } - - // Check to see if we should spend some funds! - if (n % T::SpendPeriod::get()).is_zero() { - Self::spend_funds() - } else { - Weight::zero() - } - } - - #[cfg(feature = "try-runtime")] - fn try_state( - _: frame_system::pallet_prelude::BlockNumberFor, - ) -> Result<(), sp_runtime::TryRuntimeError> { - Self::do_try_state()?; - Ok(()) - } - } - - #[derive(Default)] - struct SpendContext { - spend_in_context: BTreeMap, - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// Put forward a suggestion for spending. - /// - /// ## Dispatch Origin - /// - /// Must be signed. - /// - /// ## Details - /// A deposit proportional to the value is reserved and slashed if the proposal is rejected. - /// It is returned once the proposal is awarded. - /// - /// ### Complexity - /// - O(1) - /// - /// ## Events - /// - /// Emits [`Event::Proposed`] if successful. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::propose_spend())] - #[allow(deprecated)] - #[deprecated( - note = "`propose_spend` will be removed in February 2024. Use `spend` instead." - )] - pub fn propose_spend( - origin: OriginFor, - #[pallet::compact] value: BalanceOf, - beneficiary: AccountIdLookupOf, - ) -> 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::::InsufficientProposersBalance)?; - - let c = Self::proposal_count(); - >::put(c + 1); - >::insert( - c, - Proposal { - proposer, - value, - beneficiary, - bond, - }, - ); - - Self::deposit_event(Event::Proposed { proposal_index: c }); - Ok(()) - } - - /// Reject a proposed spend. - /// - /// ## Dispatch Origin - /// - /// Must be [`Config::RejectOrigin`]. - /// - /// ## Details - /// The original deposit will be slashed. - /// - /// ### Complexity - /// - O(1) - /// - /// ## Events - /// - /// Emits [`Event::Rejected`] if successful. - #[pallet::call_index(1)] - #[pallet::weight((T::WeightInfo::reject_proposal(), DispatchClass::Operational))] - #[allow(deprecated)] - #[deprecated( - note = "`reject_proposal` will be removed in February 2024. Use `spend` instead." - )] - pub fn reject_proposal( - origin: OriginFor, - #[pallet::compact] proposal_id: ProposalIndex, - ) -> DispatchResult { - T::RejectOrigin::ensure_origin(origin)?; - - let proposal = - >::take(&proposal_id).ok_or(Error::::InvalidIndex)?; - let value = proposal.bond; - let imbalance = T::Currency::slash_reserved(&proposal.proposer, value).0; - T::OnSlash::on_unbalanced(imbalance); - - Self::deposit_event(Event::::Rejected { - proposal_index: proposal_id, - slashed: value, - }); - Ok(()) - } - - /// Approve a proposal. - /// - /// ## Dispatch Origin - /// - /// Must be [`Config::ApproveOrigin`]. - /// - /// ## Details - /// - /// At a later time, the proposal will be allocated to the beneficiary and the original - /// deposit will be returned. - /// - /// ### Complexity - /// - O(1). - /// - /// ## Events - /// - /// No events are emitted from this dispatch. - #[pallet::call_index(2)] - #[pallet::weight((T::WeightInfo::approve_proposal(T::MaxApprovals::get()), DispatchClass::Operational))] - #[allow(deprecated)] - #[deprecated( - note = "`approve_proposal` will be removed in February 2024. Use `spend` instead." - )] - pub fn approve_proposal( - origin: OriginFor, - #[pallet::compact] proposal_id: ProposalIndex, - ) -> DispatchResult { - T::ApproveOrigin::ensure_origin(origin)?; - - ensure!( - >::contains_key(proposal_id), - Error::::InvalidIndex - ); - Approvals::::try_append(proposal_id) - .map_err(|_| Error::::TooManyApprovals)?; - Ok(()) - } - - /// Propose and approve a spend of treasury funds. - /// - /// ## Dispatch Origin - /// - /// Must be [`Config::SpendOrigin`] with the `Success` value being at least `amount`. - /// - /// ### Details - /// NOTE: For record-keeping purposes, the proposer is deemed to be equivalent to the - /// beneficiary. - /// - /// ### Parameters - /// - `amount`: The amount to be transferred from the treasury to the `beneficiary`. - /// - `beneficiary`: The destination account for the transfer. - /// - /// ## Events - /// - /// Emits [`Event::SpendApproved`] if successful. - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::spend_local())] - pub fn spend_local( - origin: OriginFor, - #[pallet::compact] amount: BalanceOf, - beneficiary: AccountIdLookupOf, - ) -> DispatchResult { - let max_amount = T::SpendOrigin::ensure_origin(origin)?; - ensure!(amount <= max_amount, Error::::InsufficientPermission); - - with_context::>, _>(|v| { - let context = v.or_default(); - - // We group based on `max_amount`, to distinguish between different kind of - // origins. (assumes that all origins have different `max_amount`) - // - // Worst case is that we reject some "valid" request. - let spend = context.spend_in_context.entry(max_amount).or_default(); - - // Ensure that we don't overflow nor use more than `max_amount` - if spend - .checked_add(&amount) - .map(|s| s > max_amount) - .unwrap_or(true) - { - Err(Error::::InsufficientPermission) - } else { - *spend = spend.saturating_add(amount); - - Ok(()) - } - }) - .unwrap_or(Ok(()))?; - - let beneficiary = T::Lookup::lookup(beneficiary)?; - let proposal_index = Self::proposal_count(); - Approvals::::try_append(proposal_index) - .map_err(|_| Error::::TooManyApprovals)?; - let proposal = Proposal { - proposer: beneficiary.clone(), - value: amount, - beneficiary: beneficiary.clone(), - bond: Default::default(), - }; - Proposals::::insert(proposal_index, proposal); - ProposalCount::::put(proposal_index + 1); - - Self::deposit_event(Event::SpendApproved { - proposal_index, - amount, - beneficiary, - }); - Ok(()) - } - - /// Force a previously approved proposal to be removed from the approval queue. - /// - /// ## Dispatch Origin - /// - /// Must be [`Config::RejectOrigin`]. - /// - /// ## Details - /// - /// The original deposit will no longer be returned. - /// - /// ### Parameters - /// - `proposal_id`: The index of a proposal - /// - /// ### Complexity - /// - O(A) where `A` is the number of approvals - /// - /// ### Errors - /// - [`Error::ProposalNotApproved`]: The `proposal_id` supplied was not found in the - /// approval queue, i.e., the proposal has not been approved. This could also mean the - /// proposal does not exist altogether, thus there is no way it would have been approved - /// in the first place. - #[pallet::call_index(4)] - #[pallet::weight((T::WeightInfo::remove_approval(), DispatchClass::Operational))] - pub fn remove_approval( - origin: OriginFor, - #[pallet::compact] proposal_id: ProposalIndex, - ) -> DispatchResult { - T::RejectOrigin::ensure_origin(origin)?; - - Approvals::::try_mutate(|v| -> DispatchResult { - if let Some(index) = v.iter().position(|x| x == &proposal_id) { - v.remove(index); - Ok(()) - } else { - Err(Error::::ProposalNotApproved.into()) - } - })?; - - Ok(()) - } - - /// Propose and approve a spend of treasury funds. - /// - /// ## Dispatch Origin - /// - /// Must be [`Config::SpendOrigin`] with the `Success` value being at least - /// `amount` of `asset_kind` in the native asset. The amount of `asset_kind` is converted - /// for assertion using the [`Config::BalanceConverter`]. - /// - /// ## Details - /// - /// Create an approved spend for transferring a specific `amount` of `asset_kind` to a - /// designated beneficiary. The spend must be claimed using the `payout` dispatchable within - /// the [`Config::PayoutPeriod`]. - /// - /// ### Parameters - /// - `asset_kind`: An indicator of the specific asset class to be spent. - /// - `amount`: The amount to be transferred from the treasury to the `beneficiary`. - /// - `beneficiary`: The beneficiary of the spend. - /// - `valid_from`: The block number from which the spend can be claimed. It can refer to - /// the past if the resulting spend has not yet expired according to the - /// [`Config::PayoutPeriod`]. If `None`, the spend can be claimed immediately after - /// approval. - /// - /// ## Events - /// - /// Emits [`Event::AssetSpendApproved`] if successful. - #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::spend())] - pub fn spend( - origin: OriginFor, - asset_kind: Box, - #[pallet::compact] amount: AssetBalanceOf, - beneficiary: Box>, - valid_from: Option>, - ) -> DispatchResult { - let max_amount = T::SpendOrigin::ensure_origin(origin)?; - let beneficiary = T::BeneficiaryLookup::lookup(*beneficiary)?; - - let now = frame_system::Pallet::::block_number(); - let valid_from = valid_from.unwrap_or(now); - let expire_at = valid_from.saturating_add(T::PayoutPeriod::get()); - ensure!(expire_at > now, Error::::SpendExpired); - - let native_amount = - T::BalanceConverter::from_asset_balance(amount, *asset_kind.clone()) - .map_err(|_| Error::::FailedToConvertBalance)?; - - ensure!( - native_amount <= max_amount, - Error::::InsufficientPermission - ); - - with_context::>, _>(|v| { - let context = v.or_default(); - // We group based on `max_amount`, to distinguish between different kind of - // origins. (assumes that all origins have different `max_amount`) - // - // Worst case is that we reject some "valid" request. - let spend = context.spend_in_context.entry(max_amount).or_default(); - - // Ensure that we don't overflow nor use more than `max_amount` - if spend - .checked_add(&native_amount) - .map(|s| s > max_amount) - .unwrap_or(true) - { - Err(Error::::InsufficientPermission) - } else { - *spend = spend.saturating_add(native_amount); - Ok(()) - } - }) - .unwrap_or(Ok(()))?; - - let index = SpendCount::::get(); - Spends::::insert( - index, - SpendStatus { - asset_kind: *asset_kind.clone(), - amount, - beneficiary: beneficiary.clone(), - valid_from, - expire_at, - status: PaymentState::Pending, - }, - ); - SpendCount::::put(index + 1); - - Self::deposit_event(Event::AssetSpendApproved { - index, - asset_kind: *asset_kind, - amount, - beneficiary, - valid_from, - expire_at, - }); - Ok(()) - } - - /// Claim a spend. - /// - /// ## Dispatch Origin - /// - /// Must be signed. - /// - /// ## Details - /// - /// Spends must be claimed within some temporal bounds. A spend may be claimed within one - /// [`Config::PayoutPeriod`] from the `valid_from` block. - /// In case of a payout failure, the spend status must be updated with the `check_status` - /// dispatchable before retrying with the current function. - /// - /// ### Parameters - /// - `index`: The spend index. - /// - /// ## Events - /// - /// Emits [`Event::Paid`] if successful. - #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::payout())] - pub fn payout(origin: OriginFor, index: SpendIndex) -> DispatchResult { - ensure_signed(origin)?; - let mut spend = Spends::::get(index).ok_or(Error::::InvalidIndex)?; - let now = frame_system::Pallet::::block_number(); - ensure!(now >= spend.valid_from, Error::::EarlyPayout); - ensure!(spend.expire_at > now, Error::::SpendExpired); - ensure!( - matches!(spend.status, PaymentState::Pending | PaymentState::Failed), - Error::::AlreadyAttempted - ); - - let id = T::Paymaster::pay(&spend.beneficiary, spend.asset_kind.clone(), spend.amount) - .map_err(|_| Error::::PayoutError)?; - - spend.status = PaymentState::Attempted { id }; - Spends::::insert(index, spend); - - Self::deposit_event(Event::::Paid { - index, - payment_id: id, - }); - - Ok(()) - } - - /// Check the status of the spend and remove it from the storage if processed. - /// - /// ## Dispatch Origin - /// - /// Must be signed. - /// - /// ## Details - /// - /// The status check is a prerequisite for retrying a failed payout. - /// If a spend has either succeeded or expired, it is removed from the storage by this - /// function. In such instances, transaction fees are refunded. - /// - /// ### Parameters - /// - `index`: The spend index. - /// - /// ## Events - /// - /// Emits [`Event::PaymentFailed`] if the spend payout has failed. - /// Emits [`Event::SpendProcessed`] if the spend payout has succeed. - #[pallet::call_index(7)] - #[pallet::weight(T::WeightInfo::check_status())] - pub fn check_status(origin: OriginFor, index: SpendIndex) -> DispatchResultWithPostInfo { - use PaymentState as State; - use PaymentStatus as Status; - - ensure_signed(origin)?; - let mut spend = Spends::::get(index).ok_or(Error::::InvalidIndex)?; - let now = frame_system::Pallet::::block_number(); - - if now > spend.expire_at && !matches!(spend.status, State::Attempted { .. }) { - // spend has expired and no further status update is expected. - Spends::::remove(index); - Self::deposit_event(Event::::SpendProcessed { index }); - return Ok(Pays::No.into()); - } - - let payment_id = match spend.status { - State::Attempted { id } => id, - _ => return Err(Error::::NotAttempted.into()), - }; - - match T::Paymaster::check_payment(payment_id) { - Status::Failure => { - spend.status = PaymentState::Failed; - Spends::::insert(index, spend); - Self::deposit_event(Event::::PaymentFailed { index, payment_id }); - } - Status::Success | Status::Unknown => { - Spends::::remove(index); - Self::deposit_event(Event::::SpendProcessed { index }); - return Ok(Pays::No.into()); - } - Status::InProgress => return Err(Error::::Inconclusive.into()), - } - return Ok(Pays::Yes.into()); - } - - /// Void previously approved spend. - /// - /// ## Dispatch Origin - /// - /// Must be [`Config::RejectOrigin`]. - /// - /// ## Details - /// - /// A spend void is only possible if the payout has not been attempted yet. - /// - /// ### Parameters - /// - `index`: The spend index. - /// - /// ## Events - /// - /// Emits [`Event::AssetSpendVoided`] if successful. - #[pallet::call_index(8)] - #[pallet::weight(T::WeightInfo::void_spend())] - pub fn void_spend(origin: OriginFor, index: SpendIndex) -> DispatchResult { - T::RejectOrigin::ensure_origin(origin)?; - let spend = Spends::::get(index).ok_or(Error::::InvalidIndex)?; - ensure!( - matches!(spend.status, PaymentState::Pending | PaymentState::Failed), - Error::::AlreadyAttempted - ); - - Spends::::remove(index); - Self::deposit_event(Event::::AssetSpendVoided { index }); - Ok(()) - } - } -} - -impl, I: 'static> Pallet { - // Add public immutables and private mutables. - - /// The account ID of the treasury pot. - /// - /// This actually does computation. If you need to keep using it, then make sure you cache the - /// value and only call this once. - pub fn account_id() -> T::AccountId { - T::PalletId::get().into_account_truncating() - } - - /// The needed bond for a proposal whose spend is `value`. - fn calculate_bond(value: BalanceOf) -> BalanceOf { - let mut r = T::ProposalBondMinimum::get().max(T::ProposalBond::get() * value); - if let Some(m) = T::ProposalBondMaximum::get() { - r = r.min(m); - } - r - } - - /// Spend some money! returns number of approvals before spend. - pub fn spend_funds() -> Weight { - let mut total_weight = Weight::zero(); - - let mut budget_remaining = Self::pot(); - Self::deposit_event(Event::Spending { budget_remaining }); - let account_id = Self::account_id(); - - let mut missed_any = false; - let mut imbalance = >::zero(); - let proposals_len = Approvals::::mutate(|v| { - let proposals_approvals_len = v.len() as u32; - v.retain(|&index| { - // Should always be true, but shouldn't panic if false or we're screwed. - if let Some(p) = Self::proposals(index) { - if p.value <= budget_remaining { - budget_remaining -= p.value; - >::remove(index); - - // return their deposit. - let err_amount = T::Currency::unreserve(&p.proposer, p.bond); - debug_assert!(err_amount.is_zero()); - - // provide the allocation. - imbalance.subsume(T::Currency::deposit_creating(&p.beneficiary, p.value)); - - Self::deposit_event(Event::Awarded { - proposal_index: index, - award: p.value, - account: p.beneficiary, - }); - false - } else { - missed_any = true; - true - } - } else { - false - } - }); - proposals_approvals_len - }); - - total_weight += T::WeightInfo::on_initialize_proposals(proposals_len); - - // Call Runtime hooks to external pallet using treasury to compute spend funds. - T::SpendFunds::spend_funds( - &mut budget_remaining, - &mut imbalance, - &mut total_weight, - &mut missed_any, - ); - - if !missed_any { - // burn some proportion of the remaining budget if we run a surplus. - let burn = (T::Burn::get() * budget_remaining).min(budget_remaining); - budget_remaining -= burn; - - let (debit, credit) = T::Currency::pair(burn); - imbalance.subsume(debit); - T::BurnDestination::on_unbalanced(credit); - Self::deposit_event(Event::Burnt { burnt_funds: burn }) - } - - // Must never be an error, but better to be safe. - // proof: budget_remaining is account free balance minus ED; - // Thus we can't spend more than account free balance minus ED; - // Thus account is kept alive; qed; - if let Err(problem) = - T::Currency::settle(&account_id, imbalance, WithdrawReasons::TRANSFER, KeepAlive) - { - print("Inconsistent state - couldn't settle imbalance for funds spent by treasury"); - // Nothing else to do here. - drop(problem); - } - - Self::deposit_event(Event::Rollover { - rollover_balance: budget_remaining, - }); - - total_weight - } - - /// Return the amount of money in the pot. - // The existential deposit is not part of the pot so treasury account never gets deleted. - pub fn pot() -> BalanceOf { - T::Currency::free_balance(&Self::account_id()) - // Must never be less than 0 but better be safe. - .saturating_sub(T::Currency::minimum_balance()) - } - - /// Ensure the correctness of the state of this pallet. - #[cfg(any(feature = "try-runtime", test))] - fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { - Self::try_state_proposals()?; - Self::try_state_spends()?; - - Ok(()) - } - - /// ### Invariants of proposal storage items - /// - /// 1. [`ProposalCount`] >= Number of elements in [`Proposals`]. - /// 2. Each entry in [`Proposals`] should be saved under a key strictly less than current - /// [`ProposalCount`]. - /// 3. Each [`ProposalIndex`] contained in [`Approvals`] should exist in [`Proposals`]. - /// Note, that this automatically implies [`Approvals`].count() <= [`Proposals`].count(). - #[cfg(any(feature = "try-runtime", test))] - fn try_state_proposals() -> Result<(), sp_runtime::TryRuntimeError> { - let current_proposal_count = ProposalCount::::get(); - ensure!( - current_proposal_count as usize >= Proposals::::iter().count(), - "Actual number of proposals exceeds `ProposalCount`." - ); - - Proposals::::iter_keys().try_for_each(|proposal_index| -> DispatchResult { - ensure!( - current_proposal_count as u32 > proposal_index, - "`ProposalCount` should by strictly greater than any ProposalIndex used as a key for `Proposals`." - ); - Ok(()) - })?; - - Approvals::::get() - .iter() - .try_for_each(|proposal_index| -> DispatchResult { - ensure!( - Proposals::::contains_key(proposal_index), - "Proposal indices in `Approvals` must also be contained in `Proposals`." - ); - Ok(()) - })?; - - Ok(()) - } - - /// ## Invariants of spend storage items - /// - /// 1. [`SpendCount`] >= Number of elements in [`Spends`]. - /// 2. Each entry in [`Spends`] should be saved under a key strictly less than current - /// [`SpendCount`]. - /// 3. For each spend entry contained in [`Spends`] we should have spend.expire_at - /// > spend.valid_from. - #[cfg(any(feature = "try-runtime", test))] - fn try_state_spends() -> Result<(), sp_runtime::TryRuntimeError> { - let current_spend_count = SpendCount::::get(); - ensure!( - current_spend_count as usize >= Spends::::iter().count(), - "Actual number of spends exceeds `SpendCount`." - ); - - Spends::::iter_keys().try_for_each(|spend_index| -> DispatchResult { - ensure!( - current_spend_count > spend_index, - "`SpendCount` should by strictly greater than any SpendIndex used as a key for `Spends`." - ); - Ok(()) - })?; - - Spends::::iter().try_for_each(|(_index, spend)| -> DispatchResult { - ensure!( - spend.valid_from < spend.expire_at, - "Spend cannot expire before it becomes valid." - ); - Ok(()) - })?; - - Ok(()) - } -} - -impl, I: 'static> OnUnbalanced> for Pallet { - fn on_nonzero_unbalanced(amount: NegativeImbalanceOf) { - 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(Event::Deposit { - value: numeric_amount, - }); - } -} - -/// TypedGet implementation to get the AccountId of the Treasury. -pub struct TreasuryAccountId(PhantomData); -impl sp_runtime::traits::TypedGet for TreasuryAccountId -where - R: crate::Config, -{ - type Type = ::AccountId; - fn get() -> Self::Type { - >::account_id() - } -} diff --git a/pallets/treasury/src/tests.rs b/pallets/treasury/src/tests.rs deleted file mode 100644 index 2bda67d364..0000000000 --- a/pallets/treasury/src/tests.rs +++ /dev/null @@ -1,1321 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Treasury pallet tests. - -#![cfg(test)] - -use core::{cell::RefCell, marker::PhantomData}; -use sp_runtime::{ - traits::{BadOrigin, Dispatchable, IdentityLookup}, - BuildStorage, -}; - -use frame_support::{ - assert_err_ignore_postinfo, assert_noop, assert_ok, derive_impl, - pallet_prelude::Pays, - parameter_types, - traits::{ - tokens::{ConversionFromAssetBalance, PaymentStatus}, - ConstU32, ConstU64, OnInitialize, - }, - PalletId, -}; - -use super::*; -use crate as treasury; - -type Block = frame_system::mocking::MockBlock; -type UtilityCall = pallet_utility::Call; -type TreasuryCall = crate::Call; - -frame_support::construct_runtime!( - pub enum Test - { - System: frame_system, - Balances: pallet_balances, - Treasury: treasury, - Utility: pallet_utility, - } -); - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type AccountId = u128; // u64 is not enough to hold bytes used to generate bounty account - type Lookup = IdentityLookup; - type Block = Block; - type AccountData = pallet_balances::AccountData; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for Test { - type AccountStore = System; -} - -impl pallet_utility::Config for Test { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type PalletsOrigin = OriginCaller; - type WeightInfo = (); -} - -thread_local! { - pub static PAID: RefCell> = RefCell::new(BTreeMap::new()); - pub static STATUS: RefCell> = RefCell::new(BTreeMap::new()); - pub static LAST_ID: RefCell = RefCell::new(0u64); - - #[cfg(feature = "runtime-benchmarks")] - pub static TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR: RefCell = RefCell::new(false); -} - -/// paid balance for a given account and asset ids -fn paid(who: u128, asset_id: u32) -> u64 { - PAID.with(|p| p.borrow().get(&(who, asset_id)).cloned().unwrap_or(0)) -} - -/// reduce paid balance for a given account and asset ids -fn unpay(who: u128, asset_id: u32, amount: u64) { - PAID.with(|p| { - p.borrow_mut() - .entry((who, asset_id)) - .or_default() - .saturating_reduce(amount) - }) -} - -/// set status for a given payment id -fn set_status(id: u64, s: PaymentStatus) { - STATUS.with(|m| m.borrow_mut().insert(id, s)); -} - -pub struct TestPay; -impl Pay for TestPay { - type Beneficiary = u128; - type Balance = u64; - type Id = u64; - type AssetKind = u32; - type Error = (); - - fn pay( - who: &Self::Beneficiary, - asset_kind: Self::AssetKind, - amount: Self::Balance, - ) -> Result { - PAID.with(|paid| *paid.borrow_mut().entry((*who, asset_kind)).or_default() += amount); - Ok(LAST_ID.with(|lid| { - let x = *lid.borrow(); - lid.replace(x + 1); - x - })) - } - fn check_payment(id: Self::Id) -> PaymentStatus { - STATUS.with(|s| { - s.borrow() - .get(&id) - .cloned() - .unwrap_or(PaymentStatus::Unknown) - }) - } - #[cfg(feature = "runtime-benchmarks")] - fn ensure_successful(_: &Self::Beneficiary, _: Self::AssetKind, _: Self::Balance) {} - #[cfg(feature = "runtime-benchmarks")] - fn ensure_concluded(id: Self::Id) { - set_status(id, PaymentStatus::Failure) - } -} - -parameter_types! { - pub const ProposalBond: Permill = Permill::from_percent(5); - pub const Burn: Permill = Permill::from_percent(50); - pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); - pub TreasuryAccount: u128 = Treasury::account_id(); - pub const SpendPayoutPeriod: u64 = 5; -} - -pub struct TestSpendOrigin; -impl frame_support::traits::EnsureOrigin for TestSpendOrigin { - type Success = u64; - fn try_origin(o: RuntimeOrigin) -> Result { - Result::, RuntimeOrigin>::from(o).and_then(|o| match o { - frame_system::RawOrigin::Root => Ok(u64::max_value()), - frame_system::RawOrigin::Signed(10) => Ok(5), - frame_system::RawOrigin::Signed(11) => Ok(10), - frame_system::RawOrigin::Signed(12) => Ok(20), - frame_system::RawOrigin::Signed(13) => Ok(50), - r => Err(RuntimeOrigin::from(r)), - }) - } - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - if TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow()) { - Err(()) - } else { - Ok(frame_system::RawOrigin::Root.into()) - } - } -} - -pub struct MulBy(PhantomData); -impl> ConversionFromAssetBalance for MulBy { - type Error = (); - fn from_asset_balance(balance: u64, _asset_id: u32) -> Result { - return balance.checked_mul(N::get()).ok_or(()); - } - #[cfg(feature = "runtime-benchmarks")] - fn ensure_successful(_: u32) {} -} - -impl Config for Test { - type PalletId = TreasuryPalletId; - type Currency = pallet_balances::Pallet; - type ApproveOrigin = frame_system::EnsureRoot; - type RejectOrigin = frame_system::EnsureRoot; - type RuntimeEvent = RuntimeEvent; - type OnSlash = (); - type ProposalBond = ProposalBond; - type ProposalBondMinimum = ConstU64<1>; - type ProposalBondMaximum = (); - type SpendPeriod = ConstU64<2>; - type Burn = Burn; - type BurnDestination = (); // Just gets burned. - type WeightInfo = (); - type SpendFunds = (); - type MaxApprovals = ConstU32<100>; - type SpendOrigin = TestSpendOrigin; - type AssetKind = u32; - type Beneficiary = u128; - type BeneficiaryLookup = IdentityLookup; - type Paymaster = TestPay; - type BalanceConverter = MulBy>; - type PayoutPeriod = SpendPayoutPeriod; - #[cfg(feature = "runtime-benchmarks")] - type BenchmarkHelper = (); -} - -pub struct ExtBuilder {} - -impl Default for ExtBuilder { - fn default() -> Self { - #[cfg(feature = "runtime-benchmarks")] - TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow_mut() = false); - - Self {} - } -} - -impl ExtBuilder { - #[cfg(feature = "runtime-benchmarks")] - pub fn spend_origin_succesful_origin_err(self) -> Self { - TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow_mut() = true); - self - } - - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - pallet_balances::GenesisConfig:: { - // Total issuance will be 200 with treasury account initialized at ED. - balances: vec![(0, 100), (1, 98), (2, 1)], - } - .assimilate_storage(&mut t) - .unwrap(); - crate::GenesisConfig::::default() - .assimilate_storage(&mut t) - .unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} - -fn get_payment_id(i: SpendIndex) -> Option { - let spend = Spends::::get(i).expect("no spend"); - match spend.status { - PaymentState::Attempted { id } => Some(id), - _ => None, - } -} - -#[test] -fn genesis_config_works() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(Treasury::pot(), 0); - assert_eq!(Treasury::proposal_count(), 0); - }); -} - -#[test] -fn spend_local_origin_permissioning_works() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - Treasury::spend_local(RuntimeOrigin::signed(1), 1, 1), - BadOrigin - ); - assert_noop!( - Treasury::spend_local(RuntimeOrigin::signed(10), 6, 1), - Error::::InsufficientPermission - ); - assert_noop!( - Treasury::spend_local(RuntimeOrigin::signed(11), 11, 1), - Error::::InsufficientPermission - ); - assert_noop!( - Treasury::spend_local(RuntimeOrigin::signed(12), 21, 1), - Error::::InsufficientPermission - ); - assert_noop!( - Treasury::spend_local(RuntimeOrigin::signed(13), 51, 1), - Error::::InsufficientPermission - ); - }); -} - -#[docify::export] -#[test] -fn spend_local_origin_works() { - ExtBuilder::default().build().execute_with(|| { - // Check that accumulate works when we have Some value in Dummy already. - Balances::make_free_balance_be(&Treasury::account_id(), 101); - // approve spend of some amount to beneficiary `6`. - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(10), 5, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(11), 10, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(12), 20, 6)); - assert_ok!(Treasury::spend_local(RuntimeOrigin::signed(13), 50, 6)); - // free balance of `6` is zero, spend period has not passed. - >::on_initialize(1); - assert_eq!(Balances::free_balance(6), 0); - // free balance of `6` is `100`, spend period has passed. - >::on_initialize(2); - assert_eq!(Balances::free_balance(6), 100); - // `100` spent, `1` burned. - assert_eq!(Treasury::pot(), 0); - }); -} - -#[test] -fn minting_works() { - ExtBuilder::default().build().execute_with(|| { - // Check that accumulate works when we have Some value in Dummy already. - Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot(), 100); - }); -} - -#[test] -fn spend_proposal_takes_min_deposit() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 1, 3) - }); - assert_eq!(Balances::free_balance(0), 99); - assert_eq!(Balances::reserved_balance(0), 1); - }); -} - -#[test] -fn spend_proposal_takes_proportional_deposit() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_eq!(Balances::free_balance(0), 95); - assert_eq!(Balances::reserved_balance(0), 5); - }); -} - -#[test] -fn spend_proposal_fails_when_proposer_poor() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - { - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(2), 100, 3) - }, - Error::::InsufficientProposersBalance, - ); - }); -} - -#[test] -fn accepted_spend_proposal_ignored_outside_spend_period() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }); - - >::on_initialize(1); - assert_eq!(Balances::free_balance(3), 0); - assert_eq!(Treasury::pot(), 100); - }); -} - -#[test] -fn unused_pot_should_diminish() { - ExtBuilder::default().build().execute_with(|| { - let init_total_issuance = Balances::total_issuance(); - Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Balances::total_issuance(), init_total_issuance + 100); - - >::on_initialize(2); - assert_eq!(Treasury::pot(), 50); - assert_eq!(Balances::total_issuance(), init_total_issuance + 50); - }); -} - -#[test] -fn rejected_spend_proposal_ignored_on_spend_period() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::reject_proposal(RuntimeOrigin::root(), 0) - }); - - >::on_initialize(2); - assert_eq!(Balances::free_balance(3), 0); - assert_eq!(Treasury::pot(), 50); - }); -} - -#[test] -fn reject_already_rejected_spend_proposal_fails() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::reject_proposal(RuntimeOrigin::root(), 0) - }); - assert_noop!( - { - #[allow(deprecated)] - Treasury::reject_proposal(RuntimeOrigin::root(), 0) - }, - Error::::InvalidIndex - ); - }); -} - -#[test] -fn reject_non_existent_spend_proposal_fails() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - { - #[allow(deprecated)] - Treasury::reject_proposal(RuntimeOrigin::root(), 0) - }, - Error::::InvalidIndex - ); - }); -} - -#[test] -fn accept_non_existent_spend_proposal_fails() { - ExtBuilder::default().build().execute_with(|| { - assert_noop!( - { - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }, - Error::::InvalidIndex - ); - }); -} - -#[test] -fn accept_already_rejected_spend_proposal_fails() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::reject_proposal(RuntimeOrigin::root(), 0) - }); - assert_noop!( - { - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }, - Error::::InvalidIndex - ); - }); -} - -#[test] -fn accepted_spend_proposal_enacted_on_spend_period() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot(), 100); - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }); - - >::on_initialize(2); - assert_eq!(Balances::free_balance(3), 100); - assert_eq!(Treasury::pot(), 0); - }); -} - -#[test] -fn pot_underflow_should_not_diminish() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot(), 100); - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 150, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }); - - >::on_initialize(2); - assert_eq!(Treasury::pot(), 100); // Pot hasn't changed - - let _ = Balances::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); - >::on_initialize(4); - assert_eq!(Balances::free_balance(3), 150); // Fund has been spent - assert_eq!(Treasury::pot(), 25); // Pot has finally changed - }); -} - -// Treasury account doesn't get deleted if amount approved to spend is all its free balance. -// i.e. pot should not include existential deposit needed for account survival. -#[test] -fn treasury_account_doesnt_get_deleted() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot(), 100); - let treasury_balance = Balances::free_balance(&Treasury::account_id()); - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), treasury_balance, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }); - - >::on_initialize(2); - assert_eq!(Treasury::pot(), 100); // Pot hasn't changed - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), Treasury::pot(), 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 1) - }); - - >::on_initialize(4); - assert_eq!(Treasury::pot(), 0); // Pot is emptied - assert_eq!(Balances::free_balance(Treasury::account_id()), 1); // but the account is still there - }); -} - -// In case treasury account is not existing then it works fine. -// This is useful for chain that will just update runtime. -#[test] -fn inexistent_account_works() { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![(0, 100), (1, 99), (2, 1)], - } - .assimilate_storage(&mut t) - .unwrap(); - // Treasury genesis config is not build thus treasury account does not exist - let mut t: sp_io::TestExternalities = t.into(); - - t.execute_with(|| { - assert_eq!(Balances::free_balance(Treasury::account_id()), 0); // Account does not exist - assert_eq!(Treasury::pot(), 0); // Pot is empty - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 99, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 1, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 1) - }); - >::on_initialize(2); - assert_eq!(Treasury::pot(), 0); // Pot hasn't changed - assert_eq!(Balances::free_balance(3), 0); // Balance of `3` hasn't changed - - Balances::make_free_balance_be(&Treasury::account_id(), 100); - assert_eq!(Treasury::pot(), 99); // Pot now contains funds - assert_eq!(Balances::free_balance(Treasury::account_id()), 100); // Account does exist - - >::on_initialize(4); - - assert_eq!(Treasury::pot(), 0); // Pot has changed - assert_eq!(Balances::free_balance(3), 99); // Balance of `3` has changed - }); -} - -#[test] -fn genesis_funding_works() { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .unwrap(); - let initial_funding = 100; - pallet_balances::GenesisConfig:: { - // 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(); - crate::GenesisConfig::::default() - .assimilate_storage(&mut t) - .unwrap(); - let mut t: sp_io::TestExternalities = t.into(); - - t.execute_with(|| { - assert_eq!( - Balances::free_balance(Treasury::account_id()), - initial_funding - ); - assert_eq!( - Treasury::pot(), - initial_funding - Balances::minimum_balance() - ); - }); -} - -#[test] -fn max_approvals_limited() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), u64::MAX); - Balances::make_free_balance_be(&0, u64::MAX); - - for _ in 0..::MaxApprovals::get() { - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }); - } - - // One too many will fail - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_noop!( - { - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }, - Error::::TooManyApprovals - ); - }); -} - -#[test] -fn remove_already_removed_approval_fails() { - ExtBuilder::default().build().execute_with(|| { - Balances::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 100, 3) - }); - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }); - assert_eq!(Treasury::approvals(), vec![0]); - assert_ok!(Treasury::remove_approval(RuntimeOrigin::root(), 0)); - assert_eq!(Treasury::approvals(), vec![]); - - assert_noop!( - Treasury::remove_approval(RuntimeOrigin::root(), 0), - Error::::ProposalNotApproved - ); - }); -} - -#[test] -fn spending_local_in_batch_respects_max_total() { - ExtBuilder::default().build().execute_with(|| { - // Respect the `max_total` for the given origin. - assert_ok!(RuntimeCall::from(UtilityCall::batch_all { - calls: vec![ - RuntimeCall::from(TreasuryCall::spend_local { - amount: 2, - beneficiary: 100 - }), - RuntimeCall::from(TreasuryCall::spend_local { - amount: 2, - beneficiary: 101 - }) - ] - }) - .dispatch(RuntimeOrigin::signed(10))); - - assert_err_ignore_postinfo!( - RuntimeCall::from(UtilityCall::batch_all { - calls: vec![ - RuntimeCall::from(TreasuryCall::spend_local { - amount: 2, - beneficiary: 100 - }), - RuntimeCall::from(TreasuryCall::spend_local { - amount: 4, - beneficiary: 101 - }) - ] - }) - .dispatch(RuntimeOrigin::signed(10)), - Error::::InsufficientPermission - ); - }) -} - -#[test] -fn spending_in_batch_respects_max_total() { - ExtBuilder::default().build().execute_with(|| { - // Respect the `max_total` for the given origin. - assert_ok!(RuntimeCall::from(UtilityCall::batch_all { - calls: vec![ - RuntimeCall::from(TreasuryCall::spend { - asset_kind: Box::new(1), - amount: 1, - beneficiary: Box::new(100), - valid_from: None, - }), - RuntimeCall::from(TreasuryCall::spend { - asset_kind: Box::new(1), - amount: 1, - beneficiary: Box::new(101), - valid_from: None, - }) - ] - }) - .dispatch(RuntimeOrigin::signed(10))); - - assert_err_ignore_postinfo!( - RuntimeCall::from(UtilityCall::batch_all { - calls: vec![ - RuntimeCall::from(TreasuryCall::spend { - asset_kind: Box::new(1), - amount: 2, - beneficiary: Box::new(100), - valid_from: None, - }), - RuntimeCall::from(TreasuryCall::spend { - asset_kind: Box::new(1), - amount: 2, - beneficiary: Box::new(101), - valid_from: None, - }) - ] - }) - .dispatch(RuntimeOrigin::signed(10)), - Error::::InsufficientPermission - ); - }) -} - -#[test] -fn spend_origin_works() { - ExtBuilder::default().build().execute_with(|| { - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 1, - Box::new(6), - None - )); - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - assert_noop!( - Treasury::spend(RuntimeOrigin::signed(10), Box::new(1), 3, Box::new(6), None), - Error::::InsufficientPermission - ); - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(11), - Box::new(1), - 5, - Box::new(6), - None - )); - assert_noop!( - Treasury::spend(RuntimeOrigin::signed(11), Box::new(1), 6, Box::new(6), None), - Error::::InsufficientPermission - ); - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(12), - Box::new(1), - 10, - Box::new(6), - None - )); - assert_noop!( - Treasury::spend( - RuntimeOrigin::signed(12), - Box::new(1), - 11, - Box::new(6), - None - ), - Error::::InsufficientPermission - ); - - assert_eq!(SpendCount::::get(), 4); - assert_eq!(Spends::::iter().count(), 4); - }); -} - -#[test] -fn spend_works() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - - assert_eq!(SpendCount::::get(), 1); - assert_eq!( - Spends::::get(0).unwrap(), - SpendStatus { - asset_kind: 1, - amount: 2, - beneficiary: 6, - valid_from: 1, - expire_at: 6, - status: PaymentState::Pending, - } - ); - System::assert_last_event( - Event::::AssetSpendApproved { - index: 0, - asset_kind: 1, - amount: 2, - beneficiary: 6, - valid_from: 1, - expire_at: 6, - } - .into(), - ); - }); -} - -#[test] -fn spend_expires() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(::PayoutPeriod::get(), 5); - - // spend `0` expires in 5 blocks after the creating. - System::set_block_number(1); - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - System::set_block_number(6); - assert_noop!( - Treasury::payout(RuntimeOrigin::signed(1), 0), - Error::::SpendExpired - ); - - // spend cannot be approved since its already expired. - assert_noop!( - Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - Some(0) - ), - Error::::SpendExpired - ); - }); -} - -#[docify::export] -#[test] -fn spend_payout_works() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - // approve a `2` coins spend of asset `1` to beneficiary `6`, the spend valid from now. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - // payout the spend. - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 0)); - // beneficiary received `2` coins of asset `1`. - assert_eq!(paid(6, 1), 2); - assert_eq!(SpendCount::::get(), 1); - let payment_id = get_payment_id(0).expect("no payment attempt"); - System::assert_last_event( - Event::::Paid { - index: 0, - payment_id, - } - .into(), - ); - set_status(payment_id, PaymentStatus::Success); - // the payment succeed. - assert_ok!(Treasury::check_status(RuntimeOrigin::signed(1), 0)); - System::assert_last_event(Event::::SpendProcessed { index: 0 }.into()); - // cannot payout the same spend twice. - assert_noop!( - Treasury::payout(RuntimeOrigin::signed(1), 0), - Error::::InvalidIndex - ); - }); -} - -#[test] -fn payout_retry_works() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 0)); - assert_eq!(paid(6, 1), 2); - let payment_id = get_payment_id(0).expect("no payment attempt"); - // spend payment is failed - set_status(payment_id, PaymentStatus::Failure); - unpay(6, 1, 2); - // cannot payout a spend in the attempted state - assert_noop!( - Treasury::payout(RuntimeOrigin::signed(1), 0), - Error::::AlreadyAttempted - ); - // check status and update it to retry the payout again - assert_ok!(Treasury::check_status(RuntimeOrigin::signed(1), 0)); - System::assert_last_event( - Event::::PaymentFailed { - index: 0, - payment_id, - } - .into(), - ); - // the payout can be retried now - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 0)); - assert_eq!(paid(6, 1), 2); - }); -} - -#[test] -fn spend_valid_from_works() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(::PayoutPeriod::get(), 5); - System::set_block_number(1); - - // spend valid from block `2`. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - Some(2) - )); - assert_noop!( - Treasury::payout(RuntimeOrigin::signed(1), 0), - Error::::EarlyPayout - ); - System::set_block_number(2); - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 0)); - - System::set_block_number(5); - // spend approved even if `valid_from` in the past since the payout period has not passed. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - Some(4) - )); - // spend paid. - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 1)); - }); -} - -#[test] -fn void_spend_works() { - ExtBuilder::default().build().execute_with(|| { - System::set_block_number(1); - // spend cannot be voided if already attempted. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - Some(1) - )); - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 0)); - assert_noop!( - Treasury::void_spend(RuntimeOrigin::root(), 0), - Error::::AlreadyAttempted - ); - - // void spend. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - Some(10) - )); - assert_ok!(Treasury::void_spend(RuntimeOrigin::root(), 1)); - assert_eq!(Spends::::get(1), None); - }); -} - -#[test] -fn check_status_works() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(::PayoutPeriod::get(), 5); - System::set_block_number(1); - - // spend `0` expired and can be removed. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - System::set_block_number(7); - let info = Treasury::check_status(RuntimeOrigin::signed(1), 0).unwrap(); - assert_eq!(info.pays_fee, Pays::No); - System::assert_last_event(Event::::SpendProcessed { index: 0 }.into()); - - // spend `1` payment failed and expired hence can be removed. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - assert_noop!( - Treasury::check_status(RuntimeOrigin::signed(1), 1), - Error::::NotAttempted - ); - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 1)); - let payment_id = get_payment_id(1).expect("no payment attempt"); - set_status(payment_id, PaymentStatus::Failure); - // spend expired. - System::set_block_number(13); - let info = Treasury::check_status(RuntimeOrigin::signed(1), 1).unwrap(); - assert_eq!(info.pays_fee, Pays::Yes); - System::assert_last_event( - Event::::PaymentFailed { - index: 1, - payment_id, - } - .into(), - ); - let info = Treasury::check_status(RuntimeOrigin::signed(1), 1).unwrap(); - assert_eq!(info.pays_fee, Pays::No); - System::assert_last_event(Event::::SpendProcessed { index: 1 }.into()); - - // spend `2` payment succeed. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 2)); - let payment_id = get_payment_id(2).expect("no payment attempt"); - set_status(payment_id, PaymentStatus::Success); - let info = Treasury::check_status(RuntimeOrigin::signed(1), 2).unwrap(); - assert_eq!(info.pays_fee, Pays::No); - System::assert_last_event(Event::::SpendProcessed { index: 2 }.into()); - - // spend `3` payment in process. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 3)); - let payment_id = get_payment_id(3).expect("no payment attempt"); - set_status(payment_id, PaymentStatus::InProgress); - assert_noop!( - Treasury::check_status(RuntimeOrigin::signed(1), 3), - Error::::Inconclusive - ); - - // spend `4` removed since the payment status is unknown. - assert_ok!(Treasury::spend( - RuntimeOrigin::signed(10), - Box::new(1), - 2, - Box::new(6), - None - )); - assert_ok!(Treasury::payout(RuntimeOrigin::signed(1), 4)); - let payment_id = get_payment_id(4).expect("no payment attempt"); - set_status(payment_id, PaymentStatus::Unknown); - let info = Treasury::check_status(RuntimeOrigin::signed(1), 4).unwrap(); - assert_eq!(info.pays_fee, Pays::No); - System::assert_last_event(Event::::SpendProcessed { index: 4 }.into()); - }); -} - -#[test] -fn try_state_proposals_invariant_1_works() { - ExtBuilder::default().build().execute_with(|| { - use frame_support::pallet_prelude::DispatchError::Other; - // Add a proposal using `propose_spend` - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 1, 3) - }); - assert_eq!(Proposals::::iter().count(), 1); - assert_eq!(ProposalCount::::get(), 1); - // Check invariant 1 holds - assert!(ProposalCount::::get() as usize >= Proposals::::iter().count()); - // Break invariant 1 by decreasing `ProposalCount` - ProposalCount::::put(0); - // Invariant 1 should be violated - assert_eq!( - Treasury::do_try_state(), - Err(Other("Actual number of proposals exceeds `ProposalCount`.")) - ); - }); -} - -#[test] -fn try_state_proposals_invariant_2_works() { - ExtBuilder::default().build().execute_with(|| { - use frame_support::pallet_prelude::DispatchError::Other; - // Add a proposal using `propose_spend` - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 1, 3) - }); - assert_eq!(Proposals::::iter().count(), 1); - let current_proposal_count = ProposalCount::::get(); - assert_eq!(current_proposal_count, 1); - // Check invariant 2 holds - assert!( - Proposals::::iter_keys() - .all(|proposal_index| { - proposal_index < current_proposal_count - }) - ); - // Break invariant 2 by inserting the proposal under key = 1 - let proposal = Proposals::::take(0).unwrap(); - Proposals::::insert(1, proposal); - // Invariant 2 should be violated - assert_eq!( - Treasury::do_try_state(), - Err(Other("`ProposalCount` should by strictly greater than any ProposalIndex used as a key for `Proposals`.")) - ); - }); -} - -#[test] -fn try_state_proposals_invariant_3_works() { - ExtBuilder::default().build().execute_with(|| { - use frame_support::pallet_prelude::DispatchError::Other; - // Add a proposal using `propose_spend` - assert_ok!({ - #[allow(deprecated)] - Treasury::propose_spend(RuntimeOrigin::signed(0), 10, 3) - }); - assert_eq!(Proposals::::iter().count(), 1); - // Approve the proposal - assert_ok!({ - #[allow(deprecated)] - Treasury::approve_proposal(RuntimeOrigin::root(), 0) - }); - assert_eq!(Approvals::::get().len(), 1); - // Check invariant 3 holds - assert!(Approvals::::get() - .iter() - .all(|proposal_index| { Proposals::::contains_key(proposal_index) })); - // Break invariant 3 by adding another key to `Approvals` - let mut approvals_modified = Approvals::::get(); - approvals_modified.try_push(2).unwrap(); - Approvals::::put(approvals_modified); - // Invariant 3 should be violated - assert_eq!( - Treasury::do_try_state(), - Err(Other( - "Proposal indices in `Approvals` must also be contained in `Proposals`." - )) - ); - }); -} - -#[test] -fn try_state_spends_invariant_1_works() { - ExtBuilder::default().build().execute_with(|| { - use frame_support::pallet_prelude::DispatchError::Other; - // Propose and approve a spend - assert_ok!({ - Treasury::spend(RuntimeOrigin::signed(10), Box::new(1), 1, Box::new(6), None) - }); - assert_eq!(Spends::::iter().count(), 1); - assert_eq!(SpendCount::::get(), 1); - // Check invariant 1 holds - assert!(SpendCount::::get() as usize >= Spends::::iter().count()); - // Break invariant 1 by decreasing `SpendCount` - SpendCount::::put(0); - // Invariant 1 should be violated - assert_eq!( - Treasury::do_try_state(), - Err(Other("Actual number of spends exceeds `SpendCount`.")) - ); - }); -} - -#[test] -fn try_state_spends_invariant_2_works() { - ExtBuilder::default().build().execute_with(|| { - use frame_support::pallet_prelude::DispatchError::Other; - // Propose and approve a spend - assert_ok!({ - Treasury::spend(RuntimeOrigin::signed(10), Box::new(1), 1, Box::new(6), None) - }); - assert_eq!(Spends::::iter().count(), 1); - let current_spend_count = SpendCount::::get(); - assert_eq!(current_spend_count, 1); - // Check invariant 2 holds - assert!( - Spends::::iter_keys() - .all(|spend_index| { - spend_index < current_spend_count - }) - ); - // Break invariant 2 by inserting the spend under key = 1 - let spend = Spends::::take(0).unwrap(); - Spends::::insert(1, spend); - // Invariant 2 should be violated - assert_eq!( - Treasury::do_try_state(), - Err(Other("`SpendCount` should by strictly greater than any SpendIndex used as a key for `Spends`.")) - ); - }); -} - -#[test] -fn try_state_spends_invariant_3_works() { - ExtBuilder::default().build().execute_with(|| { - use frame_support::pallet_prelude::DispatchError::Other; - // Propose and approve a spend - assert_ok!({ - Treasury::spend(RuntimeOrigin::signed(10), Box::new(1), 1, Box::new(6), None) - }); - assert_eq!(Spends::::iter().count(), 1); - let current_spend_count = SpendCount::::get(); - assert_eq!(current_spend_count, 1); - // Check invariant 3 holds - assert!(Spends::::iter_values().all( - |SpendStatus { - valid_from, - expire_at, - .. - }| { valid_from < expire_at } - )); - // Break invariant 3 by reversing spend.expire_at and spend.valid_from - let spend = Spends::::take(0).unwrap(); - Spends::::insert( - 0, - SpendStatus { - valid_from: spend.expire_at, - expire_at: spend.valid_from, - ..spend - }, - ); - // Invariant 3 should be violated - assert_eq!( - Treasury::do_try_state(), - Err(Other("Spend cannot expire before it becomes valid.")) - ); - }); -} diff --git a/pallets/treasury/src/weights.rs b/pallets/treasury/src/weights.rs deleted file mode 100644 index 82277e2d28..0000000000 --- a/pallets/treasury/src/weights.rs +++ /dev/null @@ -1,367 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Autogenerated weights for `pallet_treasury` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-04-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` - -// Executed Command: -// ./target/production/substrate-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_treasury -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* -// --wasm-execution=compiled -// --heap-pages=4096 -// --output=./substrate/frame/treasury/src/weights.rs -// --header=./substrate/HEADER-APACHE2 -// --template=./substrate/.maintain/frame-weight-template.hbs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; - -/// Weight functions needed for `pallet_treasury`. -pub trait WeightInfo { - fn spend_local() -> Weight; - fn propose_spend() -> Weight; - fn reject_proposal() -> Weight; - fn approve_proposal(p: u32, ) -> Weight; - fn remove_approval() -> Weight; - fn on_initialize_proposals(p: u32, ) -> Weight; - fn spend() -> Weight; - fn payout() -> Weight; - fn check_status() -> Weight; - fn void_spend() -> Weight; -} - -/// Weights for `pallet_treasury` using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - /// Storage: `Treasury::ProposalCount` (r:1 w:1) - /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Approvals` (r:1 w:1) - /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Proposals` (r:0 w:1) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - fn spend_local() -> Weight { - // Proof Size summary in bytes: - // Measured: `76` - // Estimated: `1887` - // Minimum execution time: 11_910_000 picoseconds. - Weight::from_parts(12_681_000, 1887) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `Treasury::ProposalCount` (r:1 w:1) - /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Proposals` (r:0 w:1) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - fn propose_spend() -> Weight { - // Proof Size summary in bytes: - // Measured: `177` - // Estimated: `1489` - // Minimum execution time: 24_704_000 picoseconds. - Weight::from_parts(25_484_000, 1489) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Treasury::Proposals` (r:1 w:1) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn reject_proposal() -> Weight { - // Proof Size summary in bytes: - // Measured: `335` - // Estimated: `3593` - // Minimum execution time: 26_632_000 picoseconds. - Weight::from_parts(27_325_000, 3593) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Treasury::Proposals` (r:1 w:0) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Approvals` (r:1 w:1) - /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - /// The range of component `p` is `[0, 99]`. - fn approve_proposal(p: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `504 + p * (8 ±0)` - // Estimated: `3573` - // Minimum execution time: 8_436_000 picoseconds. - Weight::from_parts(11_268_438, 3573) - // Standard Error: 1_039 - .saturating_add(Weight::from_parts(70_903, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Treasury::Approvals` (r:1 w:1) - /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - fn remove_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `161` - // Estimated: `1887` - // Minimum execution time: 6_372_000 picoseconds. - Weight::from_parts(6_567_000, 1887) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Treasury::Deactivated` (r:1 w:1) - /// Proof: `Treasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Approvals` (r:1 w:1) - /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Proposals` (r:99 w:99) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:198 w:198) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Bounties::BountyApprovals` (r:1 w:1) - /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - /// The range of component `p` is `[0, 99]`. - fn on_initialize_proposals(p: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `451 + p * (251 ±0)` - // Estimated: `1887 + p * (5206 ±0)` - // Minimum execution time: 33_150_000 picoseconds. - Weight::from_parts(41_451_020, 1887) - // Standard Error: 19_018 - .saturating_add(Weight::from_parts(34_410_759, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into()))) - .saturating_add(T::DbWeight::get().writes(3_u64)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into())) - } - /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:0) - /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) - /// Storage: `Treasury::SpendCount` (r:1 w:1) - /// Proof: `Treasury::SpendCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Spends` (r:0 w:1) - /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - fn spend() -> Weight { - // Proof Size summary in bytes: - // Measured: `140` - // Estimated: `3501` - // Minimum execution time: 14_233_000 picoseconds. - Weight::from_parts(14_842_000, 3501) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Treasury::Spends` (r:1 w:1) - /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn payout() -> Weight { - // Proof Size summary in bytes: - // Measured: `709` - // Estimated: `6208` - // Minimum execution time: 58_857_000 picoseconds. - Weight::from_parts(61_291_000, 6208) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: `Treasury::Spends` (r:1 w:1) - /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - fn check_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `198` - // Estimated: `3538` - // Minimum execution time: 12_116_000 picoseconds. - Weight::from_parts(12_480_000, 3538) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Treasury::Spends` (r:1 w:1) - /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - fn void_spend() -> Weight { - // Proof Size summary in bytes: - // Measured: `198` - // Estimated: `3538` - // Minimum execution time: 10_834_000 picoseconds. - Weight::from_parts(11_427_000, 3538) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests. -impl WeightInfo for () { - /// Storage: `Treasury::ProposalCount` (r:1 w:1) - /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Approvals` (r:1 w:1) - /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Proposals` (r:0 w:1) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - fn spend_local() -> Weight { - // Proof Size summary in bytes: - // Measured: `76` - // Estimated: `1887` - // Minimum execution time: 11_910_000 picoseconds. - Weight::from_parts(12_681_000, 1887) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `Treasury::ProposalCount` (r:1 w:1) - /// Proof: `Treasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Proposals` (r:0 w:1) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - fn propose_spend() -> Weight { - // Proof Size summary in bytes: - // Measured: `177` - // Estimated: `1489` - // Minimum execution time: 24_704_000 picoseconds. - Weight::from_parts(25_484_000, 1489) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Treasury::Proposals` (r:1 w:1) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn reject_proposal() -> Weight { - // Proof Size summary in bytes: - // Measured: `335` - // Estimated: `3593` - // Minimum execution time: 26_632_000 picoseconds. - Weight::from_parts(27_325_000, 3593) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Treasury::Proposals` (r:1 w:0) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Approvals` (r:1 w:1) - /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - /// The range of component `p` is `[0, 99]`. - fn approve_proposal(p: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `504 + p * (8 ±0)` - // Estimated: `3573` - // Minimum execution time: 8_436_000 picoseconds. - Weight::from_parts(11_268_438, 3573) - // Standard Error: 1_039 - .saturating_add(Weight::from_parts(70_903, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Treasury::Approvals` (r:1 w:1) - /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - fn remove_approval() -> Weight { - // Proof Size summary in bytes: - // Measured: `161` - // Estimated: `1887` - // Minimum execution time: 6_372_000 picoseconds. - Weight::from_parts(6_567_000, 1887) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Treasury::Deactivated` (r:1 w:1) - /// Proof: `Treasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Approvals` (r:1 w:1) - /// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Proposals` (r:99 w:99) - /// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:198 w:198) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - /// Storage: `Bounties::BountyApprovals` (r:1 w:1) - /// Proof: `Bounties::BountyApprovals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`) - /// The range of component `p` is `[0, 99]`. - fn on_initialize_proposals(p: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `451 + p * (251 ±0)` - // Estimated: `1887 + p * (5206 ±0)` - // Minimum execution time: 33_150_000 picoseconds. - Weight::from_parts(41_451_020, 1887) - // Standard Error: 19_018 - .saturating_add(Weight::from_parts(34_410_759, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(p.into()))) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(p.into()))) - .saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into())) - } - /// Storage: `AssetRate::ConversionRateToNative` (r:1 w:0) - /// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) - /// Storage: `Treasury::SpendCount` (r:1 w:1) - /// Proof: `Treasury::SpendCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `Treasury::Spends` (r:0 w:1) - /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - fn spend() -> Weight { - // Proof Size summary in bytes: - // Measured: `140` - // Estimated: `3501` - // Minimum execution time: 14_233_000 picoseconds. - Weight::from_parts(14_842_000, 3501) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Treasury::Spends` (r:1 w:1) - /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - /// Storage: `Assets::Asset` (r:1 w:1) - /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) - /// Storage: `Assets::Account` (r:2 w:2) - /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn payout() -> Weight { - // Proof Size summary in bytes: - // Measured: `709` - // Estimated: `6208` - // Minimum execution time: 58_857_000 picoseconds. - Weight::from_parts(61_291_000, 6208) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - } - /// Storage: `Treasury::Spends` (r:1 w:1) - /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - fn check_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `198` - // Estimated: `3538` - // Minimum execution time: 12_116_000 picoseconds. - Weight::from_parts(12_480_000, 3538) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Treasury::Spends` (r:1 w:1) - /// Proof: `Treasury::Spends` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) - fn void_spend() -> Weight { - // Proof Size summary in bytes: - // Measured: `198` - // Estimated: `3538` - // Minimum execution time: 10_834_000 picoseconds. - Weight::from_parts(11_427_000, 3538) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/vendor/README.md b/vendor/README.md index cf93add6c5..e0a93ed465 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -13,19 +13,19 @@ introduce any unintentional changes without being aware of them. ## Package list -| Directory | Package name | Origin | -|------------------------------------|--------------------------------|-----------------------------------------------| -| evm-tracing | moonbeam-client-evm-tracing | ${moonbeam}/client/evm-tracing | -| rpc/debug | moonbeam-rpc-debug | ${moonbeam}/client/rpc/debug | -| rpc/trace | moonbeam-rpc-trace | ${moonbeam}/client/rpc/trace | -| rpc/txpool | moonbeam-rpc-txpool | ${moonbeam}/client/rpc/txpool | -| rpc-core/types | moonbeam-rpc-core-types | ${moonbeam}/client/rpc-core/types | -| rpc-core/debug | moonbeam-rpc-core-debug | ${moonbeam}/client/rpc-core/debug | -| rpc-core/trace | moonbeam-rpc-core-trace | ${moonbeam}/client/rpc-core/trace | -| rpc-core/txpool | moonbeam-rpc-core-txpool | ${moonbeam}/client/rpc-core/txpool | -| runtime/evm_tracer | moonbeam-evm-tracer | ${moonbeam}/runtime/evm_tracer | -| runtime/ext | moonbeam-primitives-ext | ${moonbeam}/primitives/ext | -| primitives/evm-tracing-events | evm-tracing-events | ${moonbeam}/primitives/rpc/evm-tracing-events | -| primitives/debug | moonbeam-rpc-primitives-debug | ${moonbeam}/primitives/rpc/debug | -| primitives/txpool | moonbeam-rpc-primitives-txpool | ${moonbeam}/primitives/rpc/txpool | - +| Directory | Package name | Origin | +|-------------------------------|--------------------------------|-----------------------------------------------| +| evm-tracing | moonbeam-client-evm-tracing | ${moonbeam}/client/evm-tracing | +| rpc/debug | moonbeam-rpc-debug | ${moonbeam}/client/rpc/debug | +| rpc/trace | moonbeam-rpc-trace | ${moonbeam}/client/rpc/trace | +| rpc/txpool | moonbeam-rpc-txpool | ${moonbeam}/client/rpc/txpool | +| rpc-core/types | moonbeam-rpc-core-types | ${moonbeam}/client/rpc-core/types | +| rpc-core/debug | moonbeam-rpc-core-debug | ${moonbeam}/client/rpc-core/debug | +| rpc-core/trace | moonbeam-rpc-core-trace | ${moonbeam}/client/rpc-core/trace | +| rpc-core/txpool | moonbeam-rpc-core-txpool | ${moonbeam}/client/rpc-core/txpool | +| runtime/evm_tracer | moonbeam-evm-tracer | ${moonbeam}/runtime/evm_tracer | +| runtime/ext | moonbeam-primitives-ext | ${moonbeam}/primitives/ext | +| primitives/evm-tracing-events | evm-tracing-events | ${moonbeam}/primitives/rpc/evm-tracing-events | +| primitives/debug | moonbeam-rpc-primitives-debug | ${moonbeam}/primitives/rpc/debug | +| primitives/txpool | moonbeam-rpc-primitives-txpool | ${moonbeam}/primitives/rpc/txpool | +| treasury | pallet-treasury | ${polkadot-sdk}/substrate/frame/treasury |