mod payload;
use crate::client::{OfflineClientAtBlockT, OnlineClientAtBlockT};
use crate::config::Config;
use crate::error::RuntimeApiError;
use derive_where::derive_where;
use scale_decode::IntoVisitor;
use std::marker::PhantomData;
pub use payload::{DynamicPayload, Payload, StaticPayload, dynamic};
#[derive_where(Clone; Client)]
pub struct RuntimeApisClient<'atblock, T: Config, Client> {
client: &'atblock Client,
marker: PhantomData<T>,
}
impl<'atblock, T: Config, Client> RuntimeApisClient<'atblock, T, Client> {
pub(crate) fn new(client: &'atblock Client) -> Self {
Self {
client,
marker: PhantomData,
}
}
}
impl<'atblock, T, Client> RuntimeApisClient<'atblock, T, Client>
where
T: Config,
Client: OfflineClientAtBlockT<T>,
{
pub fn validate<P: Payload>(&self, payload: P) -> Result<(), RuntimeApiError> {
let Some(hash) = payload.validation_hash() else {
return Ok(());
};
let metadata = self.client.metadata_ref();
let trait_name = payload.trait_name();
let method_name = payload.method_name();
let api_trait = metadata
.runtime_api_trait_by_name(trait_name)
.ok_or_else(|| RuntimeApiError::TraitNotFound(trait_name.to_string()))?;
let api_method = api_trait.method_by_name(method_name).ok_or_else(|| {
RuntimeApiError::MethodNotFound {
trait_name: trait_name.to_string(),
method_name: method_name.to_string(),
}
})?;
if hash != api_method.hash() {
Err(RuntimeApiError::IncompatibleCodegen)
} else {
Ok(())
}
}
pub fn encode_name<P: Payload>(&self, payload: P) -> String {
format!("{}_{}", payload.trait_name(), payload.method_name())
}
pub fn encode_args<P: Payload>(&self, payload: P) -> Result<Vec<u8>, RuntimeApiError> {
let metadata = self.client.metadata_ref();
let value = frame_decode::runtime_apis::encode_runtime_api_inputs(
payload.trait_name(),
payload.method_name(),
payload.args(),
metadata,
metadata.types(),
)
.map_err(RuntimeApiError::CouldNotEncodeInputs)?;
Ok(value)
}
}
impl<'atblock, T, Client> RuntimeApisClient<'atblock, T, Client>
where
T: Config,
Client: OnlineClientAtBlockT<T>,
{
pub async fn call_raw<'a>(
&self,
function: &'a str,
call_parameters: Option<&'a [u8]>,
) -> Result<Vec<u8>, RuntimeApiError> {
let client = &self.client;
let block_hash = client.block_ref().hash();
let data = client
.backend()
.call(function, call_parameters, block_hash)
.await
.map_err(RuntimeApiError::CannotCallApi)?;
Ok(data)
}
pub async fn call<P: Payload>(&self, payload: P) -> Result<P::ReturnType, RuntimeApiError> {
let client = &self.client;
let block_hash = client.block_ref().hash();
let metadata = client.metadata_ref();
self.validate(&payload)?;
let call_name = self.encode_name(&payload);
let call_args = self.encode_args(&payload)?;
let bytes = client
.backend()
.call(&call_name, Some(call_args.as_slice()), block_hash)
.await
.map_err(RuntimeApiError::CannotCallApi)?;
let cursor = &mut &*bytes;
let value = frame_decode::runtime_apis::decode_runtime_api_response(
payload.trait_name(),
payload.method_name(),
cursor,
metadata,
metadata.types(),
P::ReturnType::into_visitor(),
)
.map_err(RuntimeApiError::CouldNotDecodeResponse)?;
Ok(value)
}
}