From e41b90910e31a8a39e0b89914f2f465959233c12 Mon Sep 17 00:00:00 2001
From: Muharem Ismailov <ismailov.m.h@gmail.com>
Date: Wed, 10 Aug 2022 12:36:16 +0200
Subject: [PATCH] Transaction payment runtime api: query call info and fee
 details (#11819)

* Transaction payment RPC calls: query call info

* transaction payment pallet - runtime api - add query_call info and fee_details

* remove unused deps

* separate call runtime api

* undo fmt for unchanged code

* system config call bounded to GetDispatchInfo, drop Call generic for query call info/fee

* impl GetDispatchInfo for Extrinsics within runtime test-utils

* introduced runtime api methods accept encoded Call instead of Call type

* replace Bytes by Vec, docs for for new api, drop len argument, drop GetDispatchInfo bound from system_Config::Call

* clean up toml and extra impl for dropped bound

* panic if Call can not be decoded

* revert to d43ba2f

* fmt and docs

* rustfmt
---
 .../bin/node-template/runtime/src/lib.rs      | 17 +++++
 substrate/bin/node/runtime/src/lib.rs         | 11 ++++
 .../rpc/runtime-api/src/lib.rs                | 12 ++++
 .../frame/transaction-payment/src/lib.rs      | 63 +++++++++++++++++++
 4 files changed, 103 insertions(+)

diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/substrate/bin/node-template/runtime/src/lib.rs
index 88fc86db02e..b43fbde52dc 100644
--- a/substrate/bin/node-template/runtime/src/lib.rs
+++ b/substrate/bin/node-template/runtime/src/lib.rs
@@ -467,6 +467,23 @@ impl_runtime_apis! {
 		}
 	}
 
+	impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, Call>
+		for Runtime
+	{
+		fn query_call_info(
+			call: Call,
+			len: u32,
+		) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> {
+			TransactionPayment::query_call_info(call, len)
+		}
+		fn query_call_fee_details(
+			call: Call,
+			len: u32,
+		) -> pallet_transaction_payment::FeeDetails<Balance> {
+			TransactionPayment::query_call_fee_details(call, len)
+		}
+	}
+
 	#[cfg(feature = "runtime-benchmarks")]
 	impl frame_benchmarking::Benchmark<Block> for Runtime {
 		fn benchmark_metadata(extra: bool) -> (
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 2ac1e6444f1..6bfbe341305 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -1965,6 +1965,17 @@ impl_runtime_apis! {
 		}
 	}
 
+	impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, Call>
+		for Runtime
+	{
+		fn query_call_info(call: Call, len: u32) -> RuntimeDispatchInfo<Balance> {
+			TransactionPayment::query_call_info(call, len)
+		}
+		fn query_call_fee_details(call: Call, len: u32) -> FeeDetails<Balance> {
+			TransactionPayment::query_call_fee_details(call, len)
+		}
+	}
+
 	impl pallet_mmr::primitives::MmrApi<
 		Block,
 		mmr::Hash,
diff --git a/substrate/frame/transaction-payment/rpc/runtime-api/src/lib.rs b/substrate/frame/transaction-payment/rpc/runtime-api/src/lib.rs
index 5a0c70138db..6944593daa5 100644
--- a/substrate/frame/transaction-payment/rpc/runtime-api/src/lib.rs
+++ b/substrate/frame/transaction-payment/rpc/runtime-api/src/lib.rs
@@ -31,4 +31,16 @@ sp_api::decl_runtime_apis! {
 		fn query_info(uxt: Block::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance>;
 		fn query_fee_details(uxt: Block::Extrinsic, len: u32) -> FeeDetails<Balance>;
 	}
+
+	pub trait TransactionPaymentCallApi<Balance, Call>
+	where
+		Balance: Codec + MaybeDisplay,
+		Call: Codec,
+	{
+		/// Query information of a dispatch class, weight, and fee of a given encoded `Call`.
+		fn query_call_info(call: Call, len: u32) -> RuntimeDispatchInfo<Balance>;
+
+		/// Query fee details of a given encoded `Call`.
+		fn query_call_fee_details(call: Call, len: u32) -> FeeDetails<Balance>;
+	}
 }
diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs
index fe37acb2144..ff65c0d2735 100644
--- a/substrate/frame/transaction-payment/src/lib.rs
+++ b/substrate/frame/transaction-payment/src/lib.rs
@@ -445,6 +445,32 @@ where
 		}
 	}
 
+	/// Query information of a dispatch class, weight, and fee of a given encoded `Call`.
+	pub fn query_call_info(call: T::Call, len: u32) -> RuntimeDispatchInfo<BalanceOf<T>>
+	where
+		T::Call: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
+	{
+		let dispatch_info = <T::Call as GetDispatchInfo>::get_dispatch_info(&call);
+		let DispatchInfo { weight, class, .. } = dispatch_info;
+
+		RuntimeDispatchInfo {
+			weight,
+			class,
+			partial_fee: Self::compute_fee(len, &dispatch_info, 0u32.into()),
+		}
+	}
+
+	/// Query fee details of a given encoded `Call`.
+	pub fn query_call_fee_details(call: T::Call, len: u32) -> FeeDetails<BalanceOf<T>>
+	where
+		T::Call: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
+	{
+		let dispatch_info = <T::Call as GetDispatchInfo>::get_dispatch_info(&call);
+		let tip = 0u32.into();
+
+		Self::compute_fee_details(len, &dispatch_info, tip)
+	}
+
 	/// Compute the final fee value for a particular transaction.
 	pub fn compute_fee(len: u32, info: &DispatchInfoOf<T::Call>, tip: BalanceOf<T>) -> BalanceOf<T>
 	where
@@ -1206,6 +1232,43 @@ mod tests {
 		});
 	}
 
+	#[test]
+	fn query_call_info_and_fee_details_works() {
+		let call = Call::Balances(BalancesCall::transfer { dest: 2, value: 69 });
+		let info = call.get_dispatch_info();
+		let encoded_call = call.encode();
+		let len = encoded_call.len() as u32;
+
+		ExtBuilder::default().base_weight(5).weight_fee(2).build().execute_with(|| {
+			// all fees should be x1.5
+			<NextFeeMultiplier<Runtime>>::put(Multiplier::saturating_from_rational(3, 2));
+
+			assert_eq!(
+				TransactionPayment::query_call_info(call.clone(), len),
+				RuntimeDispatchInfo {
+					weight: info.weight,
+					class: info.class,
+					partial_fee: 5 * 2 /* base * weight_fee */
+						+ len as u64  /* len * 1 */
+						+ info.weight.min(BlockWeights::get().max_block) as u64 * 2 * 3 / 2 /* weight */
+				},
+			);
+
+			assert_eq!(
+				TransactionPayment::query_call_fee_details(call, len),
+				FeeDetails {
+					inclusion_fee: Some(InclusionFee {
+						base_fee: 5 * 2,     /* base * weight_fee */
+						len_fee: len as u64, /* len * 1 */
+						adjusted_weight_fee: info.weight.min(BlockWeights::get().max_block) as u64 *
+							2 * 3 / 2  /* weight * weight_fee * multipler */
+					}),
+					tip: 0,
+				},
+			);
+		});
+	}
+
 	#[test]
 	fn compute_fee_works_without_multiplier() {
 		ExtBuilder::default()
-- 
GitLab