Add subxt documentation (#546)

* Documentation for cli crate

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add documentation and simplify calls generation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add documentation and simplify events generation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add documentation and simplify constants generation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add documentation and simplify storage generation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* codegen: Add lib documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* macro: Remove `-f bytes` as this is the default

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt/client: Add examples

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* lib_doc: Add documentation to dedicated file

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Link documentation from file

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add more documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add Storage example

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Add rpc documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* metadata: Add documentation for errors

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Add documentation for the `extrinsic` module

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Add documentation for `events` module

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add `Static Metadata Validation` section

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Add `Runtime Updates` section

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Fix link to documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* readme: Remove hardcoded versions

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Move documentation to dedicated folder

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Spaces between examples

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* More details for `types_mod_ident`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Add note for RuntimeGenerator

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Address feedback

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update codegen/src/api/constants.rs

Co-authored-by: James Wilson <james@jsdw.me>

* Update subxt/src/lib.rs

Co-authored-by: James Wilson <james@jsdw.me>

* Update subxt/src/metadata/metadata_type.rs

Co-authored-by: James Wilson <james@jsdw.me>

* Update subxt/src/storage.rs

Co-authored-by: James Wilson <james@jsdw.me>

* Address feedback

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Fix cargo clippy

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update call example

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Add example for fetching constants

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Link to examples in subxt.md

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update codegen/src/api/calls.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update codegen/src/api/calls.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update codegen/src/api/calls.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update codegen/src/api/constants.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update codegen/src/api/constants.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update codegen/src/api/storage.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update codegen/src/api/storage.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update codegen/src/api/storage.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update docs/subxt.md

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update docs/subxt.md

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update docs/subxt.md

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update docs/subxt.md

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update docs/subxt.md

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update subxt/src/extrinsic/mod.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update subxt/src/storage.rs

Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>

* Update documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Update examples/examples/balance_transfer.rs

Co-authored-by: James Wilson <james@jsdw.me>

* Update subxt/src/extrinsic/mod.rs

Co-authored-by: James Wilson <james@jsdw.me>

* Update subxt/src/rpc.rs

Co-authored-by: James Wilson <james@jsdw.me>

* Update subxt/src/updates.rs

Co-authored-by: James Wilson <james@jsdw.me>

* Update docs example with `PolkadotExtrinsicParams`

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* subxt: Remove extrinsic extra documentation

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* examples: Remove similar transfer example

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

* Apply cargo fmt

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>

Co-authored-by: James Wilson <james@jsdw.me>
Co-authored-by: Tarik Gul <47201679+TarikGul@users.noreply.github.com>
This commit is contained in:
Alexandru Vasile
2022-06-14 14:23:02 +03:00
committed by GitHub
parent 7ba95c2739
commit e3732e354f
20 changed files with 656 additions and 140 deletions
+4 -66
View File
@@ -22,81 +22,19 @@ subxt metadata -f bytes > metadata.scale
This defaults to querying the metadata of a locally running node on the default `http://localhost:9933/`. If querying
a different node then the `metadata` command accepts a `--url` argument.
### Generating the runtime API from the downloaded metadata
## Subxt Documentation
Declare a module and decorate it with the `subxt` attribute which points at the downloaded metadata for the
target runtime:
```rust
#[subxt::subxt(runtime_metadata_path = "metadata.scale")]
pub mod node_runtime { }
```
**Important:** `runtime_metadata_path` resolves to a path relative to the directory where your crate's `Cargo.toml`
resides ([`CARGO_MANIFEST_DIR`](https://doc.rust-lang.org/cargo/reference/environment-variables.html)), *not* relative to the source file.
### Initializing the API client
```rust
use subxt::{ClientBuilder, DefaultConfig, SubstrateExtrinsicParams};
let api = ClientBuilder::new()
.set_url("wss://rpc.polkadot.io:443")
.build()
.await?
.to_runtime_api::<node_runtime::RuntimeApi<DefaultConfig, SubstrateExtrinsicParams<DefaultConfig>>>();
```
The `RuntimeApi` type is generated by the `subxt` macro from the supplied metadata. This can be parameterized with user
supplied implementations for the `Config` and `Extra` types, if the default implementations differ from the target
chain.
### Querying Storage
Call the generated `RuntimeApi::storage()` method, followed by the `pallet_name()` and then the `storage_item_name()`.
So in order to query `Balances::TotalIssuance`:
```rust
let total_issuance = api
.storage()
.balances()
.total_issuance(None)
.await
.unwrap()
```
### Submitting Extrinsics
Submit an extrinsic, returning success once the transaction is validated and accepted into the pool:
```rust
use sp_keyring::AccountKeyring;
use subxt::PairSigner;
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let tx_hash = api
.tx()
.balances()
.transfer(dest, 10_000)
.sign_and_submit(&signer)
.await?;
```
For more advanced usage, which can wait for block inclusion and return any events triggered by the extrinsic, see the
[balance transfer example](./examples/examples/balance_transfer.rs).
For more details regarding utilizing subxt, please visit the [documentation](docs/subxt.md).
## Integration Testing
Most tests require a running substrate node to communicate with. This is done by spawning an instance of the
substrate node per test. It requires an executable binary `substrate` at [`polkadot-v0.9.10`](https://github.com/paritytech/substrate/releases/tag/polkadot-v0.9.10) on your path.
substrate node per test. It requires an up-to-date `substrate` executable on your path.
This can be installed from source via cargo:
```bash
cargo install --git https://github.com/paritytech/substrate node-cli --tag=polkadot-v0.9.10 --force
cargo install --git https://github.com/paritytech/substrate node-cli --force
```
## Real world usage
+4 -4
View File
@@ -63,7 +63,7 @@ enum Command {
/// Download metadata from a substrate node, for use with `subxt` codegen.
#[structopt(name = "metadata")]
Metadata {
/// the url of the substrate node to query for metadata
/// The url of the substrate node to query for metadata.
#[structopt(
name = "url",
long,
@@ -71,7 +71,7 @@ enum Command {
default_value = "http://localhost:9933"
)]
url: url::Url,
/// the format of the metadata to display: `json`, `hex` or `bytes`
/// The format of the metadata to display: `json`, `hex` or `bytes`.
#[structopt(long, short, default_value = "bytes")]
format: String,
},
@@ -81,10 +81,10 @@ enum Command {
///
/// `subxt codegen | rustfmt --edition=2018 --emit=stdout`
Codegen {
/// the url of the substrate node to query for metadata for codegen.
/// The url of the substrate node to query for metadata for codegen.
#[structopt(name = "url", long, parse(try_from_str))]
url: Option<url::Url>,
/// the path to the encoded metadata file.
/// The path to the encoded metadata file.
#[structopt(short, long, parse(from_os_str))]
file: Option<PathBuf>,
/// Additional derives
+39 -2
View File
@@ -20,7 +20,6 @@ use crate::types::{
};
use frame_metadata::{
v14::RuntimeMetadataV14,
PalletCallMetadata,
PalletMetadata,
};
use heck::{
@@ -35,13 +34,51 @@ use quote::{
};
use scale_info::form::PortableForm;
/// Generate calls from the provided pallet's metadata.
///
/// The function creates a new module named `calls` under the pallet's module.
/// ```ignore
/// pub mod PalletName {
/// pub mod calls {
/// ...
/// }
/// }
/// ```
///
/// The function generates the calls as rust structs that implement the `subxt::Call` trait
/// to uniquely identify the call's identity when creating the extrinsic.
///
/// ```ignore
/// pub struct CallName {
/// pub call_param: type,
/// }
/// impl ::subxt::Call for CallName {
/// ...
/// }
/// ```
///
/// Calls are extracted from the API and wrapped into the generated `TransactionApi` of
/// each module.
///
/// # Arguments
///
/// - `metadata` - Runtime metadata from which the calls are generated.
/// - `type_gen` - The type generator containing all types defined by metadata.
/// - `pallet` - Pallet metadata from which the calls are generated.
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
pub fn generate_calls(
metadata: &RuntimeMetadataV14,
type_gen: &TypeGenerator,
pallet: &PalletMetadata<PortableForm>,
call: &PalletCallMetadata<PortableForm>,
types_mod_ident: &syn::Ident,
) -> TokenStream2 {
// Early return if the pallet has no calls.
let call = if let Some(ref calls) = pallet.calls {
calls
} else {
return quote!()
};
let mut struct_defs = super::generate_structs_from_variants(
type_gen,
call.ty.id(),
+29 -2
View File
@@ -17,7 +17,6 @@
use crate::types::TypeGenerator;
use frame_metadata::{
v14::RuntimeMetadataV14,
PalletConstantMetadata,
PalletMetadata,
};
use heck::ToSnakeCase as _;
@@ -29,13 +28,41 @@ use quote::{
};
use scale_info::form::PortableForm;
/// Generate constants from the provided pallet's metadata.
///
/// The function creates a new module named `constants` under the pallet's module.
/// ```ignore
/// pub mod PalletName {
/// pub mod constants {
/// ...
/// }
/// }
/// ```
///
/// The constants are exposed via the `ConstantsApi` wrapper.
///
/// Although the constants are defined in the provided static metadata, the API
/// ensures that the constants are returned from the runtime metadata of the node.
/// This ensures that if the node's constants change value, we'll always see the latest values.
///
/// # Arguments
///
/// - `metadata` - Runtime metadata from which the calls are generated.
/// - `type_gen` - The type generator containing all types defined by metadata
/// - `pallet` - Pallet metadata from which the calls are generated.
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
pub fn generate_constants(
metadata: &RuntimeMetadataV14,
type_gen: &TypeGenerator,
pallet: &PalletMetadata<PortableForm>,
constants: &[PalletConstantMetadata<PortableForm>],
types_mod_ident: &syn::Ident,
) -> TokenStream2 {
// Early return if the pallet has no constants.
if pallet.constants.is_empty() {
return quote!()
}
let constants = &pallet.constants;
let constant_fns = constants.iter().map(|constant| {
let fn_name = format_ident!("{}", constant.name.to_snake_case());
let pallet_name = &pallet.name;
+36 -5
View File
@@ -15,20 +15,51 @@
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
use crate::types::TypeGenerator;
use frame_metadata::{
PalletEventMetadata,
PalletMetadata,
};
use frame_metadata::PalletMetadata;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use scale_info::form::PortableForm;
/// Generate events from the provided pallet metadata.
///
/// The function creates a new module named `events` under the pallet's module.
/// ```ignore
/// pub mod PalletName {
/// pub mod events {
/// ...
/// }
/// }
/// ```
///
/// The function generates the events as rust structs that implement the `subxt::Event` trait
/// to uniquely identify the event's identity when creating the extrinsic.
///
/// ```ignore
/// pub struct EventName {
/// pub event_param: type,
/// }
/// impl ::subxt::Event for EventName {
/// ...
/// }
/// ```
///
/// # Arguments
///
/// - `type_gen` - The type generator containing all types defined by metadata.
/// - `pallet` - Pallet metadata from which the events are generated.
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
pub fn generate_events(
type_gen: &TypeGenerator,
pallet: &PalletMetadata<PortableForm>,
event: &PalletEventMetadata<PortableForm>,
types_mod_ident: &syn::Ident,
) -> TokenStream2 {
// Early return if the pallet has no events.
let event = if let Some(ref event) = pallet.event {
event
} else {
return quote!()
};
let struct_defs = super::generate_structs_from_variants(
type_gen,
event.ty.id(),
+36 -39
View File
@@ -55,6 +55,15 @@ use std::{
};
use syn::parse_quote;
/// Generates the API for interacting with a Substrate runtime.
///
/// # Arguments
///
/// * `item_mod` - The module declaration for which the API is implemented.
/// * `path` - The path to the scale encoded metadata of the runtime node.
/// * `derives` - Provide custom derives for the generated types.
///
/// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases.
pub fn generate_runtime_api<P>(
item_mod: syn::ItemMod,
path: P,
@@ -78,11 +87,16 @@ where
generator.generate_runtime(item_mod, derives)
}
/// Create the API for interacting with a Substrate runtime.
pub struct RuntimeGenerator {
metadata: RuntimeMetadataV14,
}
impl RuntimeGenerator {
/// Create a new runtime generator from the provided metadata.
///
/// **Note:** If you have a path to the metadata, prefer to use [generate_runtime_api]
/// for generating the runtime API.
pub fn new(metadata: RuntimeMetadataPrefixed) -> Self {
match metadata.1 {
RuntimeMetadata::V14(v14) => Self { metadata: v14 },
@@ -90,6 +104,12 @@ impl RuntimeGenerator {
}
}
/// Generate the API for interacting with a Substrate runtime.
///
/// # Arguments
///
/// * `item_mod` - The module declaration for which the API is implemented.
/// * `derives` - Provide custom derives for the generated types.
pub fn generate_runtime(
&self,
item_mod: syn::ItemMod,
@@ -98,7 +118,7 @@ impl RuntimeGenerator {
let item_mod_ir = ir::ItemMod::from(item_mod);
let default_derives = derives.default_derives();
// some hardcoded default type substitutes, can be overridden by user
// Some hardcoded default type substitutes, can be overridden by user
let mut type_substitutes = [
(
"bitvec::order::Lsb0",
@@ -175,47 +195,24 @@ impl RuntimeGenerator {
let metadata_hash = get_metadata_per_pallet_hash(&self.metadata, &pallet_names);
let modules = pallets_with_mod_names.iter().map(|(pallet, mod_name)| {
let calls = if let Some(ref calls) = pallet.calls {
calls::generate_calls(
&self.metadata,
&type_gen,
pallet,
calls,
types_mod_ident,
)
} else {
quote!()
};
let calls =
calls::generate_calls(&self.metadata, &type_gen, pallet, types_mod_ident);
let event = if let Some(ref event) = pallet.event {
events::generate_events(&type_gen, pallet, event, types_mod_ident)
} else {
quote!()
};
let event = events::generate_events(&type_gen, pallet, types_mod_ident);
let storage_mod = if let Some(ref storage) = pallet.storage {
storage::generate_storage(
&self.metadata,
&type_gen,
pallet,
storage,
types_mod_ident,
)
} else {
quote!()
};
let storage_mod = storage::generate_storage(
&self.metadata,
&type_gen,
pallet,
types_mod_ident,
);
let constants_mod = if !pallet.constants.is_empty() {
constants::generate_constants(
&self.metadata,
&type_gen,
pallet,
&pallet.constants,
types_mod_ident,
)
} else {
quote!()
};
let constants_mod = constants::generate_constants(
&self.metadata,
&type_gen,
pallet,
types_mod_ident,
);
quote! {
pub mod #mod_name {
+39 -2
View File
@@ -18,7 +18,6 @@ use crate::types::TypeGenerator;
use frame_metadata::{
v14::RuntimeMetadataV14,
PalletMetadata,
PalletStorageMetadata,
StorageEntryMetadata,
StorageEntryModifier,
StorageEntryType,
@@ -36,13 +35,51 @@ use scale_info::{
TypeDef,
};
/// Generate storage from the provided pallet's metadata.
///
/// The function creates a new module named `storage` under the pallet's module.
///
/// ```ignore
/// pub mod PalletName {
/// pub mod storage {
/// ...
/// }
/// }
/// ```
///
/// The function generates the storage as rust structs that implement the `subxt::StorageEntry`
/// trait to uniquely identify the storage's identity when creating the extrinsic.
///
/// ```ignore
/// pub struct StorageName {
/// pub storage_param: type,
/// }
/// impl ::subxt::StorageEntry for StorageName {
/// ...
/// }
/// ```
///
/// Storages are extracted from the API and wrapped into the generated `StorageApi` of
/// each module.
///
/// # Arguments
///
/// - `metadata` - Runtime metadata from which the storages are generated.
/// - `type_gen` - The type generator containing all types defined by metadata.
/// - `pallet` - Pallet metadata from which the storages are generated.
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
pub fn generate_storage(
metadata: &RuntimeMetadataV14,
type_gen: &TypeGenerator,
pallet: &PalletMetadata<PortableForm>,
storage: &PalletStorageMetadata<PortableForm>,
types_mod_ident: &syn::Ident,
) -> TokenStream2 {
let storage = if let Some(ref storage) = pallet.storage {
storage
} else {
return quote!()
};
let (storage_structs, storage_fns): (Vec<_>, Vec<_>) = storage
.entries
.iter()
+32
View File
@@ -15,6 +15,38 @@
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! Library to generate an API for a Substrate runtime from its metadata.
//!
//! ## Generated Structure
//!
//! The API generator logic:
//! - At the root there is the `item_mod` provided (ie `pub mod api {}`)
//! - Pallets are represented by a child module (ie `pub mod PalletName {}`) of the root
//! - Each pallet exposes as child modules (if applicable):
//! - Calls (`pub mod calls {}`)
//! - Events (`pub mod events {}`)
//! - Storage (`pub mod storage {}`)
//! - Constants (`pub mod constants {}`)
//!
//! ## Example
//!
//! ```rust
//! use codec::Decode;
//! use frame_metadata::RuntimeMetadataPrefixed;
//! use subxt_codegen::DerivesRegistry;
//!
//! // Runtime metadata obtained from a node.
//! let metadata = <RuntimeMetadataPrefixed as Decode>::decode(encoded)?;
//! // Module under which the API is generated.
//! let item_mod = syn::parse_quote!(
//! pub mod api {}
//! );
//! // Default module derivatives.
//! let mut derives = DerivesRegistry::default();
//! // Generate the Runtime API.
//! let generator = subxt_codegen::RuntimeGenerator::new(metadata);
//! let runtime_api = generator.generate_runtime(item_mod, derives);
//! println!("{}", runtime_api);
//! ```
#![deny(unused_crate_dependencies)]
+144
View File
@@ -0,0 +1,144 @@
Subxt is a library to **sub**mit e**xt**rinsics to a [substrate](https://github.com/paritytech/substrate) node via RPC.
The generated Subxt API exposes the ability to:
- [Submit extrinsics](https://docs.substrate.io/v3/concepts/extrinsics/) (Calls)
- [Query storage](https://docs.substrate.io/v3/runtime/storage/) (Storage)
- [Query constants](https://docs.substrate.io/how-to-guides/v3/basics/configurable-constants/) (Constants)
- [Subscribe to events](https://docs.substrate.io/v3/runtime/events-and-errors/) (Events)
### Generate the runtime API
Subxt generates a runtime API from downloaded static metadata. The metadata can be downloaded using the
[subxt-cli](https://crates.io/crates/subxt-cli) tool.
To generate the runtime API, use the `subxt` attribute which points at downloaded static metadata.
```rust
#[subxt::subxt(runtime_metadata_path = "metadata.scale")]
pub mod node_runtime { }
```
The `node_runtime` has the following hierarchy:
```rust
pub mod node_runtime {
pub mod PalletName {
pub mod calls { }
pub mod storage { }
pub mod constants { }
pub mod events { }
}
}
```
For more information regarding the `node_runtime` hierarchy, please visit the
[subxt-codegen](https://docs.rs/subxt-codegen/latest/subxt_codegen/) documentation.
### Initializing the API client
```rust
use subxt::{ClientBuilder, DefaultConfig, PolkadotExtrinsicParams};
let api = ClientBuilder::new()
.set_url("wss://rpc.polkadot.io:443")
.build()
.await?
.to_runtime_api::<node_runtime::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
```
The `RuntimeApi` type is generated by the `subxt` macro from the supplied metadata. This can be parameterized with user
supplied implementations for the `Config` and `Extra` types, if the default implementation differs from the target
chain.
To ensure that extrinsics are properly submitted, during the build phase of the Client the
runtime metadata of the node is downloaded. If the URL is not specified (`set_url`), the local host is used instead.
### Submit Extrinsics
Extrinsics are obtained using the API's `RuntimeApi::tx()` method, followed by `pallet_name()` and then the
`call_item_name()`.
Submit an extrinsic, returning success once the transaction is validated and accepted into the pool:
Please visit the [balance_transfer](../examples/examples/balance_transfer.rs) example for more details.
### Querying Storage
The runtime storage is queried via the generated `RuntimeApi::storage()` method, followed by the `pallet_name()` and
then the `storage_item_name()`.
Please visit the [fetch_staking_details](../examples/examples/fetch_staking_details.rs) example for more details.
### Query Constants
Constants are embedded into the node's metadata.
The subxt offers the ability to query constants from the runtime metadata (metadata downloaded when constructing
the client, *not* the one provided for API generation).
To query constants use the generated `RuntimeApi::constants()` method, followed by the `pallet_name()` and then the
`constant_item_name()`.
Please visit the [fetch_constants](../examples/examples/fetch_constants.rs) example for more details.
### Subscribe to Events
To subscribe to events, use the generated `RuntimeApi::events()` method which exposes:
- `subscribe()` - Subscribe to events emitted from blocks. These blocks haven't necessarily been finalised.
- `subscribe_finalized()` - Subscribe to events from finalized blocks.
- `at()` - Obtain events at a given block hash.
*Examples*
- [subscribe_all_events](../examples/examples/subscribe_all_events.rs): Subscribe to events emitted from blocks.
- [subscribe_one_event](../examples/examples/subscribe_one_event.rs): Subscribe and filter by one event.
- [subscribe_some_events](../examples/examples/subscribe_some_events.rs): Subscribe and filter event.
### Static Metadata Validation
There are two types of metadata that the subxt is aware of:
- static metadata: Metadata used for generating the API.
- runtime metadata: Metadata downloaded from the target node when a subxt client is created.
There are cases when the static metadata is different from the runtime metadata of a node.
Such is the case when the node performs a runtime update.
To ensure that subxt can properly communicate with the target node the static metadata is validated
against the runtime metadata of the node.
This validation is performed at the Call, Constant, and Storage levels, as well for the entire metadata.
The level of granularity ensures that the users can still submit a given call, even if another
call suffered changes.
Full metadata validation:
```rust
// To make sure that all of our statically generated pallets are compatible with the
// runtime node, we can run this check:
api.validate_metadata()?;
```
Call level validation:
```rust
let extrinsic = api
.tx()
.balances()
// Constructing an extrinsic will fail if the metadata
// is not in sync with the generated API.
.transfer(dest, 123_456_789_012_345)?;
```
### Runtime Updates
There are cases when the node would perform a runtime update, and the runtime node's metadata would be
out of sync with the subxt's metadata.
The `UpdateClient` API keeps the `RuntimeVersion` and `Metadata` of the client synced with the target node.
Please visit the [subscribe_runtime_updates](../examples/examples/subscribe_runtime_updates.rs) example for more details.
+11 -8
View File
@@ -38,20 +38,23 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
let hash = api
.tx()
.balances()
.transfer(dest, 123_456_789_012_345)?
.sign_and_submit_default(&signer)
.await?;
println!("Balance transfer extrinsic submitted: {}", hash);
// Submit the `transfer` extrinsic from Alice's account to Bob's.
let dest = AccountKeyring::Bob.to_account_id().into();
// Obtain an extrinsic, calling the "transfer" function in
// the "balances" pallet.
let extrinsic = api.tx().balances().transfer(dest, 123_456_789_012_345)?;
// Sign and submit the extrinsic, returning its hash.
let tx_hash = extrinsic.sign_and_submit_default(&signer).await?;
println!("Balance transfer extrinsic submitted: {}", tx_hash);
Ok(())
}
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is part of subxt.
//
// subxt is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// subxt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-4542a603cc-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
};
// Generate the API from a static metadata path.
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Upon connecting to the target polkadot node, the node's metadata is downloaded (referred to
// as the runtime metadata).
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// Constants are queried from the node's runtime metadata.
// Query the `ExistentialDeposit` constant from the `Balances` pallet.
let existential_deposit = api
// This is the constants query.
.constants()
// Constant from the `Balances` pallet.
.balances()
// Constant name.
.existential_deposit()?;
println!("Existential Deposit: {}", existential_deposit);
Ok(())
}
+1 -1
View File
@@ -21,7 +21,7 @@
//! Download metadata from a running Substrate node using `subxt-cli`:
//!
//! ```bash
//! subxt metadata -f bytes > polkadot_metadata.scale
//! subxt metadata > polkadot_metadata.scale
//! ```
//!
//! Annotate a Rust module with the `subxt` attribute referencing the aforementioned metadata file.
+27
View File
@@ -98,6 +98,17 @@ impl ClientBuilder {
}
/// Creates a new Client.
///
/// # Example
///
/// ```rust
/// use subxt::{ClientBuilder, DefaultConfig};
///
/// let client = ClientBuilder::new()
/// .set_url("wss://rpc.polkadot.io:443")
/// .build::<DefaultConfig>()
/// .await?;
/// ```
pub async fn build<T: Config>(self) -> Result<Client<T>, BasicError> {
let client = if let Some(client) = self.client {
client
@@ -190,6 +201,22 @@ impl<T: Config> Client<T> {
}
/// Create a wrapper for performing runtime updates on this client.
///
/// # Note
///
/// The update client is intended to be used in the background for
/// performing runtime updates, while the API is still in use.
/// Without performing runtime updates the submitted extrinsics may fail.
///
/// # Example
///
/// ```rust
/// let update_client = client.updates();
/// tokio::spawn(async move {
/// let result = update_client.perform_runtime_updates().await;
/// println!("Runtime update finished with result={:?}", result);
/// });
/// ```
pub fn updates(&self) -> UpdateClient<T> {
UpdateClient::new(
self.rpc.clone(),
+52 -1
View File
@@ -14,7 +14,58 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! For working with events.
//! This module exposes the ability to work with events generated by a given block.
//! Subxt can either attempt to statically decode events into known types, or it
//! can hand back details of the raw event without knowing what shape its contents
//! are (this may be useful if we don't know what exactly we're looking for).
//!
//! This module is wrapped by the generated API in `RuntimeAPI::EventsApi`.
//!
//! # Example
//!
//! ## Subscribe to all events
//!
//! Users can subscribe to all emitted events from blocks using `subscribe()`.
//!
//! To subscribe to all events from just the finalized blocks use `subscribe_finalized()`.
//!
//! To obtain the events from a given block use `at()`.
//!
//! ```rust
//! let mut events = api.events().subscribe().await?;
//!
//! while let Some(ev) = events.next().await {
//! // Obtain all events from this block.
//! let ev: subxt::Events<_, _> = ev?;
//! // Print block hash.
//! println!("Event at block hash {:?}", ev.block_hash());
//! // Iterate over all events.
//! let mut iter = ev.iter();
//! while let Some(event_details) = iter.next() {
//! println!("Event details {:?}", event_details);
//! }
//! }
//! ```
//!
//! ## Filter events
//!
//! The subxt exposes the ability to filter events via the `filter_events()` function.
//!
//! The function filters events from the provided tuple. If 1-tuple is provided, the events are
//! returned directly. Otherwise, we'll be given a corresponding tuple of `Option`'s, with exactly
//! one variant populated each time.
//!
//! ```rust
//! let mut events = api
//! .events()
//! .subscribe()
//! .await?
//! .filter_events::<(polkadot::balances::events::Transfer,)>();
//!
//! while let Some(transfer_event) = transfer_events.next().await {
//! println!("Balance transfer event: {transfer_event:?}");
//! }
//! ```
mod decoding;
mod event_subscription;
+14
View File
@@ -15,6 +15,20 @@
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! Create signed or unsigned extrinsics.
//!
//! This modules exposes the extrinsic's parameters and the ability to sign an extrinsic.
//!
//!
//! An extrinsic is submitted with an "signed extra" and "additional" parameters, which can be
//! different for each chain. The trait [ExtrinsicParams] determines exactly which
//! additional and signed extra parameters are used when constructing an extrinsic.
//!
//!
//! The structure [BaseExtrinsicParams] is a base implementation of the trait which
//! configures most of the "signed extra" and "additional" parameters as needed for
//! Polkadot and Substrate nodes. Only the shape of the tip payments differs, leading to
//! [SubstrateExtrinsicParams] and [PolkadotExtrinsicParams] structs which pick an
//! appropriate shape for Substrate/Polkadot chains respectively.
mod params;
mod signer;
+14 -5
View File
@@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! A library to **sub**mit e**xt**rinsics to a
//! [substrate](https://github.com/paritytech/substrate) node via RPC.
#![doc = include_str!("../../docs/subxt.md")]
#![deny(
bad_style,
const_err,
@@ -126,7 +124,13 @@ pub use crate::{
},
};
/// Call trait.
/// Trait to uniquely identify the call (extrinsic)'s identity from the runtime metadata.
///
/// Generated API structures that represent each of the different possible
/// calls to a node each implement this trait.
///
/// When encoding an extrinsic, we use this information to know how to map
/// the call to the specific pallet and call index needed by a particular node.
pub trait Call: Encode {
/// Pallet name.
const PALLET: &'static str;
@@ -139,7 +143,12 @@ pub trait Call: Encode {
}
}
/// Event trait.
/// Trait to uniquely identify the events's identity from the runtime metadata.
///
/// Generated API structures that represent an event implement this trait.
///
/// The trait is utilized to decode emitted events from a block, via obtaining the
/// form of the `Event` from the metadata.
pub trait Event: Decode {
/// Pallet name.
const PALLET: &'static str;
+10 -3
View File
@@ -37,7 +37,7 @@ use std::{
sync::Arc,
};
/// Metadata error.
/// Metadata error originated from inspecting the internal representation of the runtime metadata.
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum MetadataError {
/// Module is not in metadata.
@@ -97,7 +97,7 @@ struct MetadataInner {
cached_storage_hashes: HashCache,
}
/// Runtime metadata.
/// A representation of the runtime metadata received from a node.
#[derive(Clone, Debug)]
pub struct Metadata {
inner: Arc<MetadataInner>,
@@ -307,7 +307,10 @@ impl EventMetadata {
}
}
/// Metadata for specific errors.
/// Metadata for specific errors obtained from the pallet's `PalletErrorMetadata`.
///
/// This holds in memory information regarding the Pallet's name, Error's name, and the underlying
/// metadata representation.
#[derive(Clone, Debug)]
pub struct ErrorMetadata {
pallet: String,
@@ -332,6 +335,10 @@ impl ErrorMetadata {
}
}
/// Error originated from converting a runtime metadata [RuntimeMetadataPrefixed] to
/// the internal [Metadata] representation.
///
/// The runtime metadata is converted when building the [crate::client::Client].
#[derive(Debug, thiserror::Error)]
pub enum InvalidMetadataError {
#[error("Invalid prefix")]
+43
View File
@@ -15,6 +15,49 @@
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! RPC types and client for interacting with a substrate node.
//!
//! This is used behind the scenes by various `subxt` APIs, but can
//! also be used directly.
//!
//! # Examples
//!
//! ## Fetch Storage
//!
//! ```rust
//! use subxt::rpc::Rpc;
//! use subxt::storage::StorageKeyPrefix;
//!
//! // Storage prefix is `twox_128("System") ++ twox_128("ExtrinsicCount")`.
//! let key = StorageKeyPrefix::new::<node_runtime::system::storage::ExtrinsicCount>()
//! .to_storage_key();
//!
//! // Obtain the RPC from a generated API
//! let rpc: &Rpc<_> = api
//! .client
//! .rpc();
//!
//! let result = rpc.storage(&key, None)?;
//! println!("Storage result: {:?}", result);
//! ```
//!
//! ## Fetch Keys
//!
//! ```rust
//! use subxt::rpc::Rpc;
//! use subxt::storage::StorageKeyPrefix;
//! let key = StorageKeyPrefix::new::<polkadot::xcm_pallet::storage::VersionNotifiers>();
//!
//! // Obtain the RPC from a generated API
//! let rpc: &Rpc<_> = api
//! .client
//! .rpc();
//! // Fetch up to 10 keys.
//! let keys = rpc
//! .storage_keys_paged(Some(key), 10, None, None);
//! for key in keys.iter() {
//! println!("Key: 0x{}", hex::encode(&key));
//! }
//! ```
// jsonrpsee subscriptions are interminable.
// Allows `while let status = subscription.next().await {}`
+41 -1
View File
@@ -14,7 +14,47 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! For querying runtime storage.
//! Query the runtime storage using [StorageClient].
//!
//! This module is the core of performing runtime storage queries. While you can
//! work with it directly, it's prefer to use the generated `storage()` interface where
//! possible.
//!
//! The exposed API is performing RPC calls to `state_getStorage` and `state_getKeysPaged`.
//!
//! A runtime storage entry can be of type:
//! - [StorageEntryKey::Plain] for keys constructed just from the prefix
//! `twox_128(pallet) ++ twox_128(storage_item)`
//! - [StorageEntryKey::Map] for mapped keys constructed from the prefix,
//! plus other arguments `twox_128(pallet) ++ twox_128(storage_item) ++ hash(arg1) ++ arg1`
//!
//! # Examples
//!
//! ## Fetch Storage Keys
//!
//! ```rust
//! // Fetch just the keys, returning up to 10 keys.
//! let keys = storage
//! .fetch_keys::<node_runtime::xcm_pallet::storage::VersionNotifiers>(10, None, None)
//! .await?;
//! // Iterate over each key
//! for key in keys.iter() {
//! println!("Key: 0x{}", hex::encode(&key));
//! }
//! ```
//!
//! ## Iterate over Storage
//!
//! ```rust
//! // Iterate over keys and values.
//! let mut iter = storage
//! .iter::<polkadot::xcm_pallet::storage::VersionNotifiers>(None)
//! .await?;
//! while let Some((key, value)) = iter.next().await? {
//! println!("Key: 0x{}", hex::encode(&key));
//! println!("Value: {}", value);
//! }
//! ```
use codec::{
Decode,
+21 -1
View File
@@ -14,7 +14,27 @@
// You should have received a copy of the GNU General Public License
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
//! For performing runtime updates.
//! Perform runtime updates in the background using [UpdateClient].
//!
//! There are cases when the node would perform a runtime update. As a result, the subxt's metadata
//! would be out of sync and the API would not be able to submit valid extrinsics.
//! This API keeps the `RuntimeVersion` and `Metadata` of the client synced with the target node.
//!
//! The runtime update is recommended for long-running clients, or for cases where manually
//! restarting subxt would not be feasible. Even with this, extrinsics submitted during a node
//! runtime update are at risk or failing, as it will take `subxt` a moment to catch up.
//!
//! ## Note
//!
//! Here we use tokio to check for updates in the background, but any runtime can be used.
//!
//! ```rust
//! let update_client = client.updates();
//! tokio::spawn(async move {
//! let result = update_client.perform_runtime_updates().await;
//! println!("Runtime update finished with result={:?}", result);
//! });
//! ```
use crate::{
rpc::{