referrerpolicy=no-referrer-when-downgrade

staging_xcm_builder/
fee_handling.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17use core::marker::PhantomData;
18use frame_support::traits::{Contains, Get};
19use xcm::prelude::*;
20use xcm_executor::{
21	traits::{FeeManager, FeeReason, TransactAsset},
22	AssetsInHolding,
23};
24
25/// Handles the fees that are taken by certain XCM instructions.
26pub trait HandleFee {
27	/// Do something with the fee which has been paid. Doing nothing here silently burns the
28	/// fees.
29	///
30	/// Returns any part of the fee that wasn't consumed.
31	fn handle_fee(
32		fee: AssetsInHolding,
33		context: Option<&XcmContext>,
34		reason: FeeReason,
35	) -> AssetsInHolding;
36}
37
38// Default `HandleFee` implementation that just burns the fee.
39impl HandleFee for () {
40	fn handle_fee(_: AssetsInHolding, _: Option<&XcmContext>, _: FeeReason) -> AssetsInHolding {
41		AssetsInHolding::new()
42	}
43}
44
45#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
46impl HandleFee for Tuple {
47	fn handle_fee(
48		fee: AssetsInHolding,
49		context: Option<&XcmContext>,
50		reason: FeeReason,
51	) -> AssetsInHolding {
52		let mut unconsumed_fee = fee;
53		for_tuples!( #(
54			unconsumed_fee = Tuple::handle_fee(unconsumed_fee, context, reason.clone());
55			if unconsumed_fee.is_empty() {
56				return unconsumed_fee;
57			}
58		)* );
59
60		unconsumed_fee
61	}
62}
63
64/// A `FeeManager` implementation that permits the specified `WaivedLocations` to not pay for fees
65/// and that uses the provided `HandleFee` implementation otherwise.
66pub struct XcmFeeManagerFromComponents<WaivedLocations, HandleFee>(
67	PhantomData<(WaivedLocations, HandleFee)>,
68);
69impl<WaivedLocations: Contains<Location>, FeeHandler: HandleFee> FeeManager
70	for XcmFeeManagerFromComponents<WaivedLocations, FeeHandler>
71{
72	fn is_waived(origin: Option<&Location>, _: FeeReason) -> bool {
73		let Some(loc) = origin else { return false };
74		WaivedLocations::contains(loc)
75	}
76
77	fn handle_fee(fee: AssetsInHolding, context: Option<&XcmContext>, reason: FeeReason) {
78		FeeHandler::handle_fee(fee, context, reason);
79	}
80}
81
82/// A `HandleFee` implementation that simply deposits the fees into a specific on-chain
83/// `ReceiverAccount`.
84///
85/// It reuses the `AssetTransactor` configured on the XCM executor to deposit fee assets. If
86/// the `AssetTransactor` returns an error while calling `deposit_asset`, then a warning will be
87/// logged and the fee burned.
88///
89/// `ReceiverAccount` should implement `Get<Location>`.
90pub struct SendXcmFeeToAccount<AssetTransactor, ReceiverAccount>(
91	PhantomData<(AssetTransactor, ReceiverAccount)>,
92);
93
94impl<AssetTransactor: TransactAsset, ReceiverAccount: Get<Location>> HandleFee
95	for SendXcmFeeToAccount<AssetTransactor, ReceiverAccount>
96{
97	fn handle_fee(
98		fee: AssetsInHolding,
99		context: Option<&XcmContext>,
100		_reason: FeeReason,
101	) -> AssetsInHolding {
102		deposit_or_burn_fee::<AssetTransactor>(fee, context, ReceiverAccount::get());
103		AssetsInHolding::new()
104	}
105}
106
107/// Try to deposit the given fee in the specified account.
108/// Burns the fee in case of a failure.
109pub fn deposit_or_burn_fee<AssetTransactor: TransactAsset>(
110	fee: AssetsInHolding,
111	context: Option<&XcmContext>,
112	dest: Location,
113) {
114	// If `fee` contains multiple assets, we need to process one fungible asset at a time.
115	// Non-fungibles are ignored.
116	for (asset_id, credit) in fee.fungible.into_iter() {
117		let fee_asset = AssetsInHolding::new_from_fungible_credit(asset_id, credit);
118		if let Err((unspent, e)) = AssetTransactor::deposit_asset(fee_asset, &dest, context) {
119			tracing::trace!(
120				target: "xcm::fees",
121				"`AssetTransactor::deposit_asset` returned error: {e:?}. \
122				Dropping fee: {unspent:?} (might be burned).",
123			);
124		}
125	}
126}