pallet_transaction_payment_rpc/
lib.rs1use std::sync::Arc;
21
22use codec::{Codec, Decode};
23use jsonrpsee::{
24 core::RpcResult,
25 proc_macros::rpc,
26 types::{
27 error::{ErrorCode, ErrorObject},
28 ErrorObjectOwned,
29 },
30};
31use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, InclusionFee, RuntimeDispatchInfo};
32use sp_api::ProvideRuntimeApi;
33use sp_blockchain::HeaderBackend;
34use sp_core::Bytes;
35use sp_rpc::number::NumberOrHex;
36use sp_runtime::traits::{Block as BlockT, MaybeDisplay};
37
38pub use pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi as TransactionPaymentRuntimeApi;
39
40#[rpc(client, server)]
41pub trait TransactionPaymentApi<BlockHash, ResponseType> {
42 #[method(name = "payment_queryInfo")]
43 fn query_info(&self, encoded_xt: Bytes, at: Option<BlockHash>) -> RpcResult<ResponseType>;
44
45 #[method(name = "payment_queryFeeDetails")]
46 fn query_fee_details(
47 &self,
48 encoded_xt: Bytes,
49 at: Option<BlockHash>,
50 ) -> RpcResult<FeeDetails<NumberOrHex>>;
51}
52
53pub struct TransactionPayment<C, P> {
55 client: Arc<C>,
57 _marker: std::marker::PhantomData<P>,
58}
59
60impl<C, P> TransactionPayment<C, P> {
61 pub fn new(client: Arc<C>) -> Self {
63 Self { client, _marker: Default::default() }
64 }
65}
66
67pub enum Error {
69 DecodeError,
71 RuntimeError,
73}
74
75impl From<Error> for i32 {
76 fn from(e: Error) -> i32 {
77 match e {
78 Error::RuntimeError => 1,
79 Error::DecodeError => 2,
80 }
81 }
82}
83
84impl<C, Block, Balance>
85 TransactionPaymentApiServer<
86 <Block as BlockT>::Hash,
87 RuntimeDispatchInfo<Balance, sp_weights::Weight>,
88 > for TransactionPayment<C, Block>
89where
90 Block: BlockT,
91 C: ProvideRuntimeApi<Block> + HeaderBackend<Block> + Send + Sync + 'static,
92 C::Api: TransactionPaymentRuntimeApi<Block, Balance>,
93 Balance: Codec + MaybeDisplay + Copy + TryInto<NumberOrHex> + Send + Sync + 'static,
94{
95 fn query_info(
96 &self,
97 encoded_xt: Bytes,
98 at: Option<Block::Hash>,
99 ) -> RpcResult<RuntimeDispatchInfo<Balance, sp_weights::Weight>> {
100 let api = self.client.runtime_api();
101 let at_hash = at.unwrap_or_else(|| self.client.info().best_hash);
102
103 let encoded_len = encoded_xt.len() as u32;
104
105 let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| {
106 ErrorObject::owned(
107 Error::DecodeError.into(),
108 "Unable to query dispatch info.",
109 Some(format!("{:?}", e)),
110 )
111 })?;
112
113 fn map_err(error: impl ToString, desc: &'static str) -> ErrorObjectOwned {
114 ErrorObject::owned(Error::RuntimeError.into(), desc, Some(error.to_string()))
115 }
116
117 let res = api
118 .query_info(at_hash, uxt, encoded_len)
119 .map_err(|e| map_err(e, "Unable to query dispatch info."))?;
120
121 Ok(RuntimeDispatchInfo {
122 weight: res.weight,
123 class: res.class,
124 partial_fee: res.partial_fee,
125 })
126 }
127
128 fn query_fee_details(
129 &self,
130 encoded_xt: Bytes,
131 at: Option<Block::Hash>,
132 ) -> RpcResult<FeeDetails<NumberOrHex>> {
133 let api = self.client.runtime_api();
134 let at_hash = at.unwrap_or_else(|| self.client.info().best_hash);
135
136 let encoded_len = encoded_xt.len() as u32;
137
138 let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| {
139 ErrorObject::owned(
140 Error::DecodeError.into(),
141 "Unable to query fee details.",
142 Some(format!("{:?}", e)),
143 )
144 })?;
145 let fee_details = api.query_fee_details(at_hash, uxt, encoded_len).map_err(|e| {
146 ErrorObject::owned(
147 Error::RuntimeError.into(),
148 "Unable to query fee details.",
149 Some(e.to_string()),
150 )
151 })?;
152
153 let try_into_rpc_balance = |value: Balance| {
154 value.try_into().map_err(|_| {
155 ErrorObject::owned(
156 ErrorCode::InvalidParams.code(),
157 format!("{} doesn't fit in NumberOrHex representation", value),
158 None::<()>,
159 )
160 })
161 };
162
163 Ok(FeeDetails {
164 inclusion_fee: if let Some(inclusion_fee) = fee_details.inclusion_fee {
165 Some(InclusionFee {
166 base_fee: try_into_rpc_balance(inclusion_fee.base_fee)?,
167 len_fee: try_into_rpc_balance(inclusion_fee.len_fee)?,
168 adjusted_weight_fee: try_into_rpc_balance(inclusion_fee.adjusted_weight_fee)?,
169 })
170 } else {
171 None
172 },
173 tip: Default::default(),
174 })
175 }
176}