mod payload;
use crate::client::{OfflineClientAtBlockT, OnlineClientAtBlockT};
use crate::config::Config;
use crate::error::ViewFunctionError;
use derive_where::derive_where;
use scale_decode::IntoVisitor;
use std::marker::PhantomData;
pub use payload::{DynamicPayload, Payload, StaticPayload, dynamic};
const CALL_NAME: &str = "RuntimeViewFunction_execute_view_function";
#[derive_where(Clone; Client)]
pub struct ViewFunctionsClient<'atblock, T: Config, Client> {
client: &'atblock Client,
marker: PhantomData<T>,
}
impl<'atblock, T: Config, Client> ViewFunctionsClient<'atblock, T, Client> {
pub(crate) fn new(client: &'atblock Client) -> Self {
Self {
client,
marker: PhantomData,
}
}
}
impl<'atblock, T, Client> ViewFunctionsClient<'atblock, T, Client>
where
T: Config,
Client: OfflineClientAtBlockT<T>,
{
pub fn validate<Call: Payload>(&self, payload: Call) -> Result<(), ViewFunctionError> {
let Some(hash) = payload.validation_hash() else {
return Ok(());
};
let metadata = self.client.metadata_ref();
let pallet_name = payload.pallet_name();
let function_name = payload.function_name();
let view_function = metadata
.pallet_by_name(pallet_name)
.ok_or_else(|| ViewFunctionError::PalletNotFound(pallet_name.to_string()))?
.view_function_by_name(function_name)
.ok_or_else(|| ViewFunctionError::ViewFunctionNotFound {
pallet_name: pallet_name.to_string(),
function_name: function_name.to_string(),
})?;
if hash != view_function.hash() {
Err(ViewFunctionError::IncompatibleCodegen)
} else {
Ok(())
}
}
pub fn encode_args<P: Payload>(&self, payload: P) -> Result<Vec<u8>, ViewFunctionError> {
let metadata = self.client.metadata_ref();
let inputs = frame_decode::view_functions::encode_view_function_inputs(
payload.pallet_name(),
payload.function_name(),
payload.args(),
metadata,
metadata.types(),
)
.map_err(ViewFunctionError::CouldNotEncodeInputs)?;
Ok(inputs)
}
}
impl<'atblock, T, Client> ViewFunctionsClient<'atblock, T, Client>
where
T: Config,
Client: OnlineClientAtBlockT<T>,
{
pub async fn call_raw(
&self,
call_parameters: Option<&[u8]>,
) -> Result<Vec<u8>, ViewFunctionError> {
let client = &self.client;
let block_hash = client.block_ref().hash();
let data = client
.backend()
.call(CALL_NAME, call_parameters, block_hash)
.await
.map_err(ViewFunctionError::CannotCallApi)?;
Ok(data)
}
pub async fn call<P: Payload>(&self, payload: P) -> Result<P::ReturnType, ViewFunctionError> {
let client = &self.client;
let metadata = client.metadata_ref();
let block_hash = client.block_ref().hash();
self.validate(&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(ViewFunctionError::CannotCallApi)?;
let cursor = &mut &*bytes;
let value = frame_decode::view_functions::decode_view_function_response(
payload.pallet_name(),
payload.function_name(),
cursor,
metadata,
metadata.types(),
P::ReturnType::into_visitor(),
)
.map_err(ViewFunctionError::CouldNotDecodeResponse)?;
Ok(value)
}
}