mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-14 22:35:43 +00:00
3dece311be
Combination of paritytech/polkadot#7005, its addon PR paritytech/polkadot#7585 and its companion paritytech/cumulus#2433. This PR introduces a new XcmFeesToAccount struct which implements the `FeeManager` trait, and assigns this struct as the `FeeManager` in the XCM config for all runtimes. The struct simply deposits all fees handled by the XCM executor to a specified account. In all runtimes, the specified account is configured as the treasury account. XCM __delivery__ fees are now being introduced (unless the root origin is sending a message to a system parachain on behalf of the originating chain). # Note for reviewers Most file changes are tests that had to be modified to account for the new fees. Main changes are in: - cumulus/pallets/xcmp-queue/src/lib.rs <- To make it track the delivery fees exponential factor - polkadot/xcm/xcm-builder/src/fee_handling.rs <- Added. Has the FeeManager implementation - All runtime xcm_config files <- To add the FeeManager to the XCM configuration # Important note After this change, instructions that create and send a new XCM (Query*, Report*, ExportMessage, InitiateReserveWithdraw, InitiateTeleport, DepositReserveAsset, TransferReserveAsset, LockAsset and RequestUnlock) will require the corresponding origin account in the origin register to pay for transport delivery fees, and the onward message will fail to be sent if the origin account does not have the required amount. This delivery fee is on top of what we already collect as tx fees in pallet-xcm and XCM BuyExecution fees! Wallet UIs that want to expose the new delivery fee can do so using the formula: ``` delivery_fee_factor * (base_fee + encoded_msg_len * per_byte_fee) ``` where the delivery fee factor can be obtained from the corresponding pallet based on which transport you are using (UMP, HRMP or bridges), the base fee is a constant, the encoded message length from the message itself and the per byte fee is the same as the configured per byte fee for txs (i.e. `TransactionByteFee`). --------- Co-authored-by: Branislav Kontur <bkontur@gmail.com> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Giles Cope <gilescope@gmail.com> Co-authored-by: command-bot <> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
184 lines
6.4 KiB
Rust
184 lines
6.4 KiB
Rust
// 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::impls::AccountIdOf;
|
|
use cumulus_primitives_core::{IsSystem, ParaId};
|
|
use frame_support::{
|
|
traits::{fungibles::Inspect, tokens::ConversionToAssetBalance, Contains, ContainsPair},
|
|
weights::Weight,
|
|
};
|
|
use sp_runtime::traits::Get;
|
|
use sp_std::marker::PhantomData;
|
|
use xcm::latest::prelude::*;
|
|
|
|
/// A `ChargeFeeInFungibles` implementation that converts the output of
|
|
/// a given WeightToFee implementation an amount charged in
|
|
/// a particular assetId from pallet-assets
|
|
pub struct AssetFeeAsExistentialDepositMultiplier<
|
|
Runtime,
|
|
WeightToFee,
|
|
BalanceConverter,
|
|
AssetInstance: 'static,
|
|
>(PhantomData<(Runtime, WeightToFee, BalanceConverter, AssetInstance)>);
|
|
impl<CurrencyBalance, Runtime, WeightToFee, BalanceConverter, AssetInstance>
|
|
cumulus_primitives_utility::ChargeWeightInFungibles<
|
|
AccountIdOf<Runtime>,
|
|
pallet_assets::Pallet<Runtime, AssetInstance>,
|
|
> for AssetFeeAsExistentialDepositMultiplier<Runtime, WeightToFee, BalanceConverter, AssetInstance>
|
|
where
|
|
Runtime: pallet_assets::Config<AssetInstance>,
|
|
WeightToFee: frame_support::weights::WeightToFee<Balance = CurrencyBalance>,
|
|
BalanceConverter: ConversionToAssetBalance<
|
|
CurrencyBalance,
|
|
<Runtime as pallet_assets::Config<AssetInstance>>::AssetId,
|
|
<Runtime as pallet_assets::Config<AssetInstance>>::Balance,
|
|
>,
|
|
AccountIdOf<Runtime>:
|
|
From<polkadot_primitives::AccountId> + Into<polkadot_primitives::AccountId>,
|
|
{
|
|
fn charge_weight_in_fungibles(
|
|
asset_id: <pallet_assets::Pallet<Runtime, AssetInstance> as Inspect<
|
|
AccountIdOf<Runtime>,
|
|
>>::AssetId,
|
|
weight: Weight,
|
|
) -> Result<
|
|
<pallet_assets::Pallet<Runtime, AssetInstance> as Inspect<AccountIdOf<Runtime>>>::Balance,
|
|
XcmError,
|
|
> {
|
|
let amount = WeightToFee::weight_to_fee(&weight);
|
|
// If the amount gotten is not at least the ED, then make it be the ED of the asset
|
|
// This is to avoid burning assets and decreasing the supply
|
|
let asset_amount = BalanceConverter::to_asset_balance(amount, asset_id)
|
|
.map_err(|_| XcmError::TooExpensive)?;
|
|
Ok(asset_amount)
|
|
}
|
|
}
|
|
|
|
/// Accepts an asset if it is a native asset from a particular `MultiLocation`.
|
|
pub struct ConcreteNativeAssetFrom<Location>(PhantomData<Location>);
|
|
impl<Location: Get<MultiLocation>> ContainsPair<MultiAsset, MultiLocation>
|
|
for ConcreteNativeAssetFrom<Location>
|
|
{
|
|
fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
|
log::trace!(target: "xcm::filter_asset_location",
|
|
"ConcreteNativeAsset asset: {:?}, origin: {:?}, location: {:?}",
|
|
asset, origin, Location::get());
|
|
matches!(asset.id, Concrete(ref id) if id == origin && origin == &Location::get())
|
|
}
|
|
}
|
|
|
|
pub struct RelayOrOtherSystemParachains<
|
|
SystemParachainMatcher: Contains<MultiLocation>,
|
|
Runtime: parachain_info::Config,
|
|
> {
|
|
_runtime: PhantomData<(SystemParachainMatcher, Runtime)>,
|
|
}
|
|
impl<SystemParachainMatcher: Contains<MultiLocation>, Runtime: parachain_info::Config>
|
|
Contains<MultiLocation> for RelayOrOtherSystemParachains<SystemParachainMatcher, Runtime>
|
|
{
|
|
fn contains(l: &MultiLocation) -> bool {
|
|
let self_para_id: u32 = parachain_info::Pallet::<Runtime>::get().into();
|
|
if let MultiLocation { parents: 0, interior: X1(Parachain(para_id)) } = l {
|
|
if *para_id == self_para_id {
|
|
return false
|
|
}
|
|
}
|
|
matches!(l, MultiLocation { parents: 1, interior: Here }) ||
|
|
SystemParachainMatcher::contains(l)
|
|
}
|
|
}
|
|
|
|
/// Accepts an asset if it is a concrete asset from the system (Relay Chain or system parachain).
|
|
pub struct ConcreteAssetFromSystem<AssetLocation>(PhantomData<AssetLocation>);
|
|
impl<AssetLocation: Get<MultiLocation>> ContainsPair<MultiAsset, MultiLocation>
|
|
for ConcreteAssetFromSystem<AssetLocation>
|
|
{
|
|
fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool {
|
|
log::trace!(target: "xcm::contains", "ConcreteAssetFromSystem asset: {:?}, origin: {:?}", asset, origin);
|
|
let is_system = match origin {
|
|
// The Relay Chain
|
|
MultiLocation { parents: 1, interior: Here } => true,
|
|
// System parachain
|
|
MultiLocation { parents: 1, interior: X1(Parachain(id)) } =>
|
|
ParaId::from(*id).is_system(),
|
|
// Others
|
|
_ => false,
|
|
};
|
|
matches!(asset.id, Concrete(id) if id == AssetLocation::get()) && is_system
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use frame_support::parameter_types;
|
|
|
|
use super::{
|
|
ConcreteAssetFromSystem, ContainsPair, GeneralIndex, Here, MultiAsset, MultiLocation,
|
|
PalletInstance, Parachain, Parent,
|
|
};
|
|
|
|
parameter_types! {
|
|
pub const RelayLocation: MultiLocation = MultiLocation::parent();
|
|
}
|
|
|
|
#[test]
|
|
fn concrete_asset_from_relay_works() {
|
|
let expected_asset: MultiAsset = (Parent, 1000000).into();
|
|
let expected_origin: MultiLocation = (Parent, Here).into();
|
|
|
|
assert!(<ConcreteAssetFromSystem<RelayLocation>>::contains(
|
|
&expected_asset,
|
|
&expected_origin
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn concrete_asset_from_sibling_system_para_fails_for_wrong_asset() {
|
|
let unexpected_assets: Vec<MultiAsset> = vec![
|
|
(Here, 1000000).into(),
|
|
((PalletInstance(50), GeneralIndex(1)), 1000000).into(),
|
|
((Parent, Parachain(1000), PalletInstance(50), GeneralIndex(1)), 1000000).into(),
|
|
];
|
|
let expected_origin: MultiLocation = (Parent, Parachain(1000)).into();
|
|
|
|
unexpected_assets.iter().for_each(|asset| {
|
|
assert!(!<ConcreteAssetFromSystem<RelayLocation>>::contains(asset, &expected_origin));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn concrete_asset_from_sibling_system_para_works_for_correct_asset() {
|
|
// (para_id, expected_result)
|
|
let test_data = vec![
|
|
(0, true),
|
|
(1, true),
|
|
(1000, true),
|
|
(1999, true),
|
|
(2000, false), // Not a System Parachain
|
|
(2001, false), // Not a System Parachain
|
|
];
|
|
|
|
let expected_asset: MultiAsset = (Parent, 1000000).into();
|
|
|
|
for (para_id, expected_result) in test_data {
|
|
let origin: MultiLocation = (Parent, Parachain(para_id)).into();
|
|
assert_eq!(
|
|
expected_result,
|
|
<ConcreteAssetFromSystem<RelayLocation>>::contains(&expected_asset, &origin)
|
|
);
|
|
}
|
|
}
|
|
}
|