mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 18:41:05 +00:00
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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user