mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-23 03:47:59 +00:00
Parameterize AccountData (#409)
* Add custom config example * Make account data storage item configurable * Fix contracts * Regenerate polkadot codegen * Fmt * Specify different type for `MyConfig::Index` * Update comment in custom config example * Assign concrete types for default AccountData impl * Fmt * Fix contracts tests * Fmt * Add comments * Unlink doc comment trait (subxt not in scope) * Fix missing nonce field error message * Update codegen/src/api/mod.rs Co-authored-by: David <dvdplm@gmail.com> * Update examples/custom_config.rs Co-authored-by: David <dvdplm@gmail.com> * Rename Nonce assoc type to Index for consistency * Add module level docs about codegen assumptions Co-authored-by: David <dvdplm@gmail.com>
This commit is contained in:
@@ -116,7 +116,7 @@ pub fn generate_calls(
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
A: ::subxt::AccountData<T>,
|
||||
A: ::subxt::AccountData,
|
||||
{
|
||||
pub fn new(client: &'a ::subxt::Client<T>) -> Self {
|
||||
Self { client, marker: ::core::marker::PhantomData }
|
||||
|
||||
+105
-18
@@ -14,6 +14,23 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generate code for submitting extrinsics and query storage of a Substrate runtime.
|
||||
//!
|
||||
//! ## Note
|
||||
//!
|
||||
//! By default the codegen will search for the `System` pallet's `Account` storage item, which is
|
||||
//! the conventional location where an account's index (aka nonce) is stored.
|
||||
//!
|
||||
//! If this `System::Account` storage item is discovered, then it is assumed that:
|
||||
//!
|
||||
//! 1. The type of the storage item is a `struct` (aka a composite type)
|
||||
//! 2. There exists a field called `nonce` which contains the account index.
|
||||
//!
|
||||
//! These assumptions are based on the fact that the `frame_system::AccountInfo` type is the default
|
||||
//! configured type, and that the vast majority of chain configurations will use this.
|
||||
//!
|
||||
//! If either of these conditions are not satisfied, the codegen will fail.
|
||||
|
||||
mod calls;
|
||||
mod constants;
|
||||
mod errors;
|
||||
@@ -32,8 +49,10 @@ use crate::{
|
||||
use codec::Decode;
|
||||
use frame_metadata::{
|
||||
v14::RuntimeMetadataV14,
|
||||
PalletMetadata,
|
||||
RuntimeMetadata,
|
||||
RuntimeMetadataPrefixed,
|
||||
StorageEntryType,
|
||||
};
|
||||
use heck::SnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
@@ -42,6 +61,7 @@ use quote::{
|
||||
format_ident,
|
||||
quote,
|
||||
};
|
||||
use scale_info::form::PortableForm;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
@@ -242,6 +262,16 @@ impl RuntimeGenerator {
|
||||
let error_type = error_details.type_def;
|
||||
let error_fn = error_details.dispatch_error_impl_fn;
|
||||
|
||||
let default_account_data_ident = format_ident!("DefaultAccountData");
|
||||
let default_account_data_impl = generate_default_account_data_impl(
|
||||
&pallets_with_mod_names,
|
||||
&default_account_data_ident,
|
||||
&type_gen,
|
||||
);
|
||||
let type_parameter_default_impl = default_account_data_impl
|
||||
.as_ref()
|
||||
.map(|_| quote!( = #default_account_data_ident ));
|
||||
|
||||
quote! {
|
||||
#[allow(dead_code, unused_imports, non_camel_case_types)]
|
||||
pub mod #mod_ident {
|
||||
@@ -249,10 +279,6 @@ impl RuntimeGenerator {
|
||||
#( #modules )*
|
||||
#types_mod
|
||||
|
||||
/// The default storage entry from which to fetch an account nonce, required for
|
||||
/// constructing a transaction.
|
||||
pub type DefaultAccountData = self::system::storage::Account;
|
||||
|
||||
/// The default error type returned when there is a runtime issue.
|
||||
pub type DispatchError = self::runtime_types::sp_runtime::DispatchError;
|
||||
|
||||
@@ -262,34 +288,29 @@ impl RuntimeGenerator {
|
||||
#error_fn
|
||||
}
|
||||
|
||||
impl ::subxt::AccountData<::subxt::DefaultConfig> for DefaultAccountData {
|
||||
fn nonce(result: &<Self as ::subxt::StorageEntry>::Value) -> <::subxt::DefaultConfig as ::subxt::Config>::Index {
|
||||
result.nonce
|
||||
}
|
||||
fn storage_entry(account_id: <::subxt::DefaultConfig as ::subxt::Config>::AccountId) -> Self {
|
||||
Self(account_id)
|
||||
}
|
||||
}
|
||||
#default_account_data_impl
|
||||
|
||||
pub struct RuntimeApi<T: ::subxt::Config, X> {
|
||||
pub struct RuntimeApi<T: ::subxt::Config, X, A #type_parameter_default_impl> {
|
||||
pub client: ::subxt::Client<T>,
|
||||
marker: ::core::marker::PhantomData<X>,
|
||||
marker: ::core::marker::PhantomData<(X, A)>,
|
||||
}
|
||||
|
||||
impl<T, X> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X>
|
||||
impl<T, X, A> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X, A>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
A: ::subxt::AccountData,
|
||||
{
|
||||
fn from(client: ::subxt::Client<T>) -> Self {
|
||||
Self { client, marker: ::core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, X> RuntimeApi<T, X>
|
||||
impl<'a, T, X, A> RuntimeApi<T, X, A>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
A: ::subxt::AccountData,
|
||||
{
|
||||
pub fn constants(&'a self) -> ConstantsApi {
|
||||
ConstantsApi
|
||||
@@ -299,7 +320,7 @@ impl RuntimeGenerator {
|
||||
StorageApi { client: &self.client }
|
||||
}
|
||||
|
||||
pub fn tx(&'a self) -> TransactionApi<'a, T, X, DefaultAccountData> {
|
||||
pub fn tx(&'a self) -> TransactionApi<'a, T, X, A> {
|
||||
TransactionApi { client: &self.client, marker: ::core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
@@ -339,7 +360,7 @@ impl RuntimeGenerator {
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
A: ::subxt::AccountData<T>,
|
||||
A: ::subxt::AccountData,
|
||||
{
|
||||
#(
|
||||
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, X, A> {
|
||||
@@ -352,6 +373,72 @@ impl RuntimeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
/// Most chains require a valid account nonce as part of the extrinsic, so the default behaviour of
|
||||
/// the client is to fetch the nonce for the current account.
|
||||
///
|
||||
/// The account index (aka nonce) is commonly stored in the `System` pallet's `Account` storage item.
|
||||
/// This function attempts to find that storage item, and if it is present will implement the
|
||||
/// `subxt::AccountData` trait for it. This allows the client to construct the appropriate
|
||||
/// storage key from the account id, and then retrieve the `nonce` from the resulting storage item.
|
||||
fn generate_default_account_data_impl(
|
||||
pallets_with_mod_names: &[(&PalletMetadata<PortableForm>, syn::Ident)],
|
||||
default_impl_name: &syn::Ident,
|
||||
type_gen: &TypeGenerator,
|
||||
) -> Option<TokenStream2> {
|
||||
let storage = pallets_with_mod_names
|
||||
.iter()
|
||||
.find(|(pallet, _)| pallet.name == "System")
|
||||
.and_then(|(pallet, _)| pallet.storage.as_ref())?;
|
||||
let storage_entry = storage
|
||||
.entries
|
||||
.iter()
|
||||
.find(|entry| entry.name == "Account")?;
|
||||
|
||||
// resolve the concrete types for `AccountId` (to build the key) and `Index` to extract the
|
||||
// account index (nonce) value from the result.
|
||||
let (account_id_ty, account_nonce_ty) =
|
||||
if let StorageEntryType::Map { key, value, .. } = &storage_entry.ty {
|
||||
let account_id_ty = type_gen.resolve_type_path(key.id(), &[]);
|
||||
let account_data_ty = type_gen.resolve_type(value.id());
|
||||
let nonce_field = if let scale_info::TypeDef::Composite(composite) =
|
||||
account_data_ty.type_def()
|
||||
{
|
||||
composite
|
||||
.fields()
|
||||
.iter()
|
||||
.find(|f| f.name() == Some(&"nonce".to_string()))?
|
||||
} else {
|
||||
abort_call_site!("Expected a `nonce` field in the account info struct")
|
||||
};
|
||||
let account_nonce_ty = type_gen.resolve_type_path(nonce_field.ty().id(), &[]);
|
||||
(account_id_ty, account_nonce_ty)
|
||||
} else {
|
||||
abort_call_site!("System::Account should be a `StorageEntryType::Map`")
|
||||
};
|
||||
|
||||
// this path to the storage entry depends on storage codegen.
|
||||
let storage_entry_path = quote!(self::system::storage::Account);
|
||||
|
||||
Some(quote! {
|
||||
/// The default storage entry from which to fetch an account nonce, required for
|
||||
/// constructing a transaction.
|
||||
pub enum #default_impl_name {}
|
||||
|
||||
impl ::subxt::AccountData for #default_impl_name {
|
||||
type StorageEntry = #storage_entry_path;
|
||||
type AccountId = #account_id_ty;
|
||||
type Index = #account_nonce_ty;
|
||||
|
||||
fn nonce(result: &<Self::StorageEntry as ::subxt::StorageEntry>::Value) -> Self::Index {
|
||||
result.nonce
|
||||
}
|
||||
fn storage_entry(account_id: Self::AccountId) -> Self::StorageEntry {
|
||||
#storage_entry_path(account_id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generate_structs_from_variants<'a, F>(
|
||||
type_gen: &'a TypeGenerator,
|
||||
type_id: u32,
|
||||
|
||||
Reference in New Issue
Block a user