// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! RPC interface for the transaction payment pallet. use std::{convert::TryInto, sync::Arc}; use codec::{Codec, Decode}; use jsonrpsee::{ core::{Error as JsonRpseeError, RpcResult}, proc_macros::rpc, types::error::{CallError, ErrorCode, ErrorObject}, }; use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, InclusionFee, RuntimeDispatchInfo}; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::Bytes; use sp_rpc::number::NumberOrHex; use sp_runtime::traits::{Block as BlockT, MaybeDisplay}; pub use pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi as TransactionPaymentRuntimeApi; #[rpc(client, server)] pub trait TransactionPaymentApi { #[method(name = "payment_queryInfo")] fn query_info(&self, encoded_xt: Bytes, at: Option) -> RpcResult; #[method(name = "payment_queryFeeDetails")] fn query_fee_details( &self, encoded_xt: Bytes, at: Option, ) -> RpcResult>; } /// Provides RPC methods to query a dispatchable's class, weight and fee. pub struct TransactionPayment { /// Shared reference to the client. client: Arc, _marker: std::marker::PhantomData

, } impl TransactionPayment { /// Creates a new instance of the TransactionPayment Rpc helper. pub fn new(client: Arc) -> Self { Self { client, _marker: Default::default() } } } /// Error type of this RPC api. pub enum Error { /// The transaction was not decodable. DecodeError, /// The call to runtime failed. RuntimeError, } impl From for i32 { fn from(e: Error) -> i32 { match e { Error::RuntimeError => 1, Error::DecodeError => 2, } } } impl TransactionPaymentApiServer< ::Hash, RuntimeDispatchInfo, > for TransactionPayment where Block: BlockT, C: ProvideRuntimeApi + HeaderBackend + Send + Sync + 'static, C::Api: TransactionPaymentRuntimeApi, Balance: Codec + MaybeDisplay + Copy + TryInto + Send + Sync + 'static, { fn query_info( &self, encoded_xt: Bytes, at: Option, ) -> RpcResult> { let api = self.client.runtime_api(); let at_hash = at.unwrap_or_else(|| self.client.info().best_hash); let encoded_len = encoded_xt.len() as u32; let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| { CallError::Custom(ErrorObject::owned( Error::DecodeError.into(), "Unable to query dispatch info.", Some(format!("{:?}", e)), )) })?; fn map_err(error: impl ToString, desc: &'static str) -> CallError { CallError::Custom(ErrorObject::owned( Error::RuntimeError.into(), desc, Some(error.to_string()), )) } let res = api .query_info(at_hash, uxt, encoded_len) .map_err(|e| map_err(e, "Unable to query dispatch info."))?; Ok(RuntimeDispatchInfo { weight: res.weight, class: res.class, partial_fee: res.partial_fee, }) } fn query_fee_details( &self, encoded_xt: Bytes, at: Option, ) -> RpcResult> { let api = self.client.runtime_api(); let at_hash = at.unwrap_or_else(|| self.client.info().best_hash); let encoded_len = encoded_xt.len() as u32; let uxt: Block::Extrinsic = Decode::decode(&mut &*encoded_xt).map_err(|e| { CallError::Custom(ErrorObject::owned( Error::DecodeError.into(), "Unable to query fee details.", Some(format!("{:?}", e)), )) })?; let fee_details = api.query_fee_details(at_hash, uxt, encoded_len).map_err(|e| { CallError::Custom(ErrorObject::owned( Error::RuntimeError.into(), "Unable to query fee details.", Some(e.to_string()), )) })?; let try_into_rpc_balance = |value: Balance| { value.try_into().map_err(|_| { JsonRpseeError::Call(CallError::Custom(ErrorObject::owned( ErrorCode::InvalidParams.code(), format!("{} doesn't fit in NumberOrHex representation", value), None::<()>, ))) }) }; Ok(FeeDetails { inclusion_fee: if let Some(inclusion_fee) = fee_details.inclusion_fee { Some(InclusionFee { base_fee: try_into_rpc_balance(inclusion_fee.base_fee)?, len_fee: try_into_rpc_balance(inclusion_fee.len_fee)?, adjusted_weight_fee: try_into_rpc_balance(inclusion_fee.adjusted_weight_fee)?, }) } else { None }, tip: Default::default(), }) } }