mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-07 07:18:03 +00:00
@@ -58,31 +58,34 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let block = block?;
|
||||
|
||||
let block_hash = block.hash();
|
||||
println!(" Block {:?}", block_hash);
|
||||
|
||||
println!(" Block {:?}", block_hash);
|
||||
println!(" Block {:?}", block_hash);
|
||||
// Ask for the extrinsics for this block.
|
||||
for extrinsic in block.body().await?.extrinsics() {
|
||||
let extrinsics = block.extrinsics().await?;
|
||||
|
||||
// Ask for the extrinsics for this block.
|
||||
for extrinsic in extrinsics.iter() {
|
||||
let extrinsic = extrinsic?;
|
||||
println!(" Extrinsic index {:?}", extrinsic.index());
|
||||
|
||||
let decoded = extrinsic.decode_generic();
|
||||
match decoded {
|
||||
Ok(decoded) => {
|
||||
if let Some((address, signature, extra)) = decoded.signature {
|
||||
println!(" Decoded Signature");
|
||||
println!(" Decoded Address: {:?}", address);
|
||||
println!(" Decoded Sign: {:?}", signature);
|
||||
println!(" Decoded Extra: {:?}", extra);
|
||||
};
|
||||
// let decoded = extrinsic.decode_generic();
|
||||
// match decoded {
|
||||
// Ok(decoded) => {
|
||||
// if let Some((address, signature, extra)) = decoded.signature {
|
||||
// println!(" Decoded Signature");
|
||||
// println!(" Decoded Address: {:?}", address);
|
||||
// println!(" Decoded Sign: {:?}", signature);
|
||||
// println!(" Decoded Extra: {:?}", extra);
|
||||
// };
|
||||
|
||||
let call = decoded.function;
|
||||
println!(" Decoded call:\n {:?}", call.to_value());
|
||||
}
|
||||
Err(err) => {
|
||||
println!(" Decoded extrinsic with error: {:?}", err);
|
||||
}
|
||||
}
|
||||
println!("\n");
|
||||
// let call = decoded.function;
|
||||
// println!(" Decoded call:\n {:?}", call.to_value());
|
||||
// }
|
||||
// Err(err) => {
|
||||
// println!(" Decoded extrinsic with error: {:?}", err);
|
||||
// }
|
||||
// }
|
||||
// println!("\n");
|
||||
}
|
||||
|
||||
println!("\n");
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
config::{Config, Hasher, Header},
|
||||
dynamic::DecodedValueThunk,
|
||||
error::{BlockError, Error},
|
||||
events,
|
||||
events, extrinsics,
|
||||
metadata::DecodeWithMetadata,
|
||||
rpc::types::ChainBlockResponse,
|
||||
runtime_api::RuntimeApi,
|
||||
@@ -72,13 +72,15 @@ where
|
||||
get_events(&self.client, self.header.hash(), &self.cached_events).await
|
||||
}
|
||||
|
||||
/// Return the extrinsics associated with the block.
|
||||
pub async fn extrinsics(&self) -> Result<extrinsics::Extrinsics<T>, Error> {
|
||||
let block_details = self.block_details().await?;
|
||||
extrinsics::Extrinsics::new(self.client.metadata(), block_details.block)
|
||||
}
|
||||
|
||||
/// Fetch and return the block body.
|
||||
pub async fn body(&self) -> Result<BlockBody<T, C>, Error> {
|
||||
let block_hash = self.header.hash();
|
||||
let block_details = match self.client.rpc().block(Some(block_hash)).await? {
|
||||
Some(block) => block,
|
||||
None => return Err(BlockError::not_found(block_hash).into()),
|
||||
};
|
||||
let block_details = self.block_details().await?;
|
||||
|
||||
Ok(BlockBody::new(
|
||||
self.client.clone(),
|
||||
@@ -97,6 +99,15 @@ where
|
||||
pub async fn runtime_api(&self) -> Result<RuntimeApi<T, C>, Error> {
|
||||
Ok(RuntimeApi::new(self.client.clone(), self.hash()))
|
||||
}
|
||||
|
||||
/// Fetch the block's body from the chain.
|
||||
async fn block_details(&self) -> Result<ChainBlockResponse<T>, Error> {
|
||||
let block_hash = self.header.hash();
|
||||
match self.client.rpc().block(Some(block_hash)).await? {
|
||||
Some(block) => Ok(block),
|
||||
None => Err(BlockError::not_found(block_hash).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic type IDs passed to the `UncheckedExtrinsic`.
|
||||
|
||||
@@ -57,6 +57,9 @@ pub enum Error {
|
||||
/// Block related error.
|
||||
#[error("Block error: {0}")]
|
||||
Block(#[from] BlockError),
|
||||
/// Extrinsic related error.
|
||||
#[error("Extrinsic error: {0}")]
|
||||
Extrinsic(#[from] ExtrinsicError),
|
||||
/// An error encoding a storage address.
|
||||
#[error("Error encoding storage address: {0}")]
|
||||
StorageAddress(#[from] StorageAddressError),
|
||||
@@ -112,6 +115,25 @@ impl BlockError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extrinsic error.
|
||||
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum ExtrinsicError {
|
||||
/// Extrinsic type ID cannot be resolved with the provided metadata.
|
||||
#[error("Extrinsic type ID cannot be resolved with the provided metadata. Make sure this is a valid metadata")]
|
||||
MissingType,
|
||||
/// Expected more extrinsic bytes.
|
||||
#[error("Expected more extrinsic bytes")]
|
||||
InsufficientData,
|
||||
/// Unsupported signature.
|
||||
#[error("Unsupported extrinsic version, only version 4 is supported currently")]
|
||||
/// The extrinsic has an unsupported version.
|
||||
UnsupportedVersion(u8),
|
||||
/// Decoding error.
|
||||
#[error("Cannot decode extrinsic: {0}")]
|
||||
DecodingError(codec::Error),
|
||||
}
|
||||
|
||||
/// Transaction error.
|
||||
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::config::Header;
|
||||
use crate::{
|
||||
client::OnlineClientT,
|
||||
error::{BlockError, Error},
|
||||
extrinsics::Extrinsics,
|
||||
Config,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use std::future::Future;
|
||||
|
||||
/// A client for working with extrinsics.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound = "Client: Clone"))]
|
||||
pub struct ExtrinsicsClient<T, Client> {
|
||||
client: Client,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, Client> ExtrinsicsClient<T, Client> {
|
||||
/// Create a new [`ExtrinsicsClient`].
|
||||
pub fn new(client: Client) -> Self {
|
||||
Self {
|
||||
client,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> ExtrinsicsClient<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Obtain extrinsics at some block hash.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This call only supports blocks produced since the most recent
|
||||
/// runtime upgrade. You can attempt to retrieve extrinsics from older blocks,
|
||||
/// but may run into errors attempting to work with them.
|
||||
pub fn at(
|
||||
&self,
|
||||
block_hash: T::Hash,
|
||||
) -> impl Future<Output = Result<Extrinsics<T>, Error>> + Send + 'static {
|
||||
self.at_or_latest(Some(block_hash))
|
||||
}
|
||||
|
||||
/// Obtain extrinsics at the latest block hash.
|
||||
pub fn at_latest(&self) -> impl Future<Output = Result<Extrinsics<T>, Error>> + Send + 'static {
|
||||
self.at_or_latest(None)
|
||||
}
|
||||
|
||||
/// Obtain extrinsics at some block hash.
|
||||
fn at_or_latest(
|
||||
&self,
|
||||
block_hash: Option<T::Hash>,
|
||||
) -> impl Future<Output = Result<Extrinsics<T>, 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 {
|
||||
// If block hash is not provided, get the hash
|
||||
// for the latest block and use that to create an explicit error.
|
||||
let block_hash = match block_hash {
|
||||
Some(hash) => hash,
|
||||
None => client
|
||||
.rpc()
|
||||
.block_hash(None)
|
||||
.await?
|
||||
.expect("didn't pass a block number; qed"),
|
||||
};
|
||||
|
||||
let result = client.rpc().block(Some(block_hash)).await?;
|
||||
let Some(block_details) = result else {
|
||||
return Err(BlockError::not_found(block_hash).into());
|
||||
};
|
||||
|
||||
Extrinsics::new(client.metadata(), block_details.block)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, Hasher, Header},
|
||||
dynamic::DecodedValueThunk,
|
||||
error::{BlockError, Error, ExtrinsicError},
|
||||
events,
|
||||
metadata::DecodeWithMetadata,
|
||||
rpc::types::{ChainBlock, ChainBlockExtrinsic, ChainBlockResponse},
|
||||
runtime_api::RuntimeApi,
|
||||
storage::Storage,
|
||||
Metadata,
|
||||
};
|
||||
use codec::{Codec, Decode, Encode};
|
||||
use derivative::Derivative;
|
||||
use frame_metadata::v15::RuntimeMetadataV15;
|
||||
use futures::lock::Mutex as AsyncMutex;
|
||||
use scale_value::{scale::decode_as_type, Value};
|
||||
use std::{collections::HashMap, marker::PhantomData, sync::Arc};
|
||||
|
||||
/// A collection of extrinsics obtained from a block, bundled with the necessary
|
||||
/// information needed to decode and iterate over them.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug(bound = ""), Clone(bound = ""))]
|
||||
pub struct Extrinsics<T: Config> {
|
||||
metadata: Metadata,
|
||||
/// The block hash.
|
||||
hash: T::Hash,
|
||||
/// The accompanying extrinsics.
|
||||
extrinsics: Vec<ChainBlockExtrinsic>,
|
||||
/// Generic extrinsic parameter ids from the metadata.
|
||||
ids: ExtrinsicIds,
|
||||
}
|
||||
|
||||
impl<T: Config> Extrinsics<T> {
|
||||
pub(crate) fn new(metadata: Metadata, block: ChainBlock<T>) -> Result<Self, Error> {
|
||||
let ids = ExtrinsicIds::new(metadata.runtime_metadata())?;
|
||||
|
||||
Ok(Self {
|
||||
metadata,
|
||||
hash: block.header.hash(),
|
||||
extrinsics: block.extrinsics,
|
||||
ids,
|
||||
})
|
||||
}
|
||||
|
||||
// /// Obtain the extrinsics from a block hash given custom metadata and a client.
|
||||
// ///
|
||||
// /// This method gives users the ability to inspect the extrinsics of older blocks,
|
||||
// /// where the metadata changed. For those cases, the user is responsible for
|
||||
// /// providing a valid metadata.
|
||||
// pub async fn new_from_client<Client>(
|
||||
// metadata: Metadata,
|
||||
// block_hash: T::Hash,
|
||||
// client: Client,
|
||||
// ) -> Result<Self, Error>
|
||||
// where
|
||||
// Client: OnlineClientT<T>,
|
||||
// {
|
||||
// // let event_bytes = get_event_bytes(&client, Some(block_hash)).await?;
|
||||
// // Ok(Events::new(metadata, block_hash, event_bytes))
|
||||
// }
|
||||
|
||||
/// The number of extrinsics.
|
||||
pub fn len(&self) -> usize {
|
||||
self.extrinsics.len()
|
||||
}
|
||||
|
||||
/// Are there no extrinsics in this block?
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.extrinsics.len() == 0
|
||||
}
|
||||
|
||||
/// Return the block hash that these extrinsics are from.
|
||||
pub fn block_hash(&self) -> T::Hash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
/// Iterate over all of the events, using metadata to dynamically
|
||||
/// decode them as we go, and returning the raw bytes and other associated
|
||||
/// details. If an error occurs, all subsequent iterations return `None`.
|
||||
// Dev note: The returned iterator is 'static + Send so that we can box it up and make
|
||||
// use of it with our `FilterEvents` stuff.
|
||||
pub fn iter(
|
||||
&self,
|
||||
) -> impl Iterator<Item = Result<ExtrinsicDetails, Error>> + Send + Sync + 'static {
|
||||
let metadata = self.metadata.clone();
|
||||
// TODO: Dummy clone should use Arc<[u8]>.
|
||||
let extrinsics = self.extrinsics.clone();
|
||||
let ids = self.ids.clone();
|
||||
let num_extr = self.extrinsics.len();
|
||||
let mut index = 0;
|
||||
std::iter::from_fn(move || {
|
||||
if index == num_extr {
|
||||
None
|
||||
} else {
|
||||
match ExtrinsicDetails::decode_from::<T>(
|
||||
metadata.clone(),
|
||||
extrinsics[index].clone(),
|
||||
ids,
|
||||
index,
|
||||
) {
|
||||
Ok(event_details) => {
|
||||
// Increment the index:
|
||||
index += 1;
|
||||
// Return the event details:
|
||||
Some(Ok(event_details))
|
||||
}
|
||||
Err(e) => {
|
||||
// By setting the position to the "end" of the event bytes,
|
||||
// the cursor len will become 0 and the iterator will return `None`
|
||||
// from now on:
|
||||
index = num_extr;
|
||||
Some(Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// /// Iterate through the events using metadata to dynamically decode and skip
|
||||
// /// them, and return only those which should decode to the provided `Ev` type.
|
||||
// /// If an error occurs, all subsequent iterations return `None`.
|
||||
// pub fn find<Ev: StaticEvent>(&self) -> impl Iterator<Item = Result<Ev, Error>> + '_ {
|
||||
// self.iter().filter_map(|ev| {
|
||||
// ev.and_then(|ev| ev.as_event::<Ev>().map_err(Into::into))
|
||||
// .transpose()
|
||||
// })
|
||||
// }
|
||||
|
||||
// /// Iterate through the events using metadata to dynamically decode and skip
|
||||
// /// them, and return the first event found which decodes to the provided `Ev` type.
|
||||
// pub fn find_first<Ev: StaticEvent>(&self) -> Result<Option<Ev>, Error> {
|
||||
// self.find::<Ev>().next().transpose()
|
||||
// }
|
||||
|
||||
// /// Iterate through the events using metadata to dynamically decode and skip
|
||||
// /// them, and return the last event found which decodes to the provided `Ev` type.
|
||||
// pub fn find_last<Ev: StaticEvent>(&self) -> Result<Option<Ev>, Error> {
|
||||
// self.find::<Ev>().last().transpose()
|
||||
// }
|
||||
|
||||
// /// Find an event that decodes to the type provided. Returns true if it was found.
|
||||
// pub fn has<Ev: StaticEvent>(&self) -> Result<bool, Error> {
|
||||
// Ok(self.find::<Ev>().next().transpose()?.is_some())
|
||||
// }
|
||||
}
|
||||
|
||||
/// The type IDs extracted from the metadata that represent the
|
||||
/// generic type parameters passed to the `UncheckedExtrinsic` from
|
||||
/// the substrate-based chain.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct ExtrinsicIds {
|
||||
/// The address (source) of the extrinsic.
|
||||
address: u32,
|
||||
/// The extrinsic call type.
|
||||
call: u32,
|
||||
/// The signature of the extrinsic.
|
||||
signature: u32,
|
||||
/// The extra parameters of the extrinsic.
|
||||
extra: u32,
|
||||
}
|
||||
|
||||
impl ExtrinsicIds {
|
||||
/// Extract the generic type parameters IDs from the extrinsic type.
|
||||
fn new(metadata: &RuntimeMetadataV15) -> Result<Self, ExtrinsicError> {
|
||||
const ADDRESS: &str = "Address";
|
||||
const CALL: &str = "Call";
|
||||
const SIGNATURE: &str = "Signature";
|
||||
const EXTRA: &str = "Extra";
|
||||
|
||||
let id = metadata.extrinsic.ty.id;
|
||||
|
||||
let Some(ty) = metadata.types.resolve(id) else {
|
||||
return Err(ExtrinsicError::MissingType);
|
||||
};
|
||||
|
||||
let params: HashMap<_, _> = ty
|
||||
.type_params
|
||||
.iter()
|
||||
.map(|ty_param| {
|
||||
let Some(ty) = ty_param.ty else {
|
||||
return Err(ExtrinsicError::MissingType);
|
||||
};
|
||||
|
||||
Ok((ty_param.name.as_str(), ty.id))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let Some(address) = params.get(ADDRESS) else {
|
||||
return Err(ExtrinsicError::MissingType);
|
||||
};
|
||||
let Some(call) = params.get(CALL) else {
|
||||
return Err(ExtrinsicError::MissingType);
|
||||
};
|
||||
let Some(signature) = params.get(SIGNATURE) else {
|
||||
return Err(ExtrinsicError::MissingType);
|
||||
};
|
||||
let Some(extra) = params.get(EXTRA) else {
|
||||
return Err(ExtrinsicError::MissingType);
|
||||
};
|
||||
|
||||
Ok(ExtrinsicIds {
|
||||
address: *address,
|
||||
call: *call,
|
||||
signature: *signature,
|
||||
extra: *extra,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The extrinsic details.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtrinsicDetails {
|
||||
ids: ExtrinsicIds,
|
||||
index: usize,
|
||||
metadata: Metadata,
|
||||
signature: Option<Value<u32>>,
|
||||
}
|
||||
|
||||
impl ExtrinsicDetails {
|
||||
// Attempt to dynamically decode a single event from our events input.
|
||||
fn decode_from<T: Config>(
|
||||
metadata: Metadata,
|
||||
extrinsic: ChainBlockExtrinsic,
|
||||
ids: ExtrinsicIds,
|
||||
index: usize,
|
||||
) -> Result<ExtrinsicDetails, Error> {
|
||||
const SIGNATURE_MASK: u8 = 0b1000_0000;
|
||||
const VERSION_MASK: u8 = 0b0111_1111;
|
||||
const LATEST_EXTRINSIC_VERSION: u8 = 4;
|
||||
|
||||
let bytes = extrinsic.0;
|
||||
|
||||
// Extrinsic are encoded in memory in the following way:
|
||||
// - first byte: abbbbbbb (a = 0 for unsigned, 1 for signed, b = version)
|
||||
// - signature: [unknown TBD with metadata].
|
||||
// - extrinsic data
|
||||
if bytes.is_empty() {
|
||||
return Err(ExtrinsicError::InsufficientData.into());
|
||||
}
|
||||
|
||||
let version = bytes[0] & VERSION_MASK;
|
||||
if version != LATEST_EXTRINSIC_VERSION {
|
||||
return Err(ExtrinsicError::UnsupportedVersion(version).into());
|
||||
}
|
||||
|
||||
let is_signed = bytes[0] & SIGNATURE_MASK != 0;
|
||||
|
||||
// Skip over the first byte which denotes the version and signing.
|
||||
let cursor = &mut &bytes[1..];
|
||||
|
||||
let signature = if is_signed {
|
||||
let address = decode_as_type(cursor, ids.address, &metadata.runtime_metadata().types)
|
||||
.map_err(scale_decode::Error::from)?;
|
||||
|
||||
let _signature =
|
||||
decode_as_type(cursor, ids.signature, &metadata.runtime_metadata().types)
|
||||
.map_err(scale_decode::Error::from)?;
|
||||
|
||||
let _extra = decode_as_type(cursor, ids.extra, &metadata.runtime_metadata().types)
|
||||
.map_err(scale_decode::Error::from)?;
|
||||
|
||||
Some(address)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Decode the extrinsic function call.
|
||||
let extrinsic = <DecodedValueThunk as DecodeWithMetadata>::decode_with_metadata(
|
||||
cursor, ids.call, &metadata,
|
||||
)?;
|
||||
|
||||
Ok(ExtrinsicDetails {
|
||||
ids,
|
||||
index,
|
||||
metadata,
|
||||
signature,
|
||||
})
|
||||
}
|
||||
|
||||
/// The index of the extrinsic in the given block.
|
||||
/// What index is this event in the stored events for this block.
|
||||
pub fn index(&self) -> usize {
|
||||
self.index
|
||||
}
|
||||
|
||||
// /// Return _all_ of the bytes representing this event, which include, in order:
|
||||
// /// - The phase.
|
||||
// /// - Pallet and event index.
|
||||
// /// - Event fields.
|
||||
// /// - Event Topics.
|
||||
// pub fn bytes(&self) -> &[u8] {
|
||||
// &self.all_bytes[self.start_idx..self.end_idx]
|
||||
// }
|
||||
|
||||
// /// Attempt to statically decode these [`EventDetails`] into a type representing the event
|
||||
// /// fields. This leans directly on [`codec::Decode`]. You can also attempt to decode the entirety
|
||||
// /// of the event using [`EventDetails::as_root_event()`], which is more lenient because it's able
|
||||
// /// to lean on [`scale_decode::DecodeAsType`].
|
||||
// pub fn as_event<E: StaticEvent>(&self) -> Result<Option<E>, Error> {
|
||||
// let ev_metadata = self.event_metadata();
|
||||
// if ev_metadata.pallet() == E::PALLET && ev_metadata.event() == E::EVENT {
|
||||
// let decoded = E::decode_as_fields(
|
||||
// &mut self.field_bytes(),
|
||||
// ev_metadata.fields(),
|
||||
// self.metadata.types(),
|
||||
// )?;
|
||||
// Ok(Some(decoded))
|
||||
// } else {
|
||||
// Ok(None)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! This module exposes the types and such necessary for working with extrinsics.
|
||||
//! The two main entry points into events are [`crate::OnlineClient::events()`]
|
||||
//! and calls like [crate::tx::TxProgress::wait_for_finalized_success()].
|
||||
|
||||
mod extrinsics_client;
|
||||
mod extrinsics_type;
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
pub use extrinsics_client::ExtrinsicsClient;
|
||||
pub use extrinsics_type::{ExtrinsicDetails, Extrinsics};
|
||||
|
||||
// pub use events_client::EventsClient;
|
||||
// pub use events_type::{
|
||||
// EventDetails,
|
||||
// Events,
|
||||
// // Used in codegen but hidden from docs:
|
||||
// RootEvent,
|
||||
// };
|
||||
// use scale_decode::DecodeAsFields;
|
||||
@@ -155,6 +155,7 @@ pub mod constants;
|
||||
pub mod dynamic;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod extrinsics;
|
||||
pub mod metadata;
|
||||
pub mod rpc;
|
||||
pub mod runtime_api;
|
||||
|
||||
Reference in New Issue
Block a user