From 18e54e7a7315653136ffaf91752f51d14173cf69 Mon Sep 17 00:00:00 2001
From: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Date: Mon, 18 Mar 2024 15:01:37 +0100
Subject: [PATCH] feat(xcm-fee-payment-api): add query_delivery_fees function

---
 polkadot/node/service/src/fake_runtime_api.rs    |  7 +++++--
 polkadot/runtime/rococo/src/lib.rs               | 16 +++++++++++++++-
 polkadot/runtime/westend/src/lib.rs              | 14 ++++++++++++++
 .../xcm-payment-runtime-api/Cargo.toml           |  1 +
 .../xcm-payment-runtime-api/src/lib.rs           | 14 +++++++++++++-
 5 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs
index 34111d007beb8..15c245829b099 100644
--- a/polkadot/node/service/src/fake_runtime_api.rs
+++ b/polkadot/node/service/src/fake_runtime_api.rs
@@ -41,7 +41,7 @@ use sp_runtime::{
 use sp_version::RuntimeVersion;
 use sp_weights::Weight;
 use std::collections::BTreeMap;
-use xcm::{VersionedAssetId, VersionedXcm};
+use xcm::{VersionedAssetId, VersionedXcm, VersionedLocation, VersionedAssets};
 sp_api::decl_runtime_apis! {
 	/// This runtime API is only implemented for the test runtime!
 	pub trait GetLastTimestamp {
@@ -400,7 +400,6 @@ sp_api::impl_runtime_apis! {
 	}
 
 	impl xcm_payment_runtime_api::XcmPaymentApi<Block, RuntimeCall> for Runtime {
-
 		fn query_acceptable_payment_assets(_: xcm::Version) -> Result<Vec<VersionedAssetId>, xcm_payment_runtime_api::Error> {
 			unimplemented!()
 		}
@@ -412,5 +411,9 @@ sp_api::impl_runtime_apis! {
 		fn query_xcm_weight(_: VersionedXcm<RuntimeCall>) -> Result<Weight, xcm_payment_runtime_api::Error> {
 			unimplemented!()
 		}
+
+		fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result<VersionedAssets, xcm_payment_runtime_api::Error> {
+			unimplemented!()
+		}
 	}
 }
diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index bd4b8cd2dd769..61d00cfdd0134 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -98,7 +98,7 @@ use sp_staking::SessionIndex;
 #[cfg(any(feature = "std", test))]
 use sp_version::NativeVersion;
 use sp_version::RuntimeVersion;
-use xcm::{latest::prelude::*, IntoVersion, VersionedAssetId, VersionedLocation, VersionedXcm};
+use xcm::{latest::prelude::*, IntoVersion, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm};
 use xcm_builder::PayOverXcm;
 
 pub use frame_system::Call as SystemCall;
@@ -1830,6 +1830,20 @@ sp_api::impl_runtime_apis! {
 			<xcm_config::XcmConfig as xcm_executor::Config>::Weigher::weight(&mut message)
 				.map_err(|_| XcmPaymentApiError::WeightNotComputable)
 		}
+
+		fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, XcmPaymentApiError> {
+			let destination = destination
+				.try_into()
+				.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
+			let message = message
+				.try_into()
+				.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
+			let (_, fees) = xcm_config::XcmRouter::validate(&mut Some(destination), &mut Some(message)).map_err(|error| {
+				log::error!("Error when querying delivery fees: {:?}", error);
+				XcmPaymentApiError::Unroutable
+			})?;
+			Ok(VersionedAssets::from(fees))
+		}
 	}
 
 	impl sp_api::Metadata<Block> for Runtime {
diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs
index 41d0460f3cc7d..5a4c4e7fdc39a 100644
--- a/polkadot/runtime/westend/src/lib.rs
+++ b/polkadot/runtime/westend/src/lib.rs
@@ -2287,6 +2287,20 @@ sp_api::impl_runtime_apis! {
 			<xcm_config::XcmConfig as xcm_executor::Config>::Weigher::weight(&mut message)
 				.map_err(|_| XcmPaymentApiError::WeightNotComputable)
 		}
+
+		fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, XcmPaymentApiError> {
+			let destination = destination
+				.try_into()
+				.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
+			let message = message
+				.try_into()
+				.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
+			let (_, fees) = xcm_config::XcmRouter::validate(&mut Some(destination), &mut Some(message)).map_err(|error| {
+				log::error!("Error when querying delivery fees: {:?}", error);
+				XcmPaymentApiError::Unroutable
+			})?;
+			Ok(VersionedAssets::from(fees))
+		}
 	}
 
 	impl pallet_nomination_pools_runtime_api::NominationPoolsApi<
diff --git a/polkadot/xcm/xcm-builder/xcm-payment-runtime-api/Cargo.toml b/polkadot/xcm/xcm-builder/xcm-payment-runtime-api/Cargo.toml
index 38c95606ee393..c551e7c2edc26 100644
--- a/polkadot/xcm/xcm-builder/xcm-payment-runtime-api/Cargo.toml
+++ b/polkadot/xcm/xcm-builder/xcm-payment-runtime-api/Cargo.toml
@@ -26,6 +26,7 @@ sp-runtime = { path = "../../../../substrate/primitives/runtime", default-featur
 sp-weights = { path = "../../../../substrate/primitives/weights", default-features = false }
 xcm = { package = "staging-xcm", path = "../..", default-features = false }
 frame-support = { path = "../../../../substrate/frame/support", default-features = false }
+
 [features]
 default = ["std"]
 std = [
diff --git a/polkadot/xcm/xcm-builder/xcm-payment-runtime-api/src/lib.rs b/polkadot/xcm/xcm-builder/xcm-payment-runtime-api/src/lib.rs
index ef55dda51a7f6..e5af1d7048be2 100644
--- a/polkadot/xcm/xcm-builder/xcm-payment-runtime-api/src/lib.rs
+++ b/polkadot/xcm/xcm-builder/xcm-payment-runtime-api/src/lib.rs
@@ -22,7 +22,7 @@ use codec::{Codec, Decode, Encode};
 use frame_support::pallet_prelude::TypeInfo;
 use sp_std::vec::Vec;
 use sp_weights::Weight;
-use xcm::{Version, VersionedAssetId, VersionedXcm};
+use xcm::{Version, VersionedAssetId, VersionedXcm, VersionedLocation, VersionedAssets};
 
 sp_api::decl_runtime_apis! {
 	/// A trait of XCM payment API.
@@ -60,6 +60,16 @@ sp_api::decl_runtime_apis! {
 		/// * `weight`: convertible `Weight`.
 		/// * `asset`: `VersionedAssetId`.
 		fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, Error>;
+
+		/// Get delivery fees for sending a specific `message` to a `destination`.
+		/// These always come in a specific asset, defined by the chain.
+		///
+		/// # Arguments
+		/// * `message`: The message that'll be sent, necessary because most delivery fees are based on the
+		///   size of the message.
+		/// * `destination`: The destination to send the message to. Different destinations may use
+		///   different senders that charge different fees.
+		fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, Error>;
 	}
 }
 
@@ -79,4 +89,6 @@ pub enum Error {
 	/// The given asset is not handled(as a fee payment).
 	#[codec(index = 4)]
 	AssetNotFound,
+	#[codec(index = 5)]
+	Unroutable,
 }