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::traits::{FeeManager, FeeReason, TransactAsset};
21
22/// Handles the fees that are taken by certain XCM instructions.
23pub trait HandleFee {
24	/// Do something with the fee which has been paid. Doing nothing here silently burns the
25	/// fees.
26	///
27	/// Returns any part of the fee that wasn't consumed.
28	fn handle_fee(fee: Assets, context: Option<&XcmContext>, reason: FeeReason) -> Assets;
29}
30
31// Default `HandleFee` implementation that just burns the fee.
32impl HandleFee for () {
33	fn handle_fee(_: Assets, _: Option<&XcmContext>, _: FeeReason) -> Assets {
34		Assets::new()
35	}
36}
37
38#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
39impl HandleFee for Tuple {
40	fn handle_fee(fee: Assets, context: Option<&XcmContext>, reason: FeeReason) -> Assets {
41		let mut unconsumed_fee = fee;
42		for_tuples!( #(
43			unconsumed_fee = Tuple::handle_fee(unconsumed_fee, context, reason.clone());
44			if unconsumed_fee.is_none() {
45				return unconsumed_fee;
46			}
47		)* );
48
49		unconsumed_fee
50	}
51}
52
53/// A `FeeManager` implementation that permits the specified `WaivedLocations` to not pay for fees
54/// and that uses the provided `HandleFee` implementation otherwise.
55pub struct XcmFeeManagerFromComponents<WaivedLocations, HandleFee>(
56	PhantomData<(WaivedLocations, HandleFee)>,
57);
58impl<WaivedLocations: Contains<Location>, FeeHandler: HandleFee> FeeManager
59	for XcmFeeManagerFromComponents<WaivedLocations, FeeHandler>
60{
61	fn is_waived(origin: Option<&Location>, _: FeeReason) -> bool {
62		let Some(loc) = origin else { return false };
63		WaivedLocations::contains(loc)
64	}
65
66	fn handle_fee(fee: Assets, context: Option<&XcmContext>, reason: FeeReason) {
67		FeeHandler::handle_fee(fee, context, reason);
68	}
69}
70
71/// A `HandleFee` implementation that simply deposits the fees into a specific on-chain
72/// `ReceiverAccount`.
73///
74/// It reuses the `AssetTransactor` configured on the XCM executor to deposit fee assets. If
75/// the `AssetTransactor` returns an error while calling `deposit_asset`, then a warning will be
76/// logged and the fee burned.
77#[deprecated(
78	note = "`XcmFeeToAccount` will be removed in January 2025. Use `SendXcmFeeToAccount` instead."
79)]
80pub struct XcmFeeToAccount<AssetTransactor, AccountId, ReceiverAccount>(
81	PhantomData<(AssetTransactor, AccountId, ReceiverAccount)>,
82);
83
84#[allow(deprecated)]
85impl<
86		AssetTransactor: TransactAsset,
87		AccountId: Clone + Into<[u8; 32]>,
88		ReceiverAccount: Get<AccountId>,
89	> HandleFee for XcmFeeToAccount<AssetTransactor, AccountId, ReceiverAccount>
90{
91	fn handle_fee(fee: Assets, context: Option<&XcmContext>, _reason: FeeReason) -> Assets {
92		let dest = AccountId32 { network: None, id: ReceiverAccount::get().into() }.into();
93		deposit_or_burn_fee::<AssetTransactor>(fee, context, dest);
94
95		Assets::new()
96	}
97}
98
99/// A `HandleFee` implementation that simply deposits the fees into a specific on-chain
100/// `ReceiverAccount`.
101///
102/// It reuses the `AssetTransactor` configured on the XCM executor to deposit fee assets. If
103/// the `AssetTransactor` returns an error while calling `deposit_asset`, then a warning will be
104/// logged and the fee burned.
105///
106/// `ReceiverAccount` should implement `Get<Location>`.
107pub struct SendXcmFeeToAccount<AssetTransactor, ReceiverAccount>(
108	PhantomData<(AssetTransactor, ReceiverAccount)>,
109);
110
111impl<AssetTransactor: TransactAsset, ReceiverAccount: Get<Location>> HandleFee
112	for SendXcmFeeToAccount<AssetTransactor, ReceiverAccount>
113{
114	fn handle_fee(fee: Assets, context: Option<&XcmContext>, _reason: FeeReason) -> Assets {
115		deposit_or_burn_fee::<AssetTransactor>(fee, context, ReceiverAccount::get());
116
117		Assets::new()
118	}
119}
120
121/// Try to deposit the given fee in the specified account.
122/// Burns the fee in case of a failure.
123pub fn deposit_or_burn_fee<AssetTransactor: TransactAsset>(
124	fee: Assets,
125	context: Option<&XcmContext>,
126	dest: Location,
127) {
128	for asset in fee.into_inner() {
129		if let Err(e) = AssetTransactor::deposit_asset(&asset, &dest, context) {
130			tracing::trace!(
131				target: "xcm::fees",
132				"`AssetTransactor::deposit_asset` returned error: {e:?}. Burning fee: {asset:?}. \
133				They might be burned.",
134			);
135		}
136	}
137}