Treasury spends various asset kinds (#1333)

### Summary 

This PR introduces new dispatchables to the treasury pallet, allowing
spends of various asset types. The enhanced features of the treasury
pallet, in conjunction with the asset-rate pallet, are set up and
enabled for Westend and Rococo.

### Westend and Rococo runtimes.

Polkadot/Kusams/Rococo Treasury can accept proposals for `spends` of
various asset kinds by specifying the asset's location and ID.

#### Treasury Instance New Dispatchables:
- `spend(AssetKind, AssetBalance, Beneficiary, Option<ValidFrom>)` -
propose and approve a spend;
- `payout(SpendIndex)` - payout an approved spend or retry a failed
payout
- `check_payment(SpendIndex)` - check the status of a payout;
- `void_spend(SpendIndex)` - void previously approved spend;
> existing spend dispatchable renamed to spend_local

in this context, the `AssetKind` parameter contains the asset's location
and it's corresponding `asset_id`, for example:
`USDT` on `AssetHub`,
``` rust
location = MultiLocation(0, X1(Parachain(1000)))
asset_id = MultiLocation(0, X2(PalletInstance(50), GeneralIndex(1984)))
```

the `Beneficiary` parameter is a `MultiLocation` in the context of the
asset's location, for example
``` rust
// the Fellowship salary pallet's location / account
FellowshipSalaryPallet = MultiLocation(1, X2(Parachain(1001), PalletInstance(64)))
// or custom `AccountId`
Alice = MultiLocation(0, AccountId32(network: None, id: [1,...]))
```

the `AssetBalance` represents the amount of the `AssetKind` to be
transferred to the `Beneficiary`. For permission checks, the asset
amount is converted to the native amount and compared against the
maximum spendable amount determined by the commanding spend origin.

the `spend` dispatchable allows for batching spends with different
`ValidFrom` arguments, enabling milestone-based spending. If the
expectations tied to an approved spend are not met, it is possible to
void the spend later using the `void_spend` dispatchable.

Asset Rate Pallet provides the conversion rate from the `AssetKind` to
the native balance.

#### Asset Rate Instance Dispatchables:
- `create(AssetKind, Rate)` - initialize a conversion rate to the native
balance for the given asset
- `update(AssetKind, Rate)` - update the conversion rate to the native
balance for the given asset
- `remove(AssetKind)` - remove an existing conversion rate to the native
balance for the given asset

the pallet's dispatchables can be executed by the Root or Treasurer
origins.

### Treasury Pallet

Treasury Pallet can accept proposals for `spends` of various asset kinds
and pay them out through the implementation of the `Pay` trait.

New Dispatchables:
- `spend(Config::AssetKind, AssetBalance, Config::Beneficiary,
Option<ValidFrom>)` - propose and approve a spend;
- `payout(SpendIndex)` - payout an approved spend or retry a failed
payout;
- `check_payment(SpendIndex)` - check the status of a payout;
- `void_spend(SpendIndex)` - void previously approved spend;
> existing spend dispatchable renamed to spend_local

The parameters' types of the `spend` dispatchable exposed via the
pallet's `Config` and allows to propose and accept a spend of a certain
amount.

An approved spend can be claimed via the `payout` within the
`Config::SpendPeriod`. Clients provide an implementation of the `Pay`
trait which can pay an asset of the `AssetKind` to the `Beneficiary` in
`AssetBalance` units.

The implementation of the Pay trait might not have an immediate final
payment status, for example if implemented over `XCM` and the actual
transfer happens on a remote chain.

The `check_status` dispatchable can be executed to update the spend's
payment state and retry the `payout` if the payment has failed.

---------

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
Co-authored-by: command-bot <>
This commit is contained in:
Muharem
2023-10-07 19:32:35 +02:00
committed by GitHub
parent 5a6912606a
commit cb944dc548
32 changed files with 2213 additions and 286 deletions
Generated
+12
View File
@@ -840,20 +840,27 @@ version = "1.0.0"
dependencies = [
"assert_matches",
"asset-hub-westend-runtime",
"cumulus-pallet-dmp-queue",
"cumulus-pallet-parachain-system",
"frame-support",
"frame-system",
"integration-tests-common",
"pallet-asset-conversion",
"pallet-asset-rate",
"pallet-assets",
"pallet-balances",
"pallet-treasury",
"pallet-xcm",
"parachains-common",
"parity-scale-codec",
"polkadot-core-primitives",
"polkadot-parachain-primitives",
"polkadot-runtime-common",
"polkadot-runtime-parachains",
"sp-runtime",
"staging-xcm",
"staging-xcm-builder",
"staging-xcm-executor",
"xcm-emulator",
]
@@ -10567,6 +10574,7 @@ dependencies = [
name = "pallet-treasury"
version = "4.0.0-dev"
dependencies = [
"docify",
"frame-benchmarking",
"frame-support",
"frame-system",
@@ -12485,6 +12493,7 @@ dependencies = [
"impl-trait-for-tuples",
"libsecp256k1",
"log",
"pallet-asset-rate",
"pallet-authorship",
"pallet-babe",
"pallet-balances",
@@ -12519,6 +12528,7 @@ dependencies = [
"sp-staking",
"sp-std",
"staging-xcm",
"staging-xcm-builder",
"static_assertions",
]
@@ -13900,6 +13910,7 @@ dependencies = [
"frame-try-runtime",
"hex-literal",
"log",
"pallet-asset-rate",
"pallet-authority-discovery",
"pallet-authorship",
"pallet-babe",
@@ -19941,6 +19952,7 @@ dependencies = [
"frame-try-runtime",
"hex-literal",
"log",
"pallet-asset-rate",
"pallet-authority-discovery",
"pallet-authorship",
"pallet-babe",
@@ -18,17 +18,24 @@ frame-system = { path = "../../../../../../substrate/frame/system", default-feat
pallet-balances = { path = "../../../../../../substrate/frame/balances", default-features = false}
pallet-assets = { path = "../../../../../../substrate/frame/assets", default-features = false}
pallet-asset-conversion = { path = "../../../../../../substrate/frame/asset-conversion", default-features = false}
pallet-treasury = { path = "../../../../../../substrate/frame/treasury", default-features = false}
pallet-asset-rate = { path = "../../../../../../substrate/frame/asset-rate", default-features = false}
# Polkadot
polkadot-core-primitives = { path = "../../../../../../polkadot/core-primitives", default-features = false}
polkadot-parachain-primitives = { path = "../../../../../../polkadot/parachain", default-features = false}
polkadot-runtime-common = { path = "../../../../../../polkadot/runtime/common" }
polkadot-runtime-parachains = { path = "../../../../../../polkadot/runtime/parachains" }
xcm = { package = "staging-xcm", path = "../../../../../../polkadot/xcm", default-features = false}
xcm-builder = { package = "staging-xcm-builder", path = "../../../../../../polkadot/xcm/xcm-builder", default-features = false}
xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../polkadot/xcm/xcm-executor", default-features = false}
pallet-xcm = { path = "../../../../../../polkadot/xcm/pallet-xcm", default-features = false}
# Cumulus
parachains-common = { path = "../../../../common" }
asset-hub-westend-runtime = { path = "../../../../runtimes/assets/asset-hub-westend" }
cumulus-pallet-dmp-queue = { default-features = false, path = "../../../../../pallets/dmp-queue" }
cumulus-pallet-parachain-system = { default-features = false, path = "../../../../../pallets/parachain-system" }
# Local
xcm-emulator = { path = "../../../../../xcm/xcm-emulator", default-features = false}
@@ -37,15 +44,21 @@ integration-tests-common = { path = "../../common", default-features = false}
[features]
runtime-benchmarks = [
"asset-hub-westend-runtime/runtime-benchmarks",
"cumulus-pallet-parachain-system/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"integration-tests-common/runtime-benchmarks",
"pallet-asset-conversion/runtime-benchmarks",
"pallet-asset-rate/runtime-benchmarks",
"pallet-assets/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-treasury/runtime-benchmarks",
"pallet-xcm/runtime-benchmarks",
"parachains-common/runtime-benchmarks",
"polkadot-parachain-primitives/runtime-benchmarks",
"polkadot-runtime-common/runtime-benchmarks",
"polkadot-runtime-parachains/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
]
@@ -18,3 +18,4 @@ mod send;
mod set_xcm_versions;
mod swap;
mod teleport;
mod treasury;
@@ -0,0 +1,126 @@
// 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.
use crate::*;
use frame_support::traits::fungibles::{Create, Inspect, Mutate};
use integration_tests_common::constants::accounts::{ALICE, BOB};
use polkadot_runtime_common::impls::VersionedLocatableAsset;
use xcm_executor::traits::ConvertLocation;
#[test]
fn create_and_claim_treasury_spend() {
const ASSET_ID: u32 = 1984;
const SPEND_AMOUNT: u128 = 1_000_000;
// treasury location from a sibling parachain.
let treasury_location: MultiLocation = MultiLocation::new(1, PalletInstance(37));
// treasury account on a sibling parachain.
let treasury_account =
asset_hub_westend_runtime::xcm_config::LocationToAccountId::convert_location(
&treasury_location,
)
.unwrap();
let asset_hub_location = MultiLocation::new(0, Parachain(AssetHubWestend::para_id().into()));
let root = <Westend as Chain>::RuntimeOrigin::root();
// asset kind to be spend from the treasury.
let asset_kind = VersionedLocatableAsset::V3 {
location: asset_hub_location,
asset_id: AssetId::Concrete((PalletInstance(50), GeneralIndex(ASSET_ID.into())).into()),
};
// treasury spend beneficiary.
let alice: AccountId = Westend::account_id_of(ALICE);
let bob: AccountId = Westend::account_id_of(BOB);
let bob_signed = <Westend as Chain>::RuntimeOrigin::signed(bob.clone());
AssetHubWestend::execute_with(|| {
type Assets = <AssetHubWestend as AssetHubWestendPallet>::Assets;
// create an asset class and mint some assets to the treasury account.
assert_ok!(<Assets as Create<_>>::create(
ASSET_ID,
treasury_account.clone(),
true,
SPEND_AMOUNT / 2
));
assert_ok!(<Assets as Mutate<_>>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4));
// beneficiary has zero balance.
assert_eq!(<Assets as Inspect<_>>::balance(ASSET_ID, &alice,), 0u128,);
});
Westend::execute_with(|| {
type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
type Treasury = <Westend as WestendPallet>::Treasury;
type AssetRate = <Westend as WestendPallet>::AssetRate;
// create a conversion rate from `asset_kind` to the native currency.
assert_ok!(AssetRate::create(root.clone(), Box::new(asset_kind.clone()), 2.into()));
// create and approve a treasury spend.
assert_ok!(Treasury::spend(
root,
Box::new(asset_kind),
SPEND_AMOUNT,
Box::new(MultiLocation::new(0, Into::<[u8; 32]>::into(alice.clone())).into()),
None,
));
// claim the spend.
assert_ok!(Treasury::payout(bob_signed.clone(), 0));
assert_expected_events!(
Westend,
vec![
RuntimeEvent::Treasury(pallet_treasury::Event::Paid { .. }) => {},
]
);
});
AssetHubWestend::execute_with(|| {
type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
type Assets = <AssetHubWestend as AssetHubWestendPallet>::Assets;
// assert events triggered by xcm pay program
// 1. treasury asset transferred to spend beneficiary
// 2. response to Relay Chain treasury pallet instance sent back
// 3. XCM program completed
assert_expected_events!(
AssetHubWestend,
vec![
RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => {
id: id == &ASSET_ID,
from: from == &treasury_account,
to: to == &alice,
amount: amount == &SPEND_AMOUNT,
},
RuntimeEvent::ParachainSystem(cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }) => {},
RuntimeEvent::DmpQueue(cumulus_pallet_dmp_queue::Event::ExecutedDownward { outcome: Outcome::Complete(..) ,.. }) => {},
]
);
// beneficiary received the assets from the treasury.
assert_eq!(<Assets as Inspect<_>>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,);
});
Westend::execute_with(|| {
type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
type Treasury = <Westend as WestendPallet>::Treasury;
// check the payment status to ensure the response from the AssetHub was received.
assert_ok!(Treasury::check_status(bob_signed, 0));
assert_expected_events!(
Westend,
vec![
RuntimeEvent::Treasury(pallet_treasury::Event::SpendProcessed { .. }) => {},
]
);
});
}
@@ -46,6 +46,8 @@ decl_test_relay_chains! {
XcmPallet: westend_runtime::XcmPallet,
Sudo: westend_runtime::Sudo,
Balances: westend_runtime::Balances,
Treasury: westend_runtime::Treasury,
AssetRate: westend_runtime::AssetRate,
}
},
#[api_version(7)]
@@ -38,11 +38,12 @@ use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses,
AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter,
DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FungiblesAdapter, IsConcrete,
LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative,
SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative,
SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId,
UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal,
EnsureXcmOrigin, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NativeAsset,
NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents,
WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
};
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
@@ -75,6 +76,9 @@ pub type LocationToAccountId = (
SiblingParachainConvertsVia<Sibling, AccountId>,
// Straight up local `AccountId32` origins just alias directly to `AccountId`.
AccountId32Aliases<RelayNetwork, AccountId>,
// Foreign chain account alias into local accounts according to a hash of their standard
// description.
HashedDescription<AccountId, DescribeFamily<DescribePalletTerminal>>,
);
/// Means for transacting the native currency on this chain.
@@ -222,6 +226,9 @@ match_types! {
MultiLocation { parents: 1, interior: Here } |
MultiLocation { parents: 1, interior: X1(Plurality { .. }) }
};
pub type TreasuryPallet: impl Contains<MultiLocation> = {
MultiLocation { parents: 1, interior: X1(PalletInstance(37)) }
};
}
/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
@@ -449,8 +456,9 @@ pub type Barrier = TrailingSetTopicAsId<
// If the message is one that immediately attemps to pay for execution, then
// allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Parent and its pluralities (i.e. governance bodies) get free execution.
AllowExplicitUnpaidExecutionFrom<ParentOrParentsPlurality>,
// Parent, its pluralities (i.e. governance bodies) and treasury pallet get
// free execution.
AllowExplicitUnpaidExecutionFrom<(ParentOrParentsPlurality, TreasuryPallet)>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<Everything>,
),
+7
View File
@@ -38,6 +38,7 @@ pallet-timestamp = { path = "../../../substrate/frame/timestamp", default-featur
pallet-vesting = { path = "../../../substrate/frame/vesting", default-features = false }
pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false }
pallet-treasury = { path = "../../../substrate/frame/treasury", default-features = false }
pallet-asset-rate = { path = "../../../substrate/frame/asset-rate", default-features = false }
pallet-election-provider-multi-phase = { path = "../../../substrate/frame/election-provider-multi-phase", default-features = false }
frame-election-provider-support = { path = "../../../substrate/frame/election-provider-support", default-features = false }
@@ -50,6 +51,7 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac
slot-range-helper = { path = "slot_range_helper", default-features = false }
xcm = { package = "staging-xcm", path = "../../xcm", default-features = false }
xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false }
[dev-dependencies]
hex-literal = "0.4.1"
@@ -74,6 +76,7 @@ std = [
"inherents/std",
"libsecp256k1/std",
"log/std",
"pallet-asset-rate/std",
"pallet-authorship/std",
"pallet-balances/std",
"pallet-election-provider-multi-phase/std",
@@ -100,6 +103,7 @@ std = [
"sp-session/std",
"sp-staking/std",
"sp-std/std",
"xcm-builder/std",
"xcm/std",
]
runtime-benchmarks = [
@@ -109,6 +113,7 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"libsecp256k1/hmac",
"libsecp256k1/static-context",
"pallet-asset-rate/runtime-benchmarks",
"pallet-babe/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-election-provider-multi-phase/runtime-benchmarks",
@@ -121,12 +126,14 @@ runtime-benchmarks = [
"runtime-parachains/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"sp-staking/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
]
try-runtime = [
"frame-election-provider-support/try-runtime",
"frame-support-test/try-runtime",
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-asset-rate/try-runtime",
"pallet-authorship/try-runtime",
"pallet-babe?/try-runtime",
"pallet-balances/try-runtime",
+104 -2
View File
@@ -18,8 +18,10 @@
use crate::NegativeImbalance;
use frame_support::traits::{Currency, Imbalance, OnUnbalanced};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use primitives::Balance;
use sp_runtime::Perquintill;
use sp_runtime::{traits::TryConvert, Perquintill, RuntimeDebug};
use xcm::VersionedMultiLocation;
/// Logic for the author to get a portion of fees.
pub struct ToAuthor<R>(sp_std::marker::PhantomData<R>);
@@ -98,13 +100,104 @@ pub fn era_payout(
(staking_payout, rest)
}
/// Versioned locatable asset type which contains both an XCM `location` and `asset_id` to identify
/// an asset which exists on some chain.
#[derive(
Encode, Decode, Eq, PartialEq, Clone, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen,
)]
pub enum VersionedLocatableAsset {
#[codec(index = 3)]
V3 {
/// The (relative) location in which the asset ID is meaningful.
location: xcm::v3::MultiLocation,
/// The asset's ID.
asset_id: xcm::v3::AssetId,
},
}
/// Converts the [`VersionedLocatableAsset`] to the [`xcm_builder::LocatableAssetId`].
pub struct LocatableAssetConverter;
impl TryConvert<VersionedLocatableAsset, xcm_builder::LocatableAssetId>
for LocatableAssetConverter
{
fn try_convert(
asset: VersionedLocatableAsset,
) -> Result<xcm_builder::LocatableAssetId, VersionedLocatableAsset> {
match asset {
VersionedLocatableAsset::V3 { location, asset_id } =>
Ok(xcm_builder::LocatableAssetId { asset_id, location }),
}
}
}
/// Converts the [`VersionedMultiLocation`] to the [`xcm::latest::MultiLocation`].
pub struct VersionedMultiLocationConverter;
impl TryConvert<&VersionedMultiLocation, xcm::latest::MultiLocation>
for VersionedMultiLocationConverter
{
fn try_convert(
location: &VersionedMultiLocation,
) -> Result<xcm::latest::MultiLocation, &VersionedMultiLocation> {
let latest = match location.clone() {
VersionedMultiLocation::V2(l) => l.try_into().map_err(|_| location)?,
VersionedMultiLocation::V3(l) => l,
};
Ok(latest)
}
}
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarks {
use super::VersionedLocatableAsset;
use pallet_asset_rate::AssetKindFactory;
use pallet_treasury::ArgumentsFactory as TreasuryArgumentsFactory;
use xcm::prelude::*;
/// Provides a factory method for the [`VersionedLocatableAsset`].
/// The location of the asset is determined as a Parachain with an ID equal to the passed seed.
pub struct AssetRateArguments;
impl AssetKindFactory<VersionedLocatableAsset> for AssetRateArguments {
fn create_asset_kind(seed: u32) -> VersionedLocatableAsset {
VersionedLocatableAsset::V3 {
location: xcm::v3::MultiLocation::new(0, X1(Parachain(seed))),
asset_id: xcm::v3::MultiLocation::new(
0,
X2(PalletInstance(seed.try_into().unwrap()), GeneralIndex(seed.into())),
)
.into(),
}
}
}
/// Provide factory methods for the [`VersionedLocatableAsset`] and the `Beneficiary` of the
/// [`VersionedMultiLocation`]. The location of the asset is determined as a Parachain with an
/// ID equal to the passed seed.
pub struct TreasuryArguments;
impl TreasuryArgumentsFactory<VersionedLocatableAsset, VersionedMultiLocation>
for TreasuryArguments
{
fn create_asset_kind(seed: u32) -> VersionedLocatableAsset {
AssetRateArguments::create_asset_kind(seed)
}
fn create_beneficiary(seed: [u8; 32]) -> VersionedMultiLocation {
VersionedMultiLocation::V3(xcm::v3::MultiLocation::new(
0,
X1(AccountId32 { network: None, id: seed }),
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use frame_support::{
dispatch::DispatchClass,
parameter_types,
traits::{ConstU32, FindAuthor},
traits::{
tokens::{PayFromAccount, UnityAssetBalanceConversion},
ConstU32, FindAuthor,
},
weights::Weight,
PalletId,
};
@@ -189,6 +282,7 @@ mod tests {
parameter_types! {
pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
pub const MaxApprovals: u32 = 100;
pub TreasuryAccount: AccountId = Treasury::account_id();
}
impl pallet_treasury::Config for Test {
@@ -208,6 +302,14 @@ mod tests {
type MaxApprovals = MaxApprovals;
type WeightInfo = ();
type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>;
type AssetKind = ();
type Beneficiary = Self::AccountId;
type BeneficiaryLookup = IdentityLookup<Self::AccountId>;
type Paymaster = PayFromAccount<Balances, TreasuryAccount>;
type BalanceConverter = UnityAssetBalanceConversion;
type PayoutPeriod = ConstU64<0>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
pub struct OneAuthor;
+5 -1
View File
@@ -52,6 +52,7 @@ pallet-collective = { path = "../../../substrate/frame/collective", default-feat
pallet-conviction-voting = { path = "../../../substrate/frame/conviction-voting", default-features = false }
pallet-democracy = { path = "../../../substrate/frame/democracy", default-features = false }
pallet-elections-phragmen = { path = "../../../substrate/frame/elections-phragmen", default-features = false }
pallet-asset-rate = { path = "../../../substrate/frame/asset-rate", default-features = false }
frame-executive = { path = "../../../substrate/frame/executive", default-features = false }
pallet-grandpa = { path = "../../../substrate/frame/grandpa", default-features = false }
pallet-identity = { path = "../../../substrate/frame/identity", default-features = false }
@@ -72,7 +73,7 @@ pallet-scheduler = { path = "../../../substrate/frame/scheduler", default-featur
pallet-session = { path = "../../../substrate/frame/session", default-features = false }
pallet-society = { path = "../../../substrate/frame/society", default-features = false }
pallet-sudo = { path = "../../../substrate/frame/sudo", default-features = false }
frame-support = { path = "../../../substrate/frame/support", default-features = false }
frame-support = { path = "../../../substrate/frame/support", default-features = false, features = ["tuples-96"] }
pallet-staking = { path = "../../../substrate/frame/staking", default-features = false }
frame-system = { path = "../../../substrate/frame/system", default-features = false }
frame-system-rpc-runtime-api = { path = "../../../substrate/frame/system/rpc/runtime-api", default-features = false }
@@ -131,6 +132,7 @@ std = [
"inherents/std",
"log/std",
"offchain-primitives/std",
"pallet-asset-rate/std",
"pallet-authority-discovery/std",
"pallet-authorship/std",
"pallet-babe/std",
@@ -207,6 +209,7 @@ runtime-benchmarks = [
"frame-support/runtime-benchmarks",
"frame-system-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-asset-rate/runtime-benchmarks",
"pallet-babe/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-bounties/runtime-benchmarks",
@@ -258,6 +261,7 @@ try-runtime = [
"frame-system/try-runtime",
"frame-try-runtime",
"frame-try-runtime/try-runtime",
"pallet-asset-rate/try-runtime",
"pallet-authority-discovery/try-runtime",
"pallet-authorship/try-runtime",
"pallet-babe/try-runtime",
+49 -3
View File
@@ -30,7 +30,10 @@ use primitives::{
ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID,
};
use runtime_common::{
assigned_slots, auctions, claims, crowdloan, impl_runtime_weights, impls::ToAuthor,
assigned_slots, auctions, claims, crowdloan, impl_runtime_weights,
impls::{
LocatableAssetConverter, ToAuthor, VersionedLocatableAsset, VersionedMultiLocationConverter,
},
paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, BlockHashCount, BlockLength,
SlowAdjustingFeeUpdate,
};
@@ -79,7 +82,8 @@ use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{
AccountIdLookup, BlakeTwo256, Block as BlockT, ConstU32, ConvertInto,
Extrinsic as ExtrinsicT, Keccak256, OpaqueKeys, SaturatedConversion, Verify,
Extrinsic as ExtrinsicT, IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion,
Verify,
},
transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity},
ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, RuntimeDebug,
@@ -88,7 +92,11 @@ use sp_staking::SessionIndex;
#[cfg(any(feature = "std", test))]
use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
use xcm::latest::Junction;
use xcm::{
latest::{InteriorMultiLocation, Junction, Junction::PalletInstance},
VersionedMultiLocation,
};
use xcm_builder::PayOverXcm;
pub use frame_system::Call as SystemCall;
pub use pallet_balances::Call as BalancesCall;
@@ -385,6 +393,10 @@ parameter_types! {
pub const SpendPeriod: BlockNumber = 6 * DAYS;
pub const Burn: Permill = Permill::from_perthousand(2);
pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
pub const PayoutSpendPeriod: BlockNumber = 30 * DAYS;
// The asset's interior location for the paying account. This is the Treasury
// pallet instance (which sits at index 18).
pub TreasuryInteriorLocation: InteriorMultiLocation = PalletInstance(18).into();
pub const TipCountdown: BlockNumber = 1 * DAYS;
pub const TipFindersFee: Percent = Percent::from_percent(20);
@@ -394,6 +406,7 @@ parameter_types! {
pub const MaxAuthorities: u32 = 100_000;
pub const MaxKeys: u32 = 10_000;
pub const MaxPeerInHeartbeats: u32 = 10_000;
pub const MaxBalance: Balance = Balance::max_value();
}
impl pallet_treasury::Config for Runtime {
@@ -413,6 +426,23 @@ impl pallet_treasury::Config for Runtime {
type WeightInfo = weights::pallet_treasury::WeightInfo<Runtime>;
type SpendFunds = Bounties;
type SpendOrigin = TreasurySpender;
type AssetKind = VersionedLocatableAsset;
type Beneficiary = VersionedMultiLocation;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Paymaster = PayOverXcm<
TreasuryInteriorLocation,
crate::xcm_config::XcmRouter,
crate::XcmPallet,
ConstU32<{ 6 * HOURS }>,
Self::Beneficiary,
Self::AssetKind,
LocatableAssetConverter,
VersionedMultiLocationConverter,
>;
type BalanceConverter = AssetRate;
type PayoutPeriod = PayoutSpendPeriod;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = runtime_common::impls::benchmarks::TreasuryArguments;
}
parameter_types! {
@@ -1202,6 +1232,18 @@ impl pallet_sudo::Config for Runtime {
type WeightInfo = weights::pallet_sudo::WeightInfo<Runtime>;
}
impl pallet_asset_rate::Config for Runtime {
type WeightInfo = weights::pallet_asset_rate::WeightInfo<Runtime>;
type RuntimeEvent = RuntimeEvent;
type CreateOrigin = EnsureRoot<AccountId>;
type RemoveOrigin = EnsureRoot<AccountId>;
type UpdateOrigin = EnsureRoot<AccountId>;
type Currency = Balances;
type AssetKind = <Runtime as pallet_treasury::Config>::AssetKind;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = runtime_common::impls::benchmarks::AssetRateArguments;
}
construct_runtime! {
pub enum Runtime
{
@@ -1279,6 +1321,9 @@ construct_runtime! {
// Preimage registrar.
Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>, HoldReason} = 32,
// Asset rate.
AssetRate: pallet_asset_rate::{Pallet, Call, Storage, Event<T>} = 39,
// Bounties modules.
Bounties: pallet_bounties::{Pallet, Call, Storage, Event<T>} = 35,
ChildBounties: pallet_child_bounties = 40,
@@ -1465,6 +1510,7 @@ mod benches {
[pallet_treasury, Treasury]
[pallet_utility, Utility]
[pallet_vesting, Vesting]
[pallet_asset_rate, AssetRate]
[pallet_whitelist, Whitelist]
// XCM
[pallet_xcm, XcmPallet]
@@ -16,6 +16,7 @@
//! A list of the different weight modules for our runtime.
pub mod frame_system;
pub mod pallet_asset_rate;
pub mod pallet_balances;
pub mod pallet_balances_nis_counterpart_balances;
pub mod pallet_bounties;
@@ -0,0 +1,86 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Autogenerated weights for `pallet_asset_rate`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-07-03, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024
// Executed Command:
// ./target/debug/polkadot
// benchmark
// pallet
// --chain=rococo-dev
// --steps=50
// --repeat=2
// --pallet=pallet_asset_rate
// --extrinsic=*
// --wasm-execution=compiled
// --heap-pages=4096
// --output=./runtime/rococo/src/weights/
// --header=./file_header.txt
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{traits::Get, weights::Weight};
use core::marker::PhantomData;
/// Weight functions for `pallet_asset_rate`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_asset_rate::WeightInfo for WeightInfo<T> {
/// Storage: AssetRate ConversionRateToNative (r:1 w:1)
/// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen)
fn create() -> Weight {
// Proof Size summary in bytes:
// Measured: `42`
// Estimated: `4702`
// Minimum execution time: 143_000_000 picoseconds.
Weight::from_parts(155_000_000, 0)
.saturating_add(Weight::from_parts(0, 4702))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
/// Storage: AssetRate ConversionRateToNative (r:1 w:1)
/// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen)
fn update() -> Weight {
// Proof Size summary in bytes:
// Measured: `110`
// Estimated: `4702`
// Minimum execution time: 156_000_000 picoseconds.
Weight::from_parts(172_000_000, 0)
.saturating_add(Weight::from_parts(0, 4702))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
/// Storage: AssetRate ConversionRateToNative (r:1 w:1)
/// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen)
fn remove() -> Weight {
// Proof Size summary in bytes:
// Measured: `110`
// Estimated: `4702`
// Minimum execution time: 150_000_000 picoseconds.
Weight::from_parts(160_000_000, 0)
.saturating_add(Weight::from_parts(0, 4702))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}
@@ -17,24 +17,24 @@
//! Autogenerated weights for `pallet_treasury`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-05-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2023-07-07, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024
//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024
// Executed Command:
// ./target/production/polkadot
// ./target/debug/polkadot
// benchmark
// pallet
// --chain=rococo-dev
// --steps=50
// --repeat=20
// --repeat=2
// --pallet=pallet_treasury
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --header=./file_header.txt
// --heap-pages=4096
// --output=./runtime/rococo/src/weights/
// --header=./file_header.txt
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -47,13 +47,21 @@ use core::marker::PhantomData;
/// Weight functions for `pallet_treasury`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_treasury::WeightInfo for WeightInfo<T> {
fn spend() -> Weight {
/// 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: `0`
// Estimated: `0`
// Minimum execution time: 204_000 picoseconds.
Weight::from_parts(233_000, 0)
.saturating_add(Weight::from_parts(0, 0))
// Measured: `42`
// Estimated: `1887`
// Minimum execution time: 177_000_000 picoseconds.
Weight::from_parts(191_000_000, 0)
.saturating_add(Weight::from_parts(0, 1887))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(3))
}
/// Storage: Treasury ProposalCount (r:1 w:1)
/// Proof: Treasury ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
@@ -61,10 +69,10 @@ impl<T: frame_system::Config> pallet_treasury::WeightInfo for WeightInfo<T> {
/// Proof: Treasury Proposals (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen)
fn propose_spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `107`
// Measured: `143`
// Estimated: `1489`
// Minimum execution time: 27_592_000 picoseconds.
Weight::from_parts(27_960_000, 0)
// Minimum execution time: 354_000_000 picoseconds.
Weight::from_parts(376_000_000, 0)
.saturating_add(Weight::from_parts(0, 1489))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(2))
@@ -75,10 +83,10 @@ impl<T: frame_system::Config> pallet_treasury::WeightInfo for WeightInfo<T> {
/// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen)
fn reject_proposal() -> Weight {
// Proof Size summary in bytes:
// Measured: `265`
// Measured: `301`
// Estimated: `3593`
// Minimum execution time: 40_336_000 picoseconds.
Weight::from_parts(41_085_000, 0)
// Minimum execution time: 547_000_000 picoseconds.
Weight::from_parts(550_000_000, 0)
.saturating_add(Weight::from_parts(0, 3593))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
@@ -90,13 +98,13 @@ impl<T: frame_system::Config> pallet_treasury::WeightInfo for WeightInfo<T> {
/// The range of component `p` is `[0, 99]`.
fn approve_proposal(p: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `433 + p * (8 ±0)`
// Measured: `470 + p * (8 ±0)`
// Estimated: `3573`
// Minimum execution time: 9_938_000 picoseconds.
Weight::from_parts(12_061_206, 0)
// Minimum execution time: 104_000_000 picoseconds.
Weight::from_parts(121_184_402, 0)
.saturating_add(Weight::from_parts(0, 3573))
// Standard Error: 801
.saturating_add(Weight::from_parts(26_602, 0).saturating_mul(p.into()))
// Standard Error: 42_854
.saturating_add(Weight::from_parts(153_112, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
@@ -104,10 +112,10 @@ impl<T: frame_system::Config> pallet_treasury::WeightInfo for WeightInfo<T> {
/// 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: `90`
// Measured: `127`
// Estimated: `1887`
// Minimum execution time: 7_421_000 picoseconds.
Weight::from_parts(7_620_000, 0)
// Minimum execution time: 80_000_000 picoseconds.
Weight::from_parts(82_000_000, 0)
.saturating_add(Weight::from_parts(0, 1887))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
@@ -118,26 +126,98 @@ impl<T: frame_system::Config> pallet_treasury::WeightInfo for WeightInfo<T> {
/// Proof: Balances InactiveIssuance (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:100 w:100)
/// 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:201 w:201)
/// Storage: System Account (r:199 w:199)
/// 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, 100]`.
/// The range of component `p` is `[0, 99]`.
fn on_initialize_proposals(p: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `296 + p * (251 ±0)`
// Measured: `331 + p * (251 ±0)`
// Estimated: `3593 + p * (5206 ±0)`
// Minimum execution time: 62_706_000 picoseconds.
Weight::from_parts(61_351_470, 0)
// Minimum execution time: 887_000_000 picoseconds.
Weight::from_parts(828_616_021, 0)
.saturating_add(Weight::from_parts(0, 3593))
// Standard Error: 32_787
.saturating_add(Weight::from_parts(37_873_920, 0).saturating_mul(p.into()))
// Standard Error: 695_351
.saturating_add(Weight::from_parts(566_114_524, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into())))
.saturating_add(T::DbWeight::get().writes(5))
.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(1237), added: 3712, 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(1848), added: 4323, mode: MaxEncodedLen)
fn spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `114`
// Estimated: `4702`
// Minimum execution time: 208_000_000 picoseconds.
Weight::from_parts(222_000_000, 0)
.saturating_add(Weight::from_parts(0, 4702))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: Treasury Spends (r:1 w:1)
/// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen)
/// Storage: XcmPallet QueryCounter (r:1 w:1)
/// Proof Skipped: XcmPallet QueryCounter (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: Configuration ActiveConfig (r:1 w:0)
/// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: Dmp DeliveryFeeFactor (r:1 w:0)
/// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured)
/// Storage: XcmPallet SupportedVersion (r:1 w:0)
/// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured)
/// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
/// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
/// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: Dmp DownwardMessageQueues (r:1 w:1)
/// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured)
/// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
/// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured)
/// Storage: XcmPallet Queries (r:0 w:1)
/// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured)
fn payout() -> Weight {
// Proof Size summary in bytes:
// Measured: `737`
// Estimated: `5313`
// Minimum execution time: 551_000_000 picoseconds.
Weight::from_parts(569_000_000, 0)
.saturating_add(Weight::from_parts(0, 5313))
.saturating_add(T::DbWeight::get().reads(9))
.saturating_add(T::DbWeight::get().writes(6))
}
/// Storage: Treasury Spends (r:1 w:1)
/// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen)
/// Storage: XcmPallet Queries (r:1 w:1)
/// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured)
fn check_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `442`
// Estimated: `5313`
// Minimum execution time: 245_000_000 picoseconds.
Weight::from_parts(281_000_000, 0)
.saturating_add(Weight::from_parts(0, 5313))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: Treasury Spends (r:1 w:1)
/// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen)
fn void_spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `172`
// Estimated: `5313`
// Minimum execution time: 147_000_000 picoseconds.
Weight::from_parts(160_000_000, 0)
.saturating_add(Weight::from_parts(0, 5313))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}
+5 -1
View File
@@ -41,10 +41,11 @@ sp-npos-elections = { path = "../../../substrate/primitives/npos-elections", def
frame-election-provider-support = { path = "../../../substrate/frame/election-provider-support", default-features = false }
frame-executive = { path = "../../../substrate/frame/executive", default-features = false }
frame-support = { path = "../../../substrate/frame/support", default-features = false }
frame-support = { path = "../../../substrate/frame/support", default-features = false, features = ["tuples-96"] }
frame-system = { path = "../../../substrate/frame/system", default-features = false }
frame-system-rpc-runtime-api = { path = "../../../substrate/frame/system/rpc/runtime-api", default-features = false }
westend-runtime-constants = { package = "westend-runtime-constants", path = "constants", default-features = false }
pallet-asset-rate = { path = "../../../substrate/frame/asset-rate", default-features = false }
pallet-authority-discovery = { path = "../../../substrate/frame/authority-discovery", default-features = false }
pallet-authorship = { path = "../../../substrate/frame/authorship", default-features = false }
pallet-babe = { path = "../../../substrate/frame/babe", default-features = false }
@@ -143,6 +144,7 @@ std = [
"inherents/std",
"log/std",
"offchain-primitives/std",
"pallet-asset-rate/std",
"pallet-authority-discovery/std",
"pallet-authorship/std",
"pallet-babe/std",
@@ -228,6 +230,7 @@ runtime-benchmarks = [
"frame-system-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"hex-literal",
"pallet-asset-rate/runtime-benchmarks",
"pallet-babe/runtime-benchmarks",
"pallet-bags-list/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
@@ -283,6 +286,7 @@ try-runtime = [
"frame-system/try-runtime",
"frame-try-runtime",
"frame-try-runtime/try-runtime",
"pallet-asset-rate/try-runtime",
"pallet-authority-discovery/try-runtime",
"pallet-authorship/try-runtime",
"pallet-babe/try-runtime",
+52 -5
View File
@@ -53,9 +53,14 @@ use primitives::{
ValidatorSignature, PARACHAIN_KEY_TYPE_ID,
};
use runtime_common::{
assigned_slots, auctions, crowdloan, elections::OnChainAccuracy, impl_runtime_weights,
impls::ToAuthor, paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, BalanceToU256,
BlockHashCount, BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, U256ToBalance,
assigned_slots, auctions, crowdloan,
elections::OnChainAccuracy,
impl_runtime_weights,
impls::{
LocatableAssetConverter, ToAuthor, VersionedLocatableAsset, VersionedMultiLocationConverter,
},
paras_registrar, paras_sudo_wrapper, prod_or_fast, slots, BalanceToU256, BlockHashCount,
BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, U256ToBalance,
};
use runtime_parachains::{
assigner_parachains as parachains_assigner_parachains,
@@ -77,7 +82,7 @@ use sp_runtime::{
generic, impl_opaque_keys,
traits::{
AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT,
Keccak256, OpaqueKeys, SaturatedConversion, Verify,
IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify,
},
transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity},
ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill,
@@ -87,7 +92,11 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*};
#[cfg(any(feature = "std", test))]
use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
use xcm::latest::Junction;
use xcm::{
latest::{InteriorMultiLocation, Junction, Junction::PalletInstance},
VersionedMultiLocation,
};
use xcm_builder::PayOverXcm;
pub use frame_system::Call as SystemCall;
pub use pallet_balances::Call as BalancesCall;
@@ -698,6 +707,10 @@ parameter_types! {
pub const SpendPeriod: BlockNumber = 6 * DAYS;
pub const Burn: Permill = Permill::from_perthousand(2);
pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
pub const PayoutSpendPeriod: BlockNumber = 30 * DAYS;
// The asset's interior location for the paying account. This is the Treasury
// pallet instance (which sits at index 37).
pub TreasuryInteriorLocation: InteriorMultiLocation = PalletInstance(37).into();
pub const TipCountdown: BlockNumber = 1 * DAYS;
pub const TipFindersFee: Percent = Percent::from_percent(20);
@@ -707,6 +720,7 @@ parameter_types! {
pub const MaxAuthorities: u32 = 100_000;
pub const MaxKeys: u32 = 10_000;
pub const MaxPeerInHeartbeats: u32 = 10_000;
pub const MaxBalance: Balance = Balance::max_value();
}
impl pallet_treasury::Config for Runtime {
@@ -726,6 +740,23 @@ impl pallet_treasury::Config for Runtime {
type WeightInfo = weights::pallet_treasury::WeightInfo<Runtime>;
type SpendFunds = ();
type SpendOrigin = TreasurySpender;
type AssetKind = VersionedLocatableAsset;
type Beneficiary = VersionedMultiLocation;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Paymaster = PayOverXcm<
TreasuryInteriorLocation,
crate::xcm_config::XcmRouter,
crate::XcmPallet,
ConstU32<{ 6 * HOURS }>,
Self::Beneficiary,
Self::AssetKind,
LocatableAssetConverter,
VersionedMultiLocationConverter,
>;
type BalanceConverter = AssetRate;
type PayoutPeriod = PayoutSpendPeriod;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = runtime_common::impls::benchmarks::TreasuryArguments;
}
impl pallet_offences::Config for Runtime {
@@ -1331,6 +1362,18 @@ parameter_types! {
pub const MigrationMaxKeyLen: u32 = 512;
}
impl pallet_asset_rate::Config for Runtime {
type WeightInfo = weights::pallet_asset_rate::WeightInfo<Runtime>;
type RuntimeEvent = RuntimeEvent;
type CreateOrigin = EnsureRoot<AccountId>;
type RemoveOrigin = EnsureRoot<AccountId>;
type UpdateOrigin = EnsureRoot<AccountId>;
type Currency = Balances;
type AssetKind = <Runtime as pallet_treasury::Config>::AssetKind;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = runtime_common::impls::benchmarks::AssetRateArguments;
}
construct_runtime! {
pub enum Runtime
{
@@ -1443,6 +1486,9 @@ construct_runtime! {
// Generalized message queue
MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event<T>} = 100,
// Asset rate.
AssetRate: pallet_asset_rate::{Pallet, Call, Storage, Event<T>} = 101,
}
}
@@ -1574,6 +1620,7 @@ mod benches {
[pallet_utility, Utility]
[pallet_vesting, Vesting]
[pallet_whitelist, Whitelist]
[pallet_asset_rate, AssetRate]
// XCM
[pallet_xcm, XcmPallet]
// NOTE: Make sure you point to the individual modules below.
@@ -17,6 +17,7 @@
pub mod frame_election_provider_support;
pub mod frame_system;
pub mod pallet_asset_rate;
pub mod pallet_bags_list;
pub mod pallet_balances;
pub mod pallet_conviction_voting;
@@ -0,0 +1,86 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Autogenerated weights for `pallet_asset_rate`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-07-04, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("polkadot-dev"), DB CACHE: 1024
// Executed Command:
// ./target/debug/polkadot
// benchmark
// pallet
// --chain=polkadot-dev
// --steps=50
// --repeat=2
// --pallet=pallet_asset_rate
// --extrinsic=*
// --wasm-execution=compiled
// --heap-pages=4096
// --output=./runtime/polkadot/src/weights/
// --header=./file_header.txt
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{traits::Get, weights::Weight};
use core::marker::PhantomData;
/// Weight functions for `pallet_asset_rate`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_asset_rate::WeightInfo for WeightInfo<T> {
/// Storage: AssetRate ConversionRateToNative (r:1 w:1)
/// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen)
fn create() -> Weight {
// Proof Size summary in bytes:
// Measured: `42`
// Estimated: `4702`
// Minimum execution time: 67_000_000 picoseconds.
Weight::from_parts(69_000_000, 0)
.saturating_add(Weight::from_parts(0, 4702))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
/// Storage: AssetRate ConversionRateToNative (r:1 w:1)
/// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen)
fn update() -> Weight {
// Proof Size summary in bytes:
// Measured: `110`
// Estimated: `4702`
// Minimum execution time: 69_000_000 picoseconds.
Weight::from_parts(71_000_000, 0)
.saturating_add(Weight::from_parts(0, 4702))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
/// Storage: AssetRate ConversionRateToNative (r:1 w:1)
/// Proof: AssetRate ConversionRateToNative (max_values: None, max_size: Some(1237), added: 3712, mode: MaxEncodedLen)
fn remove() -> Weight {
// Proof Size summary in bytes:
// Measured: `110`
// Estimated: `4702`
// Minimum execution time: 70_000_000 picoseconds.
Weight::from_parts(90_000_000, 0)
.saturating_add(Weight::from_parts(0, 4702))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}
@@ -17,25 +17,24 @@
//! Autogenerated weights for `pallet_treasury`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-07-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2023-07-07, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `runner-o7yfgx5n-project-163-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024
//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024
// Executed Command:
// target/production/polkadot
// ./target/debug/polkadot
// benchmark
// pallet
// --chain=rococo-dev
// --steps=50
// --repeat=20
// --repeat=2
// --pallet=pallet_treasury
// --extrinsic=*
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/builds/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=pallet_treasury
// --chain=westend-dev
// --output=./runtime/rococo/src/weights/
// --header=./file_header.txt
// --output=./runtime/westend/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -48,103 +47,177 @@ use core::marker::PhantomData;
/// Weight functions for `pallet_treasury`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_treasury::WeightInfo for WeightInfo<T> {
/// 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() -> Weight {
/// 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: `6`
// Measured: `42`
// Estimated: `1887`
// Minimum execution time: 13_644_000 picoseconds.
Weight::from_parts(13_988_000, 0)
// Minimum execution time: 177_000_000 picoseconds.
Weight::from_parts(191_000_000, 0)
.saturating_add(Weight::from_parts(0, 1887))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(3))
}
/// 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`)
/// 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: `107`
// Measured: `143`
// Estimated: `1489`
// Minimum execution time: 26_304_000 picoseconds.
Weight::from_parts(26_850_000, 0)
// Minimum execution time: 354_000_000 picoseconds.
Weight::from_parts(376_000_000, 0)
.saturating_add(Weight::from_parts(0, 1489))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(2))
}
/// 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`)
/// 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: `265`
// Measured: `301`
// Estimated: `3593`
// Minimum execution time: 40_318_000 picoseconds.
Weight::from_parts(41_598_000, 0)
// Minimum execution time: 547_000_000 picoseconds.
Weight::from_parts(550_000_000, 0)
.saturating_add(Weight::from_parts(0, 3593))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// 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`)
/// 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: `433 + p * (8 ±0)`
// Measured: `470 + p * (8 ±0)`
// Estimated: `3573`
// Minimum execution time: 8_250_000 picoseconds.
Weight::from_parts(10_937_873, 0)
// Minimum execution time: 104_000_000 picoseconds.
Weight::from_parts(121_184_402, 0)
.saturating_add(Weight::from_parts(0, 3573))
// Standard Error: 1_239
.saturating_add(Weight::from_parts(82_426, 0).saturating_mul(p.into()))
// Standard Error: 42_854
.saturating_add(Weight::from_parts(153_112, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
}
/// Storage: `Treasury::Approvals` (r:1 w:1)
/// Proof: `Treasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`)
/// 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: `90`
// Measured: `127`
// Estimated: `1887`
// Minimum execution time: 6_170_000 picoseconds.
Weight::from_parts(6_366_000, 0)
// Minimum execution time: 80_000_000 picoseconds.
Weight::from_parts(82_000_000, 0)
.saturating_add(Weight::from_parts(0, 1887))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
/// Storage: `Treasury::Deactivated` (r:1 w:1)
/// Proof: `Treasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `Balances::InactiveIssuance` (r:1 w:1)
/// Proof: `Balances::InactiveIssuance` (`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:100 w:100)
/// Proof: `Treasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:200 w:200)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
/// The range of component `p` is `[0, 100]`.
/// Storage: Treasury Deactivated (r:1 w:1)
/// Proof: Treasury Deactivated (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen)
/// Storage: Balances InactiveIssuance (r:1 w:1)
/// Proof: Balances InactiveIssuance (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:199 w:199)
/// 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: `175 + p * (251 ±0)`
// Estimated: `1887 + p * (5206 ±0)`
// Minimum execution time: 39_691_000 picoseconds.
Weight::from_parts(29_703_313, 0)
.saturating_add(Weight::from_parts(0, 1887))
// Standard Error: 18_540
.saturating_add(Weight::from_parts(42_601_290, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(3))
// Measured: `331 + p * (251 ±0)`
// Estimated: `3593 + p * (5206 ±0)`
// Minimum execution time: 887_000_000 picoseconds.
Weight::from_parts(828_616_021, 0)
.saturating_add(Weight::from_parts(0, 3593))
// Standard Error: 695_351
.saturating_add(Weight::from_parts(566_114_524, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into())))
.saturating_add(T::DbWeight::get().writes(3))
.saturating_add(T::DbWeight::get().writes(5))
.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(1237), added: 3712, 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(1848), added: 4323, mode: MaxEncodedLen)
fn spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `114`
// Estimated: `4702`
// Minimum execution time: 208_000_000 picoseconds.
Weight::from_parts(222_000_000, 0)
.saturating_add(Weight::from_parts(0, 4702))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: Treasury Spends (r:1 w:1)
/// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen)
/// Storage: XcmPallet QueryCounter (r:1 w:1)
/// Proof Skipped: XcmPallet QueryCounter (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: Configuration ActiveConfig (r:1 w:0)
/// Proof Skipped: Configuration ActiveConfig (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: Dmp DeliveryFeeFactor (r:1 w:0)
/// Proof Skipped: Dmp DeliveryFeeFactor (max_values: None, max_size: None, mode: Measured)
/// Storage: XcmPallet SupportedVersion (r:1 w:0)
/// Proof Skipped: XcmPallet SupportedVersion (max_values: None, max_size: None, mode: Measured)
/// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
/// Proof Skipped: XcmPallet VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
/// Proof Skipped: XcmPallet SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: Dmp DownwardMessageQueues (r:1 w:1)
/// Proof Skipped: Dmp DownwardMessageQueues (max_values: None, max_size: None, mode: Measured)
/// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
/// Proof Skipped: Dmp DownwardMessageQueueHeads (max_values: None, max_size: None, mode: Measured)
/// Storage: XcmPallet Queries (r:0 w:1)
/// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured)
fn payout() -> Weight {
// Proof Size summary in bytes:
// Measured: `737`
// Estimated: `5313`
// Minimum execution time: 551_000_000 picoseconds.
Weight::from_parts(569_000_000, 0)
.saturating_add(Weight::from_parts(0, 5313))
.saturating_add(T::DbWeight::get().reads(9))
.saturating_add(T::DbWeight::get().writes(6))
}
/// Storage: Treasury Spends (r:1 w:1)
/// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen)
/// Storage: XcmPallet Queries (r:1 w:1)
/// Proof Skipped: XcmPallet Queries (max_values: None, max_size: None, mode: Measured)
fn check_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `442`
// Estimated: `5313`
// Minimum execution time: 245_000_000 picoseconds.
Weight::from_parts(281_000_000, 0)
.saturating_add(Weight::from_parts(0, 5313))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: Treasury Spends (r:1 w:1)
/// Proof: Treasury Spends (max_values: None, max_size: Some(1848), added: 4323, mode: MaxEncodedLen)
fn void_spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `172`
// Estimated: `5313`
// Minimum execution time: 147_000_000 picoseconds.
Weight::from_parts(160_000_000, 0)
.saturating_add(Weight::from_parts(0, 5313))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
}
}
+10 -1
View File
@@ -37,7 +37,7 @@ use frame_support::{
parameter_types,
traits::{
fungible::{Balanced, Credit, HoldConsideration, ItemOf},
tokens::{nonfungibles_v2::Inspect, GetSalary, PayFromAccount},
tokens::{nonfungibles_v2::Inspect, pay::PayAssetFromAccount, GetSalary, PayFromAccount},
AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency,
EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter,
KeyOwnerProofSystem, LinearStoragePrice, LockIdentifier, Nothing, OnUnbalanced,
@@ -1186,6 +1186,7 @@ parameter_types! {
pub const MaximumReasonLength: u32 = 300;
pub const MaxApprovals: u32 = 100;
pub const MaxBalance: Balance = Balance::max_value();
pub const SpendPayoutPeriod: BlockNumber = 30 * DAYS;
}
impl pallet_treasury::Config for Runtime {
@@ -1211,6 +1212,14 @@ impl pallet_treasury::Config for Runtime {
type WeightInfo = pallet_treasury::weights::SubstrateWeight<Runtime>;
type MaxApprovals = MaxApprovals;
type SpendOrigin = EnsureWithSuccess<EnsureRoot<AccountId>, AccountId, MaxBalance>;
type AssetKind = u32;
type Beneficiary = AccountId;
type BeneficiaryLookup = Indices;
type Paymaster = PayAssetFromAccount<Assets, TreasuryAccount>;
type BalanceConverter = AssetRate;
type PayoutPeriod = SpendPayoutPeriod;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
impl pallet_asset_rate::Config for Runtime {
+5
View File
@@ -240,4 +240,9 @@ where
.ok_or(pallet::Error::<T>::UnknownAssetKind.into())?;
Ok(rate.saturating_mul_int(balance))
}
/// Set a conversion rate to `1` for the `asset_id`.
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful(asset_id: AssetKindOf<T>) {
pallet::ConversionRateToNative::<T>::set(asset_id.clone(), Some(1.into()));
}
}
+22 -1
View File
@@ -24,7 +24,10 @@ use crate as pallet_bounties;
use frame_support::{
assert_noop, assert_ok, parameter_types,
traits::{ConstU32, ConstU64, OnInitialize},
traits::{
tokens::{PayFromAccount, UnityAssetBalanceConversion},
ConstU32, ConstU64, OnInitialize,
},
PalletId,
};
@@ -104,6 +107,8 @@ parameter_types! {
pub const TreasuryPalletId2: PalletId = PalletId(*b"py/trsr2");
pub static SpendLimit: Balance = u64::MAX;
pub static SpendLimit1: Balance = u64::MAX;
pub TreasuryAccount: u128 = Treasury::account_id();
pub TreasuryInstance1Account: u128 = Treasury1::account_id();
}
impl pallet_treasury::Config for Test {
@@ -123,6 +128,14 @@ impl pallet_treasury::Config for Test {
type SpendFunds = Bounties;
type MaxApprovals = ConstU32<100>;
type SpendOrigin = frame_system::EnsureRootWithSuccess<Self::AccountId, SpendLimit>;
type AssetKind = ();
type Beneficiary = Self::AccountId;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Paymaster = PayFromAccount<Balances, TreasuryAccount>;
type BalanceConverter = UnityAssetBalanceConversion;
type PayoutPeriod = ConstU64<10>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
impl pallet_treasury::Config<Instance1> for Test {
@@ -142,6 +155,14 @@ impl pallet_treasury::Config<Instance1> for Test {
type SpendFunds = Bounties1;
type MaxApprovals = ConstU32<100>;
type SpendOrigin = frame_system::EnsureRootWithSuccess<Self::AccountId, SpendLimit1>;
type AssetKind = ();
type Beneficiary = Self::AccountId;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Paymaster = PayFromAccount<Balances, TreasuryInstance1Account>;
type BalanceConverter = UnityAssetBalanceConversion;
type PayoutPeriod = ConstU64<10>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
parameter_types! {
+13 -1
View File
@@ -24,7 +24,10 @@ use crate as pallet_child_bounties;
use frame_support::{
assert_noop, assert_ok, parameter_types,
traits::{ConstU32, ConstU64, OnInitialize},
traits::{
tokens::{PayFromAccount, UnityAssetBalanceConversion},
ConstU32, ConstU64, OnInitialize,
},
weights::Weight,
PalletId,
};
@@ -104,6 +107,7 @@ 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 SpendLimit: Balance = u64::MAX;
}
@@ -124,6 +128,14 @@ impl pallet_treasury::Config for Test {
type SpendFunds = Bounties;
type MaxApprovals = ConstU32<100>;
type SpendOrigin = frame_system::EnsureRootWithSuccess<Self::AccountId, SpendLimit>;
type AssetKind = ();
type Beneficiary = Self::AccountId;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Paymaster = PayFromAccount<Balances, TreasuryAccount>;
type BalanceConverter = UnityAssetBalanceConversion;
type PayoutPeriod = ConstU64<10>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
parameter_types! {
// This will be 50% of the bounty fee.
+2 -1
View File
@@ -31,6 +31,7 @@ pub mod pay;
pub use misc::{
AssetId, Balance, BalanceStatus, ConversionFromAssetBalance, ConversionToAssetBalance,
ConvertRank, DepositConsequence, ExistenceRequirement, Fortitude, GetSalary, Locker, Precision,
Preservation, Provenance, Restriction, WithdrawConsequence, WithdrawReasons,
Preservation, Provenance, Restriction, UnityAssetBalanceConversion, WithdrawConsequence,
WithdrawReasons,
};
pub use pay::{Pay, PayFromAccount, PaymentStatus};
@@ -277,6 +277,26 @@ pub trait ConversionFromAssetBalance<AssetBalance, AssetId, OutBalance> {
balance: AssetBalance,
asset_id: AssetId,
) -> Result<OutBalance, Self::Error>;
/// Ensures that a conversion for the `asset_id` will be successful if done immediately after
/// this call.
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful(asset_id: AssetId);
}
/// Implements [`ConversionFromAssetBalance`], enabling a 1:1 conversion of the asset balance
/// value to the balance.
pub struct UnityAssetBalanceConversion;
impl<AssetBalance, AssetId, OutBalance>
ConversionFromAssetBalance<AssetBalance, AssetId, OutBalance> for UnityAssetBalanceConversion
where
AssetBalance: Into<OutBalance>,
{
type Error = ();
fn from_asset_balance(balance: AssetBalance, _: AssetId) -> Result<OutBalance, Self::Error> {
Ok(balance.into())
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful(_: AssetId) {}
}
/// Trait to handle NFT locking mechanism to ensure interactions with the asset can be implemented
@@ -23,7 +23,7 @@ use sp_core::{RuntimeDebug, TypedGet};
use sp_runtime::DispatchError;
use sp_std::fmt::Debug;
use super::{fungible, Balance, Preservation::Expendable};
use super::{fungible, fungibles, Balance, Preservation::Expendable};
/// Can be implemented by `PayFromAccount` using a `fungible` impl, but can also be implemented with
/// XCM/MultiAsset and made generic over assets.
@@ -107,3 +107,36 @@ impl<A: TypedGet, F: fungible::Mutate<A::Type>> Pay for PayFromAccount<F, A> {
#[cfg(feature = "runtime-benchmarks")]
fn ensure_concluded(_: Self::Id) {}
}
/// Simple implementation of `Pay` for assets which makes a payment from a "pot" - i.e. a single
/// account.
pub struct PayAssetFromAccount<F, A>(sp_std::marker::PhantomData<(F, A)>);
impl<A, F> frame_support::traits::tokens::Pay for PayAssetFromAccount<F, A>
where
A: TypedGet,
F: fungibles::Mutate<A::Type> + fungibles::Create<A::Type>,
{
type Balance = F::Balance;
type Beneficiary = A::Type;
type AssetKind = F::AssetId;
type Id = ();
type Error = DispatchError;
fn pay(
who: &Self::Beneficiary,
asset: Self::AssetKind,
amount: Self::Balance,
) -> Result<Self::Id, Self::Error> {
<F as fungibles::Mutate<_>>::transfer(asset, &A::get(), who, amount, Expendable)?;
Ok(())
}
fn check_payment(_: ()) -> PaymentStatus {
PaymentStatus::Success
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful(_: &Self::Beneficiary, asset: Self::AssetKind, amount: Self::Balance) {
<F as fungibles::Create<_>>::create(asset.clone(), A::get(), true, amount).unwrap();
<F as fungibles::Mutate<_>>::mint_into(asset, &A::get(), amount).unwrap();
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_concluded(_: Self::Id) {}
}
+23 -1
View File
@@ -29,7 +29,10 @@ use sp_storage::Storage;
use frame_support::{
assert_noop, assert_ok, parameter_types,
storage::StoragePrefixedMap,
traits::{ConstU32, ConstU64, SortedMembers, StorageVersion},
traits::{
tokens::{PayFromAccount, UnityAssetBalanceConversion},
ConstU32, ConstU64, SortedMembers, StorageVersion,
},
PalletId,
};
@@ -123,7 +126,10 @@ parameter_types! {
pub const Burn: Permill = Permill::from_percent(50);
pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
pub const TreasuryPalletId2: PalletId = PalletId(*b"py/trsr2");
pub TreasuryAccount: u128 = Treasury::account_id();
pub TreasuryInstance1Account: u128 = Treasury1::account_id();
}
impl pallet_treasury::Config for Test {
type PalletId = TreasuryPalletId;
type Currency = pallet_balances::Pallet<Test>;
@@ -141,6 +147,14 @@ impl pallet_treasury::Config for Test {
type SpendFunds = ();
type MaxApprovals = ConstU32<100>;
type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>;
type AssetKind = ();
type Beneficiary = Self::AccountId;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Paymaster = PayFromAccount<Balances, TreasuryAccount>;
type BalanceConverter = UnityAssetBalanceConversion;
type PayoutPeriod = ConstU64<10>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
impl pallet_treasury::Config<Instance1> for Test {
@@ -160,6 +174,14 @@ impl pallet_treasury::Config<Instance1> for Test {
type SpendFunds = ();
type MaxApprovals = ConstU32<100>;
type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>;
type AssetKind = ();
type Beneficiary = Self::AccountId;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Paymaster = PayFromAccount<Balances, TreasuryInstance1Account>;
type BalanceConverter = UnityAssetBalanceConversion;
type PayoutPeriod = ConstU64<10>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
parameter_types! {
+5 -2
View File
@@ -17,6 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features =
"derive",
"max-encoded-len",
] }
docify = "0.2.0"
impl-trait-for-tuples = "0.2.2"
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
serde = { version = "1.0.188", features = ["derive"], optional = true }
@@ -26,11 +27,12 @@ frame-system = { path = "../system", default-features = false}
pallet-balances = { path = "../balances", default-features = false}
sp-runtime = { path = "../../primitives/runtime", default-features = false}
sp-std = { path = "../../primitives/std", default-features = false}
sp-core = { path = "../../primitives/core", default-features = false, optional = true}
[dev-dependencies]
sp-core = { path = "../../primitives/core" }
sp-io = { path = "../../primitives/io" }
pallet-utility = { path = "../utility" }
sp-core = { path = "../../primitives/core", default-features = false }
[features]
default = [ "std" ]
@@ -43,12 +45,13 @@ std = [
"pallet-utility/std",
"scale-info/std",
"serde",
"sp-core/std",
"sp-core?/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]
runtime-benchmarks = [
"dep:sp-core",
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
+224 -31
View File
@@ -21,12 +21,41 @@
use super::{Pallet as Treasury, *};
use frame_benchmarking::v1::{account, benchmarks_instance_pallet, BenchmarkError};
use frame_benchmarking::{
v1::{account, BenchmarkError},
v2::*,
};
use frame_support::{
ensure,
traits::{EnsureOrigin, OnInitialize, UnfilteredDispatchable},
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<AssetKind, Beneficiary> {
/// 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<AssetKind, Beneficiary> ArgumentsFactory<AssetKind, Beneficiary> 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;
@@ -66,81 +95,245 @@ fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
}
benchmarks_instance_pallet! {
// Create the arguments for the `spend` dispatchable.
fn create_spend_arguments<T: Config<I>, I: 'static>(
seed: u32,
) -> (T::AssetKind, AssetBalanceOf<T, I>, T::Beneficiary, BeneficiaryLookupOf<T, I>) {
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.
spend {
#[benchmark]
fn spend_local() -> Result<(), BenchmarkError> {
let (_, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED);
let origin = T::SpendOrigin::try_successful_origin();
let origin =
T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let beneficiary = T::Lookup::lookup(beneficiary_lookup.clone()).unwrap();
let call = Call::<T, I>::spend { amount: value, beneficiary: beneficiary_lookup };
}: {
if let Ok(origin) = origin.clone() {
call.dispatch_bypass_filter(origin)?;
}
}
verify {
if origin.is_ok() {
assert_last_event::<T, I>(Event::SpendApproved { proposal_index: 0, amount: value, beneficiary }.into())
}
#[extrinsic_call]
_(origin as T::RuntimeOrigin, value, beneficiary_lookup);
assert_last_event::<T, I>(
Event::SpendApproved { proposal_index: 0, amount: value, beneficiary }.into(),
);
Ok(())
}
propose_spend {
#[benchmark]
fn propose_spend() -> Result<(), BenchmarkError> {
let (caller, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED);
// Whitelist caller account from further DB operations.
let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into());
}: _(RawOrigin::Signed(caller), value, beneficiary_lookup)
reject_proposal {
#[extrinsic_call]
_(RawOrigin::Signed(caller), value, beneficiary_lookup);
Ok(())
}
#[benchmark]
fn reject_proposal() -> Result<(), BenchmarkError> {
let (caller, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED);
#[allow(deprecated)]
Treasury::<T, _>::propose_spend(
RawOrigin::Signed(caller).into(),
value,
beneficiary_lookup
beneficiary_lookup,
)?;
let proposal_id = Treasury::<T, _>::proposal_count() - 1;
let reject_origin =
T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
}: _<T::RuntimeOrigin>(reject_origin, proposal_id)
approve_proposal {
let p in 0 .. T::MaxApprovals::get() - 1;
#[extrinsic_call]
_(reject_origin as T::RuntimeOrigin, proposal_id);
Ok(())
}
#[benchmark]
fn approve_proposal(
p: Linear<0, { T::MaxApprovals::get() - 1 }>,
) -> Result<(), BenchmarkError> {
create_approved_proposals::<T, _>(p)?;
let (caller, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED);
#[allow(deprecated)]
Treasury::<T, _>::propose_spend(
RawOrigin::Signed(caller).into(),
value,
beneficiary_lookup
beneficiary_lookup,
)?;
let proposal_id = Treasury::<T, _>::proposal_count() - 1;
let approve_origin =
T::ApproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
}: _<T::RuntimeOrigin>(approve_origin, proposal_id)
remove_approval {
#[extrinsic_call]
_(approve_origin as T::RuntimeOrigin, proposal_id);
Ok(())
}
#[benchmark]
fn remove_approval() -> Result<(), BenchmarkError> {
let (caller, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED);
#[allow(deprecated)]
Treasury::<T, _>::propose_spend(
RawOrigin::Signed(caller).into(),
value,
beneficiary_lookup
beneficiary_lookup,
)?;
let proposal_id = Treasury::<T, _>::proposal_count() - 1;
#[allow(deprecated)]
Treasury::<T, I>::approve_proposal(RawOrigin::Root.into(), proposal_id)?;
let reject_origin =
T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
}: _<T::RuntimeOrigin>(reject_origin, proposal_id)
on_initialize_proposals {
let p in 0 .. T::MaxApprovals::get();
#[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::<T, _>();
create_approved_proposals::<T, _>(p)?;
}: {
Treasury::<T, _>::on_initialize(frame_system::pallet_prelude::BlockNumberFor::<T>::zero());
#[block]
{
Treasury::<T, _>::on_initialize(0u32.into());
}
Ok(())
}
#[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::<T, _>(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::<T>::block_number();
let expire_at = valid_from.saturating_add(T::PayoutPeriod::get());
assert_last_event::<T, I>(
Event::AssetSpendApproved {
index: 0,
asset_kind,
amount,
beneficiary,
valid_from,
expire_at,
}
.into(),
);
Ok(())
}
#[benchmark]
fn payout() -> Result<(), BenchmarkError> {
let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?;
let (asset_kind, amount, beneficiary, beneficiary_lookup) =
create_spend_arguments::<T, _>(SEED);
T::BalanceConverter::ensure_successful(asset_kind.clone());
Treasury::<T, _>::spend(
origin,
Box::new(asset_kind.clone()),
amount,
Box::new(beneficiary_lookup),
None,
)?;
T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount);
let caller: T::AccountId = account("caller", 0, SEED);
#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), 0u32);
let id = match Spends::<T, I>::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::<T, I>(Event::Paid { index: 0, payment_id: id }.into());
assert!(Treasury::<T, _>::payout(RawOrigin::Signed(caller).into(), 0u32).is_err());
Ok(())
}
#[benchmark]
fn check_status() -> Result<(), BenchmarkError> {
let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?;
let (asset_kind, amount, beneficiary, beneficiary_lookup) =
create_spend_arguments::<T, _>(SEED);
T::BalanceConverter::ensure_successful(asset_kind.clone());
Treasury::<T, _>::spend(
origin,
Box::new(asset_kind.clone()),
amount,
Box::new(beneficiary_lookup),
None,
)?;
T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount);
let caller: T::AccountId = account("caller", 0, SEED);
Treasury::<T, _>::payout(RawOrigin::Signed(caller.clone()).into(), 0u32)?;
match Spends::<T, I>::get(0).unwrap().status {
PaymentState::Attempted { id, .. } => {
T::Paymaster::ensure_concluded(id);
},
_ => panic!("No payout attempt made"),
};
#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), 0u32);
if let Some(s) = Spends::<T, I>::get(0) {
assert!(!matches!(s.status, PaymentState::Attempted { .. }));
}
Ok(())
}
#[benchmark]
fn void_spend() -> Result<(), BenchmarkError> {
let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?;
let (asset_kind, amount, _, beneficiary_lookup) = create_spend_arguments::<T, _>(SEED);
T::BalanceConverter::ensure_successful(asset_kind.clone());
Treasury::<T, _>::spend(
origin,
Box::new(asset_kind.clone()),
amount,
Box::new(beneficiary_lookup),
None,
)?;
assert!(Spends::<T, I>::get(0).is_some());
let origin =
T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
#[extrinsic_call]
_(origin as T::RuntimeOrigin, 0u32);
assert!(Spends::<T, I>::get(0).is_none());
Ok(())
}
impl_benchmark_test_suite!(Treasury, crate::tests::new_test_ext(), crate::tests::Test);
+451 -45
View File
@@ -15,46 +15,60 @@
// 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.
//!
//! - [`Config`]
//! - [`Call`]
//!
//! ## 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.
//! 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, the Council could vote to fund the Treasury with a portion of the block
//! 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.
//! - **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.
//! - **Spend** An approved proposal for transferring a specific amount of funds to a designated
//! beneficiary.
//!
//! ## Interface
//! ### Example
//!
//! ### Dispatchable Functions
//! 1. Multiple local spends approved by spend origins and received by a beneficiary.
#![doc = docify::embed!("src/tests.rs", spend_local_origin_works)]
//!
//! General spending/proposal protocol:
//! - `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.
//! - `remove_approval` - Remove an approval, the deposit will no longer be returned.
//! 2. Approve a spend of some asset kind and claim it.
#![doc = docify::embed!("src/tests.rs", spend_payout_works)]
//!
//! ## GenesisConfig
//! ## Pallet API
//!
//! The Treasury pallet depends on the [`GenesisConfig`].
//! 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)]
@@ -62,6 +76,8 @@ mod benchmarking;
#[cfg(test)]
mod tests;
pub mod weights;
#[cfg(feature = "runtime-benchmarks")]
pub use benchmarking::ArgumentsFactory;
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
@@ -75,7 +91,7 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*};
use frame_support::{
print,
traits::{
Currency, ExistenceRequirement::KeepAlive, Get, Imbalance, OnUnbalanced,
tokens::Pay, Currency, ExistenceRequirement::KeepAlive, Get, Imbalance, OnUnbalanced,
ReservableCurrency, WithdrawReasons,
},
weights::Weight,
@@ -87,6 +103,7 @@ pub use weights::WeightInfo;
pub type BalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
pub type AssetBalanceOf<T, I> = <<T as Config<I>>::Paymaster as Pay>::Balance;
pub type PositiveImbalanceOf<T, I = ()> = <<T as Config<I>>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::PositiveImbalance;
@@ -94,6 +111,7 @@ pub type NegativeImbalanceOf<T, I = ()> = <<T as Config<I>>::Currency as Currenc
<T as frame_system::Config>::AccountId,
>>::NegativeImbalance;
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
type BeneficiaryLookupOf<T, I> = <<T as Config<I>>::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
@@ -133,10 +151,47 @@ pub struct Proposal<AccountId, Balance> {
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<Id> {
/// 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<AssetKind, AssetBalance, Beneficiary, BlockNumber, PaymentId> {
// 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<PaymentId>,
}
/// 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::*};
use frame_support::{
dispatch_context::with_context,
pallet_prelude::*,
traits::tokens::{ConversionFromAssetBalance, PaymentStatus},
};
use frame_system::pallet_prelude::*;
#[pallet::pallet]
@@ -201,9 +256,38 @@ pub mod pallet {
type MaxApprovals: Get<u32>;
/// The origin required for approving spends from the treasury outside of the proposal
/// process. The `Success` value is the maximum amount that this origin is allowed to
/// spend at a time.
/// 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<Self::RuntimeOrigin, Success = BalanceOf<Self, I>>;
/// 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<Target = Self::Beneficiary>;
/// Type for processing spends of [Self::AssetKind] in favor of [`Self::Beneficiary`].
type Paymaster: Pay<Beneficiary = Self::Beneficiary, AssetKind = Self::AssetKind>;
/// 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<
<Self::Paymaster as Pay>::Balance,
Self::AssetKind,
BalanceOf<Self, I>,
>;
/// The period during which an approved treasury spend has to be claimed.
#[pallet::constant]
type PayoutPeriod: Get<BlockNumberFor<Self>>;
/// Helper type for benchmarks.
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper: ArgumentsFactory<Self::AssetKind, Self::Beneficiary>;
}
/// Number of proposals that have been made.
@@ -233,6 +317,27 @@ pub mod pallet {
pub type Approvals<T: Config<I>, I: 'static = ()> =
StorageValue<_, BoundedVec<ProposalIndex, T::MaxApprovals>, ValueQuery>;
/// The count of spends that have been made.
#[pallet::storage]
pub(crate) type SpendCount<T, I = ()> = 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<T: Config<I>, I: 'static = ()> = StorageMap<
_,
Twox64Concat,
SpendIndex,
SpendStatus<
T::AssetKind,
AssetBalanceOf<T, I>,
T::Beneficiary,
BlockNumberFor<T>,
<T::Paymaster as Pay>::Id,
>,
OptionQuery,
>;
#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
@@ -277,6 +382,24 @@ pub mod pallet {
},
/// The inactive funds of the pallet have been updated.
UpdatedInactive { reactivated: BalanceOf<T, I>, deactivated: BalanceOf<T, I> },
/// A new asset spend proposal has been approved.
AssetSpendApproved {
index: SpendIndex,
asset_kind: T::AssetKind,
amount: AssetBalanceOf<T, I>,
beneficiary: T::Beneficiary,
valid_from: BlockNumberFor<T>,
expire_at: BlockNumberFor<T>,
},
/// An approved spend was voided.
AssetSpendVoided { index: SpendIndex },
/// A payment happened.
Paid { index: SpendIndex, payment_id: <T::Paymaster as Pay>::Id },
/// A payment failed and can be retried.
PaymentFailed { index: SpendIndex, payment_id: <T::Paymaster as Pay>::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.
@@ -284,7 +407,7 @@ pub mod pallet {
pub enum Error<T, I = ()> {
/// Proposer's balance is too low.
InsufficientProposersBalance,
/// No proposal or bounty at that index.
/// No proposal, bounty or spend at that index.
InvalidIndex,
/// Too many approvals in the queue.
TooManyApprovals,
@@ -293,6 +416,20 @@ pub mod pallet {
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]
@@ -328,12 +465,22 @@ pub mod pallet {
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Put forward a suggestion for spending. A deposit proportional to the value
/// is reserved and slashed if the proposal is rejected. It is returned once the
/// proposal is awarded.
/// Put forward a suggestion for spending.
///
/// ## Complexity
/// ## 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)]
@@ -360,12 +507,21 @@ pub mod pallet {
Ok(())
}
/// Reject a proposed spend. The original deposit will be slashed.
/// Reject a proposed spend.
///
/// May only be called from `T::RejectOrigin`.
/// ## Dispatch Origin
///
/// ## Complexity
/// 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)]
@@ -391,13 +547,23 @@ pub mod pallet {
Ok(())
}
/// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary
/// and the original deposit will be returned.
/// Approve a proposal.
///
/// May only be called from `T::ApproveOrigin`.
/// ## Dispatch Origin
///
/// ## Complexity
/// 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)]
@@ -418,15 +584,24 @@ pub mod pallet {
/// Propose and approve a spend of treasury funds.
///
/// - `origin`: Must be `SpendOrigin` with the `Success` value being at least `amount`.
/// ## 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.
///
/// NOTE: For record-keeping purposes, the proposer is deemed to be equivalent to the
/// beneficiary.
/// ## Events
///
/// Emits [`Event::SpendApproved`] if successful.
#[pallet::call_index(3)]
#[pallet::weight(T::WeightInfo::spend())]
pub fn spend(
#[pallet::weight(T::WeightInfo::spend_local())]
pub fn spend_local(
origin: OriginFor<T>,
#[pallet::compact] amount: BalanceOf<T, I>,
beneficiary: AccountIdLookupOf<T>,
@@ -472,18 +647,26 @@ pub mod pallet {
}
/// 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.
///
/// May only be called from `T::RejectOrigin`.
/// ### Parameters
/// - `proposal_id`: The index of a proposal
///
/// ## Complexity
/// ### Complexity
/// - O(A) where `A` is the number of approvals
///
/// Errors:
/// - `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.
/// ### 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(
@@ -503,6 +686,229 @@ pub mod pallet {
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<T>,
asset_kind: Box<T::AssetKind>,
#[pallet::compact] amount: AssetBalanceOf<T, I>,
beneficiary: Box<BeneficiaryLookupOf<T, I>>,
valid_from: Option<BlockNumberFor<T>>,
) -> DispatchResult {
let max_amount = T::SpendOrigin::ensure_origin(origin)?;
let beneficiary = T::BeneficiaryLookup::lookup(*beneficiary)?;
let now = frame_system::Pallet::<T>::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::<T, I>::SpendExpired);
let native_amount =
T::BalanceConverter::from_asset_balance(amount, *asset_kind.clone())
.map_err(|_| Error::<T, I>::FailedToConvertBalance)?;
ensure!(native_amount <= max_amount, Error::<T, I>::InsufficientPermission);
with_context::<SpendContext<BalanceOf<T, I>>, _>(|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::<T, I>::InsufficientPermission)
} else {
*spend = spend.saturating_add(native_amount);
Ok(())
}
})
.unwrap_or(Ok(()))?;
let index = SpendCount::<T, I>::get();
Spends::<T, I>::insert(
index,
SpendStatus {
asset_kind: *asset_kind.clone(),
amount,
beneficiary: beneficiary.clone(),
valid_from,
expire_at,
status: PaymentState::Pending,
},
);
SpendCount::<T, I>::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<T>, index: SpendIndex) -> DispatchResult {
ensure_signed(origin)?;
let mut spend = Spends::<T, I>::get(index).ok_or(Error::<T, I>::InvalidIndex)?;
let now = frame_system::Pallet::<T>::block_number();
ensure!(now >= spend.valid_from, Error::<T, I>::EarlyPayout);
ensure!(spend.expire_at > now, Error::<T, I>::SpendExpired);
ensure!(
matches!(spend.status, PaymentState::Pending | PaymentState::Failed),
Error::<T, I>::AlreadyAttempted
);
let id = T::Paymaster::pay(&spend.beneficiary, spend.asset_kind.clone(), spend.amount)
.map_err(|_| Error::<T, I>::PayoutError)?;
spend.status = PaymentState::Attempted { id };
Spends::<T, I>::insert(index, spend);
Self::deposit_event(Event::<T, I>::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<T>, index: SpendIndex) -> DispatchResultWithPostInfo {
use PaymentState as State;
use PaymentStatus as Status;
ensure_signed(origin)?;
let mut spend = Spends::<T, I>::get(index).ok_or(Error::<T, I>::InvalidIndex)?;
let now = frame_system::Pallet::<T>::block_number();
if now > spend.expire_at && !matches!(spend.status, State::Attempted { .. }) {
// spend has expired and no further status update is expected.
Spends::<T, I>::remove(index);
Self::deposit_event(Event::<T, I>::SpendProcessed { index });
return Ok(Pays::No.into())
}
let payment_id = match spend.status {
State::Attempted { id } => id,
_ => return Err(Error::<T, I>::NotAttempted.into()),
};
match T::Paymaster::check_payment(payment_id) {
Status::Failure => {
spend.status = PaymentState::Failed;
Spends::<T, I>::insert(index, spend);
Self::deposit_event(Event::<T, I>::PaymentFailed { index, payment_id });
},
Status::Success | Status::Unknown => {
Spends::<T, I>::remove(index);
Self::deposit_event(Event::<T, I>::SpendProcessed { index });
return Ok(Pays::No.into())
},
Status::InProgress => return Err(Error::<T, I>::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<T>, index: SpendIndex) -> DispatchResult {
T::RejectOrigin::ensure_origin(origin)?;
let spend = Spends::<T, I>::get(index).ok_or(Error::<T, I>::InvalidIndex)?;
ensure!(
matches!(spend.status, PaymentState::Pending | PaymentState::Failed),
Error::<T, I>::AlreadyAttempted
);
Spends::<T, I>::remove(index);
Self::deposit_event(Event::<T, I>::AssetSpendVoided { index });
Ok(())
}
}
}
+405 -23
View File
@@ -19,6 +19,7 @@
#![cfg(test)]
use core::{cell::RefCell, marker::PhantomData};
use sp_core::H256;
use sp_runtime::{
traits::{BadOrigin, BlakeTwo256, Dispatchable, IdentityLookup},
@@ -26,8 +27,13 @@ use sp_runtime::{
};
use frame_support::{
assert_err_ignore_postinfo, assert_noop, assert_ok, parameter_types,
traits::{ConstU32, ConstU64, OnInitialize},
assert_err_ignore_postinfo, assert_noop, assert_ok,
pallet_prelude::Pays,
parameter_types,
traits::{
tokens::{ConversionFromAssetBalance, PaymentStatus},
ConstU32, ConstU64, OnInitialize,
},
PalletId,
};
@@ -96,10 +102,64 @@ impl pallet_utility::Config for Test {
type WeightInfo = ();
}
thread_local! {
pub static PAID: RefCell<BTreeMap<(u128, u32), u64>> = RefCell::new(BTreeMap::new());
pub static STATUS: RefCell<BTreeMap<u64, PaymentStatus>> = RefCell::new(BTreeMap::new());
pub static LAST_ID: RefCell<u64> = RefCell::new(0u64);
}
/// 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<Self::Id, Self::Error> {
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<RuntimeOrigin> for TestSpendOrigin {
@@ -120,6 +180,16 @@ impl frame_support::traits::EnsureOrigin<RuntimeOrigin> for TestSpendOrigin {
}
}
pub struct MulBy<N>(PhantomData<N>);
impl<N: Get<u64>> ConversionFromAssetBalance<u64, u32, u64> for MulBy<N> {
type Error = ();
fn from_asset_balance(balance: u64, _asset_id: u32) -> Result<u64, Self::Error> {
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<Test>;
@@ -137,6 +207,14 @@ impl Config for Test {
type SpendFunds = ();
type MaxApprovals = ConstU32<100>;
type SpendOrigin = TestSpendOrigin;
type AssetKind = u32;
type Beneficiary = u128;
type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
type Paymaster = TestPay;
type BalanceConverter = MulBy<ConstU64<2>>;
type PayoutPeriod = SpendPayoutPeriod;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
pub fn new_test_ext() -> sp_io::TestExternalities {
@@ -151,6 +229,14 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
t.into()
}
fn get_payment_id(i: SpendIndex) -> Option<u64> {
let spend = Spends::<Test, _>::get(i).expect("no spend");
match spend.status {
PaymentState::Attempted { id } => Some(id),
_ => None,
}
}
#[test]
fn genesis_config_works() {
new_test_ext().execute_with(|| {
@@ -160,46 +246,49 @@ fn genesis_config_works() {
}
#[test]
fn spend_origin_permissioning_works() {
fn spend_local_origin_permissioning_works() {
new_test_ext().execute_with(|| {
assert_noop!(Treasury::spend(RuntimeOrigin::signed(1), 1, 1), BadOrigin);
assert_noop!(Treasury::spend_local(RuntimeOrigin::signed(1), 1, 1), BadOrigin);
assert_noop!(
Treasury::spend(RuntimeOrigin::signed(10), 6, 1),
Treasury::spend_local(RuntimeOrigin::signed(10), 6, 1),
Error::<Test>::InsufficientPermission
);
assert_noop!(
Treasury::spend(RuntimeOrigin::signed(11), 11, 1),
Treasury::spend_local(RuntimeOrigin::signed(11), 11, 1),
Error::<Test>::InsufficientPermission
);
assert_noop!(
Treasury::spend(RuntimeOrigin::signed(12), 21, 1),
Treasury::spend_local(RuntimeOrigin::signed(12), 21, 1),
Error::<Test>::InsufficientPermission
);
assert_noop!(
Treasury::spend(RuntimeOrigin::signed(13), 51, 1),
Treasury::spend_local(RuntimeOrigin::signed(13), 51, 1),
Error::<Test>::InsufficientPermission
);
});
}
#[docify::export]
#[test]
fn spend_origin_works() {
fn spend_local_origin_works() {
new_test_ext().execute_with(|| {
// Check that accumulate works when we have Some value in Dummy already.
Balances::make_free_balance_be(&Treasury::account_id(), 101);
assert_ok!(Treasury::spend(RuntimeOrigin::signed(10), 5, 6));
assert_ok!(Treasury::spend(RuntimeOrigin::signed(10), 5, 6));
assert_ok!(Treasury::spend(RuntimeOrigin::signed(10), 5, 6));
assert_ok!(Treasury::spend(RuntimeOrigin::signed(10), 5, 6));
assert_ok!(Treasury::spend(RuntimeOrigin::signed(11), 10, 6));
assert_ok!(Treasury::spend(RuntimeOrigin::signed(12), 20, 6));
assert_ok!(Treasury::spend(RuntimeOrigin::signed(13), 50, 6));
// 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.
<Treasury as OnInitialize<u64>>::on_initialize(1);
assert_eq!(Balances::free_balance(6), 0);
// free balance of `6` is `100`, spend period has passed.
<Treasury as OnInitialize<u64>>::on_initialize(2);
assert_eq!(Balances::free_balance(6), 100);
// `100` spent, `1` burned.
assert_eq!(Treasury::pot(), 0);
});
}
@@ -579,13 +668,13 @@ fn remove_already_removed_approval_fails() {
}
#[test]
fn spending_in_batch_respects_max_total() {
fn spending_local_in_batch_respects_max_total() {
new_test_ext().execute_with(|| {
// Respect the `max_total` for the given origin.
assert_ok!(RuntimeCall::from(UtilityCall::batch_all {
calls: vec![
RuntimeCall::from(TreasuryCall::spend { amount: 2, beneficiary: 100 }),
RuntimeCall::from(TreasuryCall::spend { amount: 2, beneficiary: 101 })
RuntimeCall::from(TreasuryCall::spend_local { amount: 2, beneficiary: 100 }),
RuntimeCall::from(TreasuryCall::spend_local { amount: 2, beneficiary: 101 })
]
})
.dispatch(RuntimeOrigin::signed(10)));
@@ -593,8 +682,8 @@ fn spending_in_batch_respects_max_total() {
assert_err_ignore_postinfo!(
RuntimeCall::from(UtilityCall::batch_all {
calls: vec![
RuntimeCall::from(TreasuryCall::spend { amount: 2, beneficiary: 100 }),
RuntimeCall::from(TreasuryCall::spend { amount: 4, beneficiary: 101 })
RuntimeCall::from(TreasuryCall::spend_local { amount: 2, beneficiary: 100 }),
RuntimeCall::from(TreasuryCall::spend_local { amount: 4, beneficiary: 101 })
]
})
.dispatch(RuntimeOrigin::signed(10)),
@@ -602,3 +691,296 @@ fn spending_in_batch_respects_max_total() {
);
})
}
#[test]
fn spending_in_batch_respects_max_total() {
new_test_ext().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::<Test, _>::InsufficientPermission
);
})
}
#[test]
fn spend_origin_works() {
new_test_ext().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::<Test, _>::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::<Test, _>::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::<Test, _>::InsufficientPermission
);
assert_eq!(SpendCount::<Test, _>::get(), 4);
assert_eq!(Spends::<Test, _>::iter().count(), 4);
});
}
#[test]
fn spend_works() {
new_test_ext().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::<Test, _>::get(), 1);
assert_eq!(
Spends::<Test, _>::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::<Test, _>::AssetSpendApproved {
index: 0,
asset_kind: 1,
amount: 2,
beneficiary: 6,
valid_from: 1,
expire_at: 6,
}
.into(),
);
});
}
#[test]
fn spend_expires() {
new_test_ext().execute_with(|| {
assert_eq!(<Test as Config>::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::<Test, _>::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::<Test, _>::SpendExpired
);
});
}
#[docify::export]
#[test]
fn spend_payout_works() {
new_test_ext().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::<Test, _>::get(), 1);
let payment_id = get_payment_id(0).expect("no payment attempt");
System::assert_last_event(Event::<Test, _>::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::<Test, _>::SpendProcessed { index: 0 }.into());
// cannot payout the same spend twice.
assert_noop!(Treasury::payout(RuntimeOrigin::signed(1), 0), Error::<Test, _>::InvalidIndex);
});
}
#[test]
fn payout_retry_works() {
new_test_ext().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::<Test, _>::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::<Test, _>::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() {
new_test_ext().execute_with(|| {
assert_eq!(<Test as Config>::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::<Test, _>::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() {
new_test_ext().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::<Test, _>::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::<Test, _>::get(1), None);
});
}
#[test]
fn check_status_works() {
new_test_ext().execute_with(|| {
assert_eq!(<Test as Config>::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::<Test, _>::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::<Test, _>::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::<Test, _>::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::<Test, _>::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::<Test, _>::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::<Test, _>::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::<Test, _>::SpendProcessed { index: 4 }.into());
});
}
+165 -58
View File
@@ -18,28 +18,23 @@
//! Autogenerated weights for pallet_treasury
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-06-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2023-07-07, STEPS: `20`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `runner-e8ezs4ez-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// ./target/production/substrate
// ./target/debug/substrate
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pallet_treasury
// --no-storage-info
// --no-median-slopes
// --no-min-squares
// --steps=20
// --repeat=2
// --pallet=pallet-treasury
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --output=./frame/treasury/src/weights.rs
// --header=./HEADER-APACHE2
// --output=./frame/treasury/src/._weights.rs
// --template=./.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
@@ -52,12 +47,16 @@ use core::marker::PhantomData;
/// Weight functions needed for pallet_treasury.
pub trait WeightInfo {
fn spend() -> Weight;
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.
@@ -69,12 +68,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// 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() -> Weight {
fn spend_local() -> Weight {
// Proof Size summary in bytes:
// Measured: `76`
// Estimated: `1887`
// Minimum execution time: 15_057_000 picoseconds.
Weight::from_parts(15_803_000, 1887)
// Minimum execution time: 179_000_000 picoseconds.
Weight::from_parts(190_000_000, 1887)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(3_u64))
}
@@ -86,8 +85,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `177`
// Estimated: `1489`
// Minimum execution time: 28_923_000 picoseconds.
Weight::from_parts(29_495_000, 1489)
// Minimum execution time: 349_000_000 picoseconds.
Weight::from_parts(398_000_000, 1489)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
@@ -99,8 +98,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `335`
// Estimated: `3593`
// Minimum execution time: 30_539_000 picoseconds.
Weight::from_parts(30_986_000, 3593)
// Minimum execution time: 367_000_000 picoseconds.
Weight::from_parts(388_000_000, 3593)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
@@ -111,12 +110,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// The range of component `p` is `[0, 99]`.
fn approve_proposal(p: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `504 + p * (8 ±0)`
// Measured: `483 + p * (9 ±0)`
// Estimated: `3573`
// Minimum execution time: 9_320_000 picoseconds.
Weight::from_parts(12_606_599, 3573)
// Standard Error: 1_302
.saturating_add(Weight::from_parts(71_054, 0).saturating_mul(p.into()))
// Minimum execution time: 111_000_000 picoseconds.
Weight::from_parts(108_813_243, 3573)
// Standard Error: 147_887
.saturating_add(Weight::from_parts(683_216, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -126,8 +125,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `161`
// Estimated: `1887`
// Minimum execution time: 7_231_000 picoseconds.
Weight::from_parts(7_459_000, 1887)
// Minimum execution time: 71_000_000 picoseconds.
Weight::from_parts(78_000_000, 1887)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -135,27 +134,81 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// 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:100 w:100)
/// 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:200 w:200)
/// 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, 100]`.
/// The range of component `p` is `[0, 99]`.
fn on_initialize_proposals(p: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `421 + p * (251 ±0)`
// Measured: `427 + p * (251 ±0)`
// Estimated: `1887 + p * (5206 ±0)`
// Minimum execution time: 44_769_000 picoseconds.
Weight::from_parts(57_915_572, 1887)
// Standard Error: 59_484
.saturating_add(Weight::from_parts(42_343_732, 0).saturating_mul(p.into()))
// Minimum execution time: 614_000_000 picoseconds.
Weight::from_parts(498_501_558, 1887)
// Standard Error: 1_070_260
.saturating_add(Weight::from_parts(599_011_690, 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(69), added: 2544, mode: MaxEncodedLen)
fn spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `140`
// Estimated: `3501`
// Minimum execution time: 214_000_000 picoseconds.
Weight::from_parts(216_000_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(69), added: 2544, 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: `705`
// Estimated: `6208`
// Minimum execution time: 760_000_000 picoseconds.
Weight::from_parts(822_000_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(69), added: 2544, mode: MaxEncodedLen)
fn check_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `194`
// Estimated: `3534`
// Minimum execution time: 153_000_000 picoseconds.
Weight::from_parts(160_000_000, 3534)
.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(69), added: 2544, mode: MaxEncodedLen)
fn void_spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `194`
// Estimated: `3534`
// Minimum execution time: 147_000_000 picoseconds.
Weight::from_parts(181_000_000, 3534)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
}
// For backwards compatibility and tests
@@ -166,12 +219,12 @@ impl WeightInfo for () {
/// 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() -> Weight {
fn spend_local() -> Weight {
// Proof Size summary in bytes:
// Measured: `76`
// Estimated: `1887`
// Minimum execution time: 15_057_000 picoseconds.
Weight::from_parts(15_803_000, 1887)
// Minimum execution time: 179_000_000 picoseconds.
Weight::from_parts(190_000_000, 1887)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
@@ -183,8 +236,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `177`
// Estimated: `1489`
// Minimum execution time: 28_923_000 picoseconds.
Weight::from_parts(29_495_000, 1489)
// Minimum execution time: 349_000_000 picoseconds.
Weight::from_parts(398_000_000, 1489)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
@@ -196,8 +249,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `335`
// Estimated: `3593`
// Minimum execution time: 30_539_000 picoseconds.
Weight::from_parts(30_986_000, 3593)
// Minimum execution time: 367_000_000 picoseconds.
Weight::from_parts(388_000_000, 3593)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
@@ -208,12 +261,12 @@ impl WeightInfo for () {
/// The range of component `p` is `[0, 99]`.
fn approve_proposal(p: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `504 + p * (8 ±0)`
// Measured: `483 + p * (9 ±0)`
// Estimated: `3573`
// Minimum execution time: 9_320_000 picoseconds.
Weight::from_parts(12_606_599, 3573)
// Standard Error: 1_302
.saturating_add(Weight::from_parts(71_054, 0).saturating_mul(p.into()))
// Minimum execution time: 111_000_000 picoseconds.
Weight::from_parts(108_813_243, 3573)
// Standard Error: 147_887
.saturating_add(Weight::from_parts(683_216, 0).saturating_mul(p.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -223,8 +276,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `161`
// Estimated: `1887`
// Minimum execution time: 7_231_000 picoseconds.
Weight::from_parts(7_459_000, 1887)
// Minimum execution time: 71_000_000 picoseconds.
Weight::from_parts(78_000_000, 1887)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -232,25 +285,79 @@ impl WeightInfo for () {
/// 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:100 w:100)
/// 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:200 w:200)
/// 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, 100]`.
/// The range of component `p` is `[0, 99]`.
fn on_initialize_proposals(p: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `421 + p * (251 ±0)`
// Measured: `427 + p * (251 ±0)`
// Estimated: `1887 + p * (5206 ±0)`
// Minimum execution time: 44_769_000 picoseconds.
Weight::from_parts(57_915_572, 1887)
// Standard Error: 59_484
.saturating_add(Weight::from_parts(42_343_732, 0).saturating_mul(p.into()))
// Minimum execution time: 614_000_000 picoseconds.
Weight::from_parts(498_501_558, 1887)
// Standard Error: 1_070_260
.saturating_add(Weight::from_parts(599_011_690, 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(69), added: 2544, mode: MaxEncodedLen)
fn spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `140`
// Estimated: `3501`
// Minimum execution time: 214_000_000 picoseconds.
Weight::from_parts(216_000_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(69), added: 2544, 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: `705`
// Estimated: `6208`
// Minimum execution time: 760_000_000 picoseconds.
Weight::from_parts(822_000_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(69), added: 2544, mode: MaxEncodedLen)
fn check_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `194`
// Estimated: `3534`
// Minimum execution time: 153_000_000 picoseconds.
Weight::from_parts(160_000_000, 3534)
.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(69), added: 2544, mode: MaxEncodedLen)
fn void_spend() -> Weight {
// Proof Size summary in bytes:
// Measured: `194`
// Estimated: `3534`
// Minimum execution time: 147_000_000 picoseconds.
Weight::from_parts(181_000_000, 3534)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}
+14
View File
@@ -630,6 +630,13 @@ impl sp_std::str::FromStr for AccountId32 {
}
}
/// Creates an [`AccountId32`] from the input, which should contain at least 32 bytes.
impl FromEntropy for AccountId32 {
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
Ok(AccountId32::new(FromEntropy::from_entropy(input)?))
}
}
#[cfg(feature = "std")]
pub use self::dummy::*;
@@ -1171,6 +1178,13 @@ impl FromEntropy for bool {
}
}
/// Create the unit type for any given input.
impl FromEntropy for () {
fn from_entropy(_: &mut impl codec::Input) -> Result<Self, codec::Error> {
Ok(())
}
}
macro_rules! impl_from_entropy {
($type:ty , $( $others:tt )*) => {
impl_from_entropy!($type);