asset_test_utils/
xcm_helpers.rs

1// Copyright Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3
4// Cumulus 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// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Helpers for calculating XCM delivery fees.
18
19use xcm::latest::prelude::*;
20
21/// Returns the delivery fees amount for pallet xcm's `teleport_assets` extrinsics.
22/// Because it returns only a `u128`, it assumes delivery fees are only paid
23/// in one asset and that asset is known.
24pub fn teleport_assets_delivery_fees<S: SendXcm>(
25	assets: Assets,
26	fee_asset_item: u32,
27	weight_limit: WeightLimit,
28	beneficiary: Location,
29	destination: Location,
30) -> u128 {
31	let message = teleport_assets_dummy_message(assets, fee_asset_item, weight_limit, beneficiary);
32	get_fungible_delivery_fees::<S>(destination, message)
33}
34
35/// Returns the delivery fees amount for a query response as a result of the execution
36/// of a `ExpectError` instruction with no error.
37pub fn query_response_delivery_fees<S: SendXcm>(querier: Location) -> u128 {
38	// Message to calculate delivery fees, it's encoded size is what's important.
39	// This message reports that there was no error, if an error is reported, the encoded size would
40	// be different.
41	let message = Xcm(vec![
42		SetFeesMode { jit_withdraw: true },
43		QueryResponse {
44			query_id: 0, // Dummy query id
45			response: Response::ExecutionResult(None),
46			max_weight: Weight::zero(),
47			querier: Some(querier.clone()),
48		},
49		SetTopic([0u8; 32]), // Dummy topic
50	]);
51	get_fungible_delivery_fees::<S>(querier, message)
52}
53
54/// Returns the delivery fees amount for the execution of `PayOverXcm`
55pub fn pay_over_xcm_delivery_fees<S: SendXcm>(
56	interior: Junctions,
57	destination: Location,
58	beneficiary: Location,
59	asset: Asset,
60) -> u128 {
61	// This is a dummy message.
62	// The encoded size is all that matters for delivery fees.
63	let message = Xcm(vec![
64		DescendOrigin(interior),
65		UnpaidExecution { weight_limit: Unlimited, check_origin: None },
66		SetAppendix(Xcm(vec![
67			SetFeesMode { jit_withdraw: true },
68			ReportError(QueryResponseInfo {
69				destination: destination.clone(),
70				query_id: 0,
71				max_weight: Weight::zero(),
72			}),
73		])),
74		TransferAsset { beneficiary, assets: vec![asset].into() },
75	]);
76	get_fungible_delivery_fees::<S>(destination, message)
77}
78
79/// Approximates the actual message sent by the teleport extrinsic.
80/// The assets are not reanchored and the topic is a dummy one.
81/// However, it should have the same encoded size, which is what matters for delivery fees.
82/// Also has same encoded size as the one created by the reserve transfer assets extrinsic.
83fn teleport_assets_dummy_message(
84	assets: Assets,
85	fee_asset_item: u32,
86	weight_limit: WeightLimit,
87	beneficiary: Location,
88) -> Xcm<()> {
89	Xcm(vec![
90		ReceiveTeleportedAsset(assets.clone()), // Same encoded size as `ReserveAssetDeposited`
91		ClearOrigin,
92		BuyExecution { fees: assets.get(fee_asset_item as usize).unwrap().clone(), weight_limit },
93		DepositAsset { assets: Wild(AllCounted(assets.len() as u32)), beneficiary },
94		SetTopic([0u8; 32]), // Dummy topic
95	])
96}
97
98/// Given a message, a sender, and a destination, it returns the delivery fees
99fn get_fungible_delivery_fees<S: SendXcm>(destination: Location, message: Xcm<()>) -> u128 {
100	let Ok((_, delivery_fees)) = validate_send::<S>(destination, message) else {
101		unreachable!("message can be sent; qed")
102	};
103	if let Some(delivery_fee) = delivery_fees.inner().first() {
104		let Fungible(delivery_fee_amount) = delivery_fee.fun else {
105			unreachable!("asset is fungible; qed");
106		};
107		delivery_fee_amount
108	} else {
109		0
110	}
111}