mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 17:11:02 +00:00
Metadata V16: Implement support for Pallet View Functions (#1981)
* Support Pallet View Functions in Subxt * fmt * clippy * Move a little view function logic to subxt_core * clippy * Add back check that prob isnt needed * avoid vec macro in core * Add view funciton test and apply various fixes to get it working * Add test for dynamic view fn call and fix issues * clippy * fix test-runtime * fmt * remove export * avoid vec for nostd core * use const instead of fn for view fn call name * Update to support latest unstable metadata * Update metadata stripping tests for new v16 version
This commit is contained in:
@@ -11,6 +11,7 @@ use crate::{
|
||||
runtime_api::RuntimeApiClient,
|
||||
storage::StorageClient,
|
||||
tx::TxClient,
|
||||
view_functions::ViewFunctionsClient,
|
||||
Metadata,
|
||||
};
|
||||
|
||||
@@ -67,11 +68,16 @@ pub trait OfflineClientT<T: Config>: Clone + Send + Sync + 'static {
|
||||
BlocksClient::new(self.clone())
|
||||
}
|
||||
|
||||
/// Work with runtime API.
|
||||
/// Work with runtime APIs.
|
||||
fn runtime_api(&self) -> RuntimeApiClient<T, Self> {
|
||||
RuntimeApiClient::new(self.clone())
|
||||
}
|
||||
|
||||
/// Work with View Functions.
|
||||
fn view_functions(&self) -> ViewFunctionsClient<T, Self> {
|
||||
ViewFunctionsClient::new(self.clone())
|
||||
}
|
||||
|
||||
/// Work this custom types.
|
||||
fn custom_values(&self) -> CustomValuesClient<T, Self> {
|
||||
CustomValuesClient::new(self.clone())
|
||||
@@ -150,6 +156,21 @@ impl<T: Config> OfflineClient<T> {
|
||||
<Self as OfflineClientT<T>>::constants(self)
|
||||
}
|
||||
|
||||
/// Work with blocks.
|
||||
pub fn blocks(&self) -> BlocksClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::blocks(self)
|
||||
}
|
||||
|
||||
/// Work with runtime APIs.
|
||||
pub fn runtime_api(&self) -> RuntimeApiClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::runtime_api(self)
|
||||
}
|
||||
|
||||
/// Work with View Functions.
|
||||
pub fn view_functions(&self) -> ViewFunctionsClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::view_functions(self)
|
||||
}
|
||||
|
||||
/// Access custom types
|
||||
pub fn custom_values(&self) -> CustomValuesClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::custom_values(self)
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::{
|
||||
runtime_api::RuntimeApiClient,
|
||||
storage::StorageClient,
|
||||
tx::TxClient,
|
||||
view_functions::ViewFunctionsClient,
|
||||
Metadata,
|
||||
};
|
||||
use derive_where::derive_where;
|
||||
@@ -348,11 +349,6 @@ impl<T: Config> OnlineClient<T> {
|
||||
<Self as OfflineClientT<T>>::constants(self)
|
||||
}
|
||||
|
||||
/// Access custom types.
|
||||
pub fn custom_values(&self) -> CustomValuesClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::custom_values(self)
|
||||
}
|
||||
|
||||
/// Work with blocks.
|
||||
pub fn blocks(&self) -> BlocksClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::blocks(self)
|
||||
@@ -362,6 +358,16 @@ impl<T: Config> OnlineClient<T> {
|
||||
pub fn runtime_api(&self) -> RuntimeApiClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::runtime_api(self)
|
||||
}
|
||||
|
||||
/// Work with View Functions.
|
||||
pub fn view_functions(&self) -> ViewFunctionsClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::view_functions(self)
|
||||
}
|
||||
|
||||
/// Access custom types.
|
||||
pub fn custom_values(&self) -> CustomValuesClient<T, Self> {
|
||||
<Self as OfflineClientT<T>>::custom_values(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> OfflineClientT<T> for OnlineClient<T> {
|
||||
|
||||
+3
-1
@@ -49,6 +49,7 @@ pub mod runtime_api;
|
||||
pub mod storage;
|
||||
pub mod tx;
|
||||
pub mod utils;
|
||||
pub mod view_functions;
|
||||
|
||||
/// This module provides a [`Config`] type, which is used to define various
|
||||
/// types that are important in order to speak to a particular chain.
|
||||
@@ -75,7 +76,8 @@ pub mod metadata {
|
||||
/// Submit dynamic transactions.
|
||||
pub mod dynamic {
|
||||
pub use subxt_core::dynamic::{
|
||||
constant, runtime_api_call, storage, tx, At, DecodedValue, DecodedValueThunk, Value,
|
||||
constant, runtime_api_call, storage, tx, view_function_call, At, DecodedValue,
|
||||
DecodedValueThunk, Value,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Types associated with executing View Function calls.
|
||||
|
||||
mod view_function_types;
|
||||
mod view_functions_client;
|
||||
|
||||
pub use subxt_core::view_functions::payload::{
|
||||
dynamic, DefaultPayload, DynamicPayload, Payload, StaticPayload,
|
||||
};
|
||||
pub use view_function_types::ViewFunctionsApi;
|
||||
pub use view_functions_client::ViewFunctionsClient;
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::Payload;
|
||||
use crate::{
|
||||
backend::BlockRef,
|
||||
client::OnlineClientT,
|
||||
config::{Config, HashFor},
|
||||
error::Error,
|
||||
};
|
||||
use derive_where::derive_where;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
|
||||
/// Execute View Function calls.
|
||||
#[derive_where(Clone; Client)]
|
||||
pub struct ViewFunctionsApi<T: Config, Client> {
|
||||
client: Client,
|
||||
block_ref: BlockRef<HashFor<T>>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Config, Client> ViewFunctionsApi<T, Client> {
|
||||
/// Create a new [`ViewFunctionsApi`]
|
||||
pub(crate) fn new(client: Client, block_ref: BlockRef<HashFor<T>>) -> Self {
|
||||
Self {
|
||||
client,
|
||||
block_ref,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> ViewFunctionsApi<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Run the validation logic against some View Function payload you'd like to use. Returns `Ok(())`
|
||||
/// if the payload is valid (or if it's not possible to check since the payload has no validation hash).
|
||||
/// Return an error if the payload was not valid or something went wrong trying to validate it (ie
|
||||
/// the View Function in question do not exist at all)
|
||||
pub fn validate<Call: Payload>(&self, payload: &Call) -> Result<(), Error> {
|
||||
subxt_core::view_functions::validate(payload, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Execute a View Function call.
|
||||
pub fn call<Call: Payload>(
|
||||
&self,
|
||||
payload: Call,
|
||||
) -> impl Future<Output = Result<Call::ReturnType, Error>> {
|
||||
let client = self.client.clone();
|
||||
let block_hash = self.block_ref.hash();
|
||||
// Ensure that the returned future doesn't have a lifetime tied to api.view_functions(),
|
||||
// which is a temporary thing we'll be throwing away quickly:
|
||||
async move {
|
||||
let metadata = client.metadata();
|
||||
|
||||
// Validate the View Function payload hash against the compile hash from codegen.
|
||||
subxt_core::view_functions::validate(&payload, &metadata)?;
|
||||
|
||||
// Assemble the data to call the "execute_view_function" runtime API, which
|
||||
// then calls the relevant view function.
|
||||
let call_name = subxt_core::view_functions::CALL_NAME;
|
||||
let call_args = subxt_core::view_functions::call_args(&payload, &metadata)?;
|
||||
|
||||
// Make the call.
|
||||
let bytes = client
|
||||
.backend()
|
||||
.call(call_name, Some(call_args.as_slice()), block_hash)
|
||||
.await?;
|
||||
|
||||
// Decode the response.
|
||||
let value =
|
||||
subxt_core::view_functions::decode_value(&mut &*bytes, &payload, &metadata)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::view_function_types::ViewFunctionsApi;
|
||||
|
||||
use crate::{
|
||||
backend::BlockRef,
|
||||
client::OnlineClientT,
|
||||
config::{Config, HashFor},
|
||||
error::Error,
|
||||
};
|
||||
use derive_where::derive_where;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
|
||||
/// Make View Function calls at some block.
|
||||
#[derive_where(Clone; Client)]
|
||||
pub struct ViewFunctionsClient<T, Client> {
|
||||
client: Client,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, Client> ViewFunctionsClient<T, Client> {
|
||||
/// Create a new [`ViewFunctionsClient`]
|
||||
pub fn new(client: Client) -> Self {
|
||||
Self {
|
||||
client,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> ViewFunctionsClient<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Obtain an interface to call View Functions at some block hash.
|
||||
pub fn at(&self, block_ref: impl Into<BlockRef<HashFor<T>>>) -> ViewFunctionsApi<T, Client> {
|
||||
ViewFunctionsApi::new(self.client.clone(), block_ref.into())
|
||||
}
|
||||
|
||||
/// Obtain an interface to call View Functions at the latest block hash.
|
||||
pub fn at_latest(
|
||||
&self,
|
||||
) -> impl Future<Output = Result<ViewFunctionsApi<T, Client>, Error>> + Send + 'static {
|
||||
// Clone and pass the client in like this so that we can explicitly
|
||||
// return a Future that's Send + 'static, rather than tied to &self.
|
||||
let client = self.client.clone();
|
||||
async move {
|
||||
// get the ref for the latest finalized block and use that.
|
||||
let block_ref = client.backend().latest_finalized_block_ref().await?;
|
||||
|
||||
Ok(ViewFunctionsApi::new(client, block_ref))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user