mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 16:21:02 +00:00
Introduce XcmFeesToAccount fee manager (#1234)
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>
This commit is contained in:
@@ -34,11 +34,11 @@ parachain-info = { path = "../../../pallets/parachain-info", default-features =
|
||||
parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false }
|
||||
|
||||
# Polkadot
|
||||
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}
|
||||
polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false}
|
||||
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 }
|
||||
polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false }
|
||||
|
||||
# Bridges
|
||||
pallet-xcm-bridge-hub-router = { path = "../../../../../bridges/modules/xcm-bridge-hub-router", default-features = false }
|
||||
|
||||
@@ -18,4 +18,5 @@
|
||||
|
||||
pub mod test_cases;
|
||||
pub mod test_cases_over_bridge;
|
||||
pub mod xcm_helpers;
|
||||
pub use parachains_runtimes_test_utils::*;
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
|
||||
//! Module contains predefined test-case scenarios for `Runtime` with various assets.
|
||||
|
||||
use super::xcm_helpers;
|
||||
use codec::Encode;
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok,
|
||||
traits::{fungibles::InspectEnumerable, Get, OnFinalize, OnInitialize, OriginTrait},
|
||||
traits::{
|
||||
fungible::Mutate, fungibles::InspectEnumerable, Get, OnFinalize, OnInitialize, OriginTrait,
|
||||
},
|
||||
weights::Weight,
|
||||
};
|
||||
use frame_system::pallet_prelude::BlockNumberFor;
|
||||
@@ -175,6 +178,21 @@ pub fn teleports_for_native_asset_works<
|
||||
target_account_balance_before_teleport - existential_deposit
|
||||
);
|
||||
|
||||
// Mint funds into account to ensure it has enough balance to pay delivery fees
|
||||
let delivery_fees =
|
||||
xcm_helpers::transfer_assets_delivery_fees::<XcmConfig::XcmSender>(
|
||||
(native_asset_id, native_asset_to_teleport_away.into()).into(),
|
||||
0,
|
||||
Unlimited,
|
||||
dest_beneficiary,
|
||||
dest,
|
||||
);
|
||||
<pallet_balances::Pallet<Runtime>>::mint_into(
|
||||
&target_account,
|
||||
delivery_fees.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
|
||||
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
|
||||
dest,
|
||||
@@ -184,6 +202,7 @@ pub fn teleports_for_native_asset_works<
|
||||
included_head.clone(),
|
||||
&alice,
|
||||
));
|
||||
|
||||
// check balances
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
|
||||
@@ -232,10 +251,21 @@ pub fn teleports_for_native_asset_works<
|
||||
&alice,
|
||||
));
|
||||
|
||||
let delivery_fees =
|
||||
xcm_helpers::transfer_assets_delivery_fees::<XcmConfig::XcmSender>(
|
||||
(native_asset_id, native_asset_to_teleport_away.into()).into(),
|
||||
0,
|
||||
Unlimited,
|
||||
dest_beneficiary,
|
||||
dest,
|
||||
);
|
||||
|
||||
// check balances
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
|
||||
target_account_balance_before_teleport - native_asset_to_teleport_away
|
||||
target_account_balance_before_teleport -
|
||||
native_asset_to_teleport_away -
|
||||
delivery_fees.into()
|
||||
);
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
|
||||
@@ -370,7 +400,7 @@ pub fn teleports_for_foreign_assets_works<
|
||||
fun: Fungible(buy_execution_fee_amount),
|
||||
};
|
||||
|
||||
let teleported_foreign_asset_amount = 10000000000000;
|
||||
let teleported_foreign_asset_amount = 10_000_000_000_000;
|
||||
let runtime_para_id = 1000;
|
||||
ExtBuilder::<Runtime>::default()
|
||||
.with_collators(collator_session_keys.collators())
|
||||
@@ -400,11 +430,11 @@ pub fn teleports_for_foreign_assets_works<
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
|
||||
existential_deposit
|
||||
);
|
||||
// check `CheckingAccount` before
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
|
||||
existential_deposit
|
||||
);
|
||||
// check `CheckingAccount` before
|
||||
assert_eq!(
|
||||
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
|
||||
foreign_asset_id_multilocation.into(),
|
||||
@@ -540,6 +570,21 @@ pub fn teleports_for_foreign_assets_works<
|
||||
.into()
|
||||
);
|
||||
|
||||
// Make sure the target account has enough native asset to pay for delivery fees
|
||||
let delivery_fees =
|
||||
xcm_helpers::transfer_assets_delivery_fees::<XcmConfig::XcmSender>(
|
||||
(foreign_asset_id_multilocation, asset_to_teleport_away).into(),
|
||||
0,
|
||||
Unlimited,
|
||||
dest_beneficiary,
|
||||
dest,
|
||||
);
|
||||
<pallet_balances::Pallet<Runtime>>::mint_into(
|
||||
&target_account,
|
||||
delivery_fees.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
|
||||
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
|
||||
dest,
|
||||
|
||||
@@ -20,7 +20,9 @@ use codec::Encode;
|
||||
use cumulus_primitives_core::XcmpMessageSource;
|
||||
use frame_support::{
|
||||
assert_ok,
|
||||
traits::{Currency, OnFinalize, OnInitialize, OriginTrait, ProcessMessageError},
|
||||
traits::{
|
||||
fungible::Mutate, Currency, OnFinalize, OnInitialize, OriginTrait, ProcessMessageError,
|
||||
},
|
||||
};
|
||||
use frame_system::pallet_prelude::BlockNumberFor;
|
||||
use parachains_common::{AccountId, Balance};
|
||||
@@ -164,6 +166,12 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
|
||||
}),
|
||||
};
|
||||
|
||||
// Make sure sender has enough funds for paying delivery fees
|
||||
// TODO: Get this fee via weighing the corresponding message
|
||||
let delivery_fees = 1324039894;
|
||||
<pallet_balances::Pallet<Runtime>>::mint_into(&alice_account, delivery_fees.into())
|
||||
.unwrap();
|
||||
|
||||
// do pallet_xcm call reserve transfer
|
||||
assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
|
||||
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright Parity Technologies (UK) Ltd.
|
||||
// This file is part of Cumulus.
|
||||
|
||||
// Cumulus 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.
|
||||
|
||||
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Helpers for calculating XCM delivery fees.
|
||||
|
||||
use xcm::latest::prelude::*;
|
||||
|
||||
/// Returns the delivery fees amount for pallet xcm's `teleport_assets` and
|
||||
/// `reserve_transfer_assets` extrinsics.
|
||||
/// Because it returns only a `u128`, it assumes delivery fees are only paid
|
||||
/// in one asset and that asset is known.
|
||||
pub fn transfer_assets_delivery_fees<S: SendXcm>(
|
||||
assets: MultiAssets,
|
||||
fee_asset_item: u32,
|
||||
weight_limit: WeightLimit,
|
||||
beneficiary: MultiLocation,
|
||||
destination: MultiLocation,
|
||||
) -> u128 {
|
||||
let message = teleport_assets_dummy_message(assets, fee_asset_item, weight_limit, beneficiary);
|
||||
get_fungible_delivery_fees::<S>(destination, message)
|
||||
}
|
||||
|
||||
/// Returns the delivery fees amount for a query response as a result of the execution
|
||||
/// of a `ExpectError` instruction with no error.
|
||||
pub fn query_response_delivery_fees<S: SendXcm>(querier: MultiLocation) -> u128 {
|
||||
// Message to calculate delivery fees, it's encoded size is what's important.
|
||||
// This message reports that there was no error, if an error is reported, the encoded size would
|
||||
// be different.
|
||||
let message = Xcm(vec![
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
QueryResponse {
|
||||
query_id: 0, // Dummy query id
|
||||
response: Response::ExecutionResult(None),
|
||||
max_weight: Weight::zero(),
|
||||
querier: Some(querier),
|
||||
},
|
||||
SetTopic([0u8; 32]), // Dummy topic
|
||||
]);
|
||||
get_fungible_delivery_fees::<S>(querier, message)
|
||||
}
|
||||
|
||||
/// Returns the delivery fees amount for the execution of `PayOverXcm`
|
||||
pub fn pay_over_xcm_delivery_fees<S: SendXcm>(
|
||||
interior: Junctions,
|
||||
destination: MultiLocation,
|
||||
beneficiary: MultiLocation,
|
||||
asset: MultiAsset,
|
||||
) -> u128 {
|
||||
// This is a dummy message.
|
||||
// The encoded size is all that matters for delivery fees.
|
||||
let message = Xcm(vec![
|
||||
DescendOrigin(interior),
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
SetAppendix(Xcm(vec![
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
ReportError(QueryResponseInfo { destination, query_id: 0, max_weight: Weight::zero() }),
|
||||
])),
|
||||
TransferAsset { beneficiary, assets: vec![asset].into() },
|
||||
]);
|
||||
get_fungible_delivery_fees::<S>(destination, message)
|
||||
}
|
||||
|
||||
/// Approximates the actual message sent by the teleport extrinsic.
|
||||
/// The assets are not reanchored and the topic is a dummy one.
|
||||
/// However, it should have the same encoded size, which is what matters for delivery fees.
|
||||
/// Also has same encoded size as the one created by the reserve transfer assets extrinsic.
|
||||
fn teleport_assets_dummy_message(
|
||||
assets: MultiAssets,
|
||||
fee_asset_item: u32,
|
||||
weight_limit: WeightLimit,
|
||||
beneficiary: MultiLocation,
|
||||
) -> Xcm<()> {
|
||||
Xcm(vec![
|
||||
ReceiveTeleportedAsset(assets.clone()), // Same encoded size as `ReserveAssetDeposited`
|
||||
ClearOrigin,
|
||||
BuyExecution { fees: assets.get(fee_asset_item as usize).unwrap().clone(), weight_limit },
|
||||
DepositAsset { assets: Wild(AllCounted(assets.len() as u32)), beneficiary },
|
||||
SetTopic([0u8; 32]), // Dummy topic
|
||||
])
|
||||
}
|
||||
|
||||
/// Given a message, a sender, and a destination, it returns the delivery fees
|
||||
fn get_fungible_delivery_fees<S: SendXcm>(destination: MultiLocation, message: Xcm<()>) -> u128 {
|
||||
let Ok((_, delivery_fees)) = validate_send::<S>(destination, message) else {
|
||||
unreachable!("message can be sent; qed")
|
||||
};
|
||||
if let Some(delivery_fee) = delivery_fees.inner().first() {
|
||||
let Fungible(delivery_fee_amount) = delivery_fee.fun else {
|
||||
unreachable!("asset is fungible; qed");
|
||||
};
|
||||
delivery_fee_amount
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user