mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 04:01:10 +00:00
Subxt Guide (#890)
* WIP Starting to write book; extrinsics first pass done * cargo fmt * Ongoing work; events, constants, wip blocks * at_latest() and wip blocks * remove need to import parity-scale-codec crate with Subxt for macro to work * More docs; expanding on setup guide and finish pass of main sections * Tidy and remove example section for now * format book lines to 100chars * Fix example code * cargo fmt * cargo fmt * fix example * Fix typos * fix broken doc links, pub mods * Update Subxt macro docs * can't link to Subxt here * move macro docs to Subxt to make linking better and fix example code * note on macro about docs * cargo fmt * document the no_default_derives macro feature * Address feedback and remove redundant text * address review comments; minor tweaks * WIP add Runtime calls to book * Improve Runtime API docs * expose thing we forgot to expose and doc link fixes
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
// 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.
|
||||
|
||||
// Dev note; I used the following command to normalize and wrap comments:
|
||||
// rustfmt +nightly --config wrap_comments=true,comment_width=100,normalize_comments=true subxt/src/book/mod.rs
|
||||
// It messed up comments in code blocks though, so be prepared to go and fix those.
|
||||
|
||||
//! # The Subxt Guide
|
||||
//!
|
||||
//! Subxt is a library for interacting with Substrate based nodes. It has a focus on **sub**mitting
|
||||
//! e**xt**rinsics, hence the name, however it's also capable of reading blocks, storage, events and
|
||||
//! constants from a node. The aim of this guide is to explain key concepts and get you started with
|
||||
//! using Subxt.
|
||||
//!
|
||||
//! 1. [Features](#features-at-a-glance)
|
||||
//! 2. [Limitations](#limitations)
|
||||
//! 3. [Quick start](#quick-start)
|
||||
//! 4. [Usage](#usage)
|
||||
//!
|
||||
//! ## Features at a glance
|
||||
//!
|
||||
//! Here's a quick overview of the features that Subxt has to offer:
|
||||
//!
|
||||
//! - Subxt allows you to generate a static, type safe interface to a node given some metadata; this
|
||||
//! allows you to catch many errors at compile time rather than runtime.
|
||||
//! - Subxt also makes heavy use of node metadata to encode/decode the data sent to/from it. This
|
||||
//! allows it to target almost any node which can output the correct metadata, and allows it some
|
||||
//! flexibility in encoding and decoding things to account for cross-node differences.
|
||||
//! - Subxt has a pallet-oriented interface, meaning that code you write to talk to some pallet on
|
||||
//! one node will often "Just Work" when pointed at different nodes that use the same pallet.
|
||||
//! - Subxt can work offline; you can generate and sign transactions, access constants from node
|
||||
//! metadata and more, without a network connection. This is all checked at compile time, so you
|
||||
//! can be certain it won't try to establish a network connection if you don't want it to.
|
||||
//! - Subxt can forego the statically generated interface and build transactions, storage queries
|
||||
//! and constant queries using data provided at runtime, rather than queries constructed
|
||||
//! statically.
|
||||
//! - Subxt can be compiled to WASM to run in the browser, allowing it to back Rust based browser
|
||||
//! apps, or even bind to JS apps.
|
||||
//!
|
||||
//! ## Limitations
|
||||
//!
|
||||
//! In various places, you can provide a block hash to access data at a particular block, for
|
||||
//! instance:
|
||||
//!
|
||||
//! - [`crate::storage::StorageClient::at`]
|
||||
//! - [`crate::events::EventsClient::at`]
|
||||
//! - [`crate::blocks::BlocksClient::at`]
|
||||
//! - [`crate::runtime_api::RuntimeApiClient::at`]
|
||||
//!
|
||||
//! However, Subxt is (by default) only capable of properly working with blocks that were produced
|
||||
//! after the most recent runtime update. This is because it uses the most recent metadata given
|
||||
//! back by a node to encode and decode things. It's possible to decode older blocks produced by a
|
||||
//! runtime that emits compatible (currently, V14) metadata by manually setting the metadata used by
|
||||
//! the client using [`crate::client::OnlineClient::set_metadata()`].
|
||||
//!
|
||||
//! Subxt does not support working with blocks produced prior to the runtime update that introduces
|
||||
//! V14 metadata. It may have some success decoding older blocks using newer metadata, but may also
|
||||
//! completely fail to do so.
|
||||
//!
|
||||
//! ## Quick start
|
||||
//!
|
||||
//! Here is a simple but complete example of using Subxt to transfer some tokens from the example
|
||||
//! accounts, Alice to Bob:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../examples/examples/balance_transfer_basic.rs")]
|
||||
//! ```
|
||||
//!
|
||||
//! This example assumes that a Polkadot node is running locally (Subxt endeavors to support all
|
||||
//! recent releases). Typically, to use Subxt to talk to some custom Substrate node (for example a
|
||||
//! parachain node), you'll want to:
|
||||
//!
|
||||
//! 1. [Generate an interface](setup::codegen).
|
||||
//! 2. [Configure and instantiate the client](setup::client).
|
||||
//!
|
||||
//! Follow the above links to learn more about each step.
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! Once Subxt is configured, the next step is interacting with a node. Follow the links
|
||||
//! below to learn more about how to use Subxt for each of the following things:
|
||||
//!
|
||||
//! - [Extrinsics](usage::extrinsics): Subxt can build and submit extrinsics, wait until they are in
|
||||
//! blocks, and retrieve the associated events.
|
||||
//! - [Storage](usage::storage): Subxt can query the node storage.
|
||||
//! - [Events](usage::events): Subxt can read the events emitted for recent blocks.
|
||||
//! - [Constants](usage::constants): Subxt can access the constant values stored in a node, which
|
||||
//! remain the same for a given runtime version.
|
||||
//! - [Blocks](usage::blocks): Subxt can load recent blocks or subscribe to new/finalized blocks,
|
||||
//! reading the extrinsics, events and storage at these blocks.
|
||||
//! - [Runtime APIs](usage::runtime_apis): Subxt can make calls into pallet runtime APIs to retrieve
|
||||
//! data.
|
||||
//!
|
||||
pub mod setup;
|
||||
pub mod usage;
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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.
|
||||
|
||||
//! # Configuring the Subxt client
|
||||
//!
|
||||
//! Subxt ships with two clients, an [offline client](crate::client::OfflineClient) and an [online
|
||||
//! client](crate::client::OnlineClient). These are backed by the traits
|
||||
//! [`crate::client::OfflineClientT`] and [`crate::client::OnlineClientT`], so in theory it's
|
||||
//! possible for users to implement their own clients, although this isn't generally expected.
|
||||
//!
|
||||
//! Both clients are generic over a [`crate::config::Config`] trait, which is the way that we give
|
||||
//! the client certain information about how to interact with a node that isn't otherwise available
|
||||
//! or possible to include in the node metadata. Subxt ships out of the box with two default
|
||||
//! implementations:
|
||||
//!
|
||||
//! - [`crate::config::PolkadotConfig`] for talking to Polkadot nodes, and
|
||||
//! - [`crate::config::SubstrateConfig`] for talking to generic nodes built with Substrate.
|
||||
//!
|
||||
//! The latter will generally work in many cases, but will need modifying if the chain you'd like to
|
||||
//! connect to has altered any of the details mentioned in [the trait](`crate::config::Config`).
|
||||
//!
|
||||
//! In the case of the [`crate::OnlineClient`], we have a few options to instantiate it:
|
||||
//!
|
||||
//! - [`crate::OnlineClient::new()`] to connect to a node running locally.
|
||||
//! - [`crate::OnlineClient::from_url()`] to connect to a node at a specific URL.
|
||||
//! - [`crate::OnlineClient::from_rpc_client()`] to instantiate the client with a custom RPC
|
||||
//! implementation.
|
||||
//!
|
||||
//! The latter accepts anything that implements the low level [`crate::rpc::RpcClientT`] trait; this
|
||||
//! allows you to decide how Subxt will attempt to talk to a node if you'd prefer something other
|
||||
//! than the provided interfaces.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! Defining some custom config based off the default Substrate config:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/setup_client_custom_config.rs")]
|
||||
//! ```
|
||||
//! Writing a custom [`crate::rpc::RpcClientT`] implementation:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/setup_client_custom_rpc.rs")]
|
||||
//! ```
|
||||
//! Creating an [`crate::OfflineClient`]:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/setup_client_offline.rs")]
|
||||
//! ```
|
||||
//!
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
//! # Generating an interface
|
||||
//!
|
||||
//! The simplest way to use Subxt is to generate an interface to a chain that you'd like to interact
|
||||
//! with. This generated interface allows you to build transactions and construct queries to access
|
||||
//! data while leveraging the full type safety of the Rust compiler.
|
||||
//!
|
||||
//! ## The `#[subxt]` macro
|
||||
//!
|
||||
//! The most common way to generate the interface is to use the [`#[subxt]`](crate::subxt) macro.
|
||||
//! Using this macro looks something like:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//! ```
|
||||
//!
|
||||
//! The macro takes a path to some node metadata, and uses that to generate the interface you'll use
|
||||
//! to talk to it. [Go here](crate::subxt) to learn more about the options available to the macro.
|
||||
//!
|
||||
//! To obtain this metadata you'll need for the above, you can use the `subxt` CLI tool to download it
|
||||
//! from a node. The tool can be installed via `cargo`:
|
||||
//!
|
||||
//! ```shell
|
||||
//! cargo install subxt-cli
|
||||
//! ```
|
||||
//!
|
||||
//! And then it can be used to fetch metadata and save it to a file:
|
||||
//!
|
||||
//! ```shell
|
||||
//! # Download and save all of the metadata:
|
||||
//! subxt metadata > metadata.scale
|
||||
//! # Download and save only the pallets you want to generate an interface for:
|
||||
//! subxt metadata --pallets Balances,System > metadata.scale
|
||||
//! ```
|
||||
//!
|
||||
//! Explicitly specifying pallets will cause the tool to strip out all unnecessary metadata and type
|
||||
//! information, making the bundle much smaller in the event that you only need to generate an
|
||||
//! interface for a subset of the available pallets on the node.
|
||||
//!
|
||||
//! ## The CLI tool
|
||||
//!
|
||||
//! Using the [`#[subxt]`](crate::subxt) macro carries some downsides:
|
||||
//!
|
||||
//! - Using it to generate an interface will have a small impact on compile times (though much less of
|
||||
//! one if you only need a few pallets).
|
||||
//! - IDE support for autocompletion and documentation when using the macro interface can be poor.
|
||||
//! - It's impossible to manually look at the generated code to understand and debug things.
|
||||
//!
|
||||
//! If these are an issue, you can manually generate the same code that the macro generates under the hood
|
||||
//! by using the `subxt codegen` command:
|
||||
//!
|
||||
//! ```shell
|
||||
//! # Install the CLI tool if you haven't already:
|
||||
//! cargo install subxt-cli
|
||||
//! # Generate and format rust code, saving it to `interface.rs`:
|
||||
//! subxt codegen | rustfmt > interface.rs
|
||||
//! ```
|
||||
//!
|
||||
//! Use `subxt codegen --help` for more options; many of the options available via the macro are
|
||||
//! also available via the CLI tool, such as the ability to substitute generated types for others,
|
||||
//! or strip out docs from the generated code.
|
||||
//!
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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 modules contains details on setting up Subxt:
|
||||
//!
|
||||
//! - [Codegen](codegen)
|
||||
//! - [Client](client)
|
||||
//!
|
||||
//! Alternately, [go back](super).
|
||||
|
||||
pub mod client;
|
||||
pub mod codegen;
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
//! # Blocks
|
||||
//!
|
||||
//! The [blocks API](crate::blocks::BlocksClient) in Subxt unifies many of the other interfaces, and
|
||||
//! allows you to:
|
||||
//!
|
||||
//! - Access information about specific blocks (see [`crate::blocks::BlocksClient::at()`] and
|
||||
//! [`crate::blocks::BlocksClient::at_latest()`]).
|
||||
//! - Subscribe to [all](crate::blocks::BlocksClient::subscribe_all()),
|
||||
//! [best](crate::blocks::BlocksClient::subscribe_best()) or
|
||||
//! [finalized](crate::blocks::BlocksClient::subscribe_finalized()) blocks as they are produced.
|
||||
//! Prefer to subscribe to finalized blocks unless you know what you're doing.
|
||||
//!
|
||||
//! In either case, you'll end up with [`crate::blocks::Block`]'s, from which you can access various
|
||||
//! information about the block, such a the [header](crate::blocks::Block::header()), [block
|
||||
//! number](crate::blocks::Block::number()) and [body](crate::blocks::Block::body()).
|
||||
//! [`crate::blocks::Block`]'s also provide shortcuts to other Subxt APIs that will operate at the
|
||||
//! given block:
|
||||
//!
|
||||
//! - [storage](crate::blocks::Block::storage()),
|
||||
//! - [events](crate::blocks::Block::events())
|
||||
//! - [runtime APIs](crate::blocks::Block::runtime_api())
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! Given a block, you can [download the block body](crate::blocks::Block::body()) and iterate over
|
||||
//! the extrinsics stored within it using [`crate::blocks::BlockBody::extrinsics()`].
|
||||
//!
|
||||
//! Here's an example in which we subscribe to blocks and print a bunch of information about each
|
||||
//! one:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/blocks_subscribing.rs")]
|
||||
//! ```
|
||||
//!
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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.
|
||||
|
||||
//! # Constants
|
||||
//!
|
||||
//! There are various constants stored in a node; the types and values of these are defined in a
|
||||
//! runtime, and can only change when the runtime is updated. Much like [`super::storage`], we can
|
||||
//! query these using Subxt by taking the following steps:
|
||||
//!
|
||||
//! 1. [Constructing a constant query](#constructing-a-query).
|
||||
//! 2. [Submitting the query to get back the associated value](#submitting-it).
|
||||
//!
|
||||
//! ## Constructing a constant query
|
||||
//!
|
||||
//! We can use the statically generated interface to build constant queries:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
//! let constant_query = polkadot::constants().system().block_length();
|
||||
//! ```
|
||||
//!
|
||||
//! Alternately, we can dynamically construct a constant query:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//! use subxt::dynamic::Value;
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id();
|
||||
//! let storage_query = subxt::dynamic::constant("System", "BlockLength");
|
||||
//! ```
|
||||
//!
|
||||
//! Static queries also have a static return type, so the constant is decoded appropriately. In
|
||||
//! addition, they are validated at runtime to ensure that they align with the current node state.
|
||||
//! Dynamic queries must be decoded into some static type manually, or into the dynamic
|
||||
//! [`crate::dynamic::Value`] type.
|
||||
//!
|
||||
//! ## Submitting it
|
||||
//!
|
||||
//! Constant queries are handed to Subxt via [`crate::constants::ConstantsClient::at()`]. It's worth
|
||||
//! noting that constant values are pulled directly out of the node metadata which Subxt has
|
||||
//! already acquired, and so this function requires no network access and is available from a
|
||||
//! [`crate::OfflineClient`].
|
||||
//!
|
||||
//! Here's an example using a static query:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/constants_static.rs")]
|
||||
//! ```
|
||||
//! And here's one using a dynamic query:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/constants_dynamic.rs")]
|
||||
//! ```
|
||||
//!
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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.
|
||||
|
||||
//! # Events
|
||||
//!
|
||||
//! In the process of adding extrinsics to a block, they are executed. When extrinsics are executed,
|
||||
//! they normally produce events describing what's happening (at the very least, an event dictating whether
|
||||
//! the extrinsic has succeeded or failed). The node may also emit some events of its own as the block is
|
||||
//! processed.
|
||||
//!
|
||||
//! Events live in a single location in node storage which is overwritten at each block. Normal nodes tend to
|
||||
//! keep a snapshot of the state at a small number of previous blocks, so you can sometimes access
|
||||
//! older events by using [`crate::events::EventsClient::at()`] and providing an older block hash.
|
||||
//!
|
||||
//! When we submit extrinsics using Subxt, methods like [`crate::tx::TxProgress::wait_for_finalized_success()`]
|
||||
//! return [`crate::blocks::ExtrinsicEvents`], which can be used to iterate and inspect the events produced
|
||||
//! for a specific extrinsic. We can also access _all_ of the events produced in a single block using one of these
|
||||
//! two interfaces:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! # #[tokio::main]
|
||||
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! use subxt::client::OnlineClient;
|
||||
//! use subxt::config::PolkadotConfig;
|
||||
//!
|
||||
//! // Create client:
|
||||
//! let client = OnlineClient::<PolkadotConfig>::new().await?;
|
||||
//!
|
||||
//! // Get events from the latest block (use .at() to specify a block hash):
|
||||
//! let events = client.blocks().at_latest().await?.events().await?;
|
||||
//! // We can use this shorthand too:
|
||||
//! let events = client.events().at_latest().await?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Once we've loaded our events, we can iterate all events or search for specific events via
|
||||
//! methods like [`crate::events::Events::iter()`] and [`crate::events::Events::find()`]. See
|
||||
//! [`crate::events::Events`] and [`crate::events::EventDetails`] for more information.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! Here's an example which puts this all together:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/events.rs")]
|
||||
//! ```
|
||||
//!
|
||||
@@ -0,0 +1,175 @@
|
||||
// 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.
|
||||
|
||||
//! # Extrinsics
|
||||
//!
|
||||
//! Extrinsics define function calls and their parameters, and are the only way that you can change
|
||||
//! the state of the blockchain. Submitting extrinsics to a node is one of the core features of
|
||||
//! Subxt, and generally consists of the following steps:
|
||||
//!
|
||||
//! 1. [Constructing an extrinsic payload to submit](#constructing-an-extrinsic-payload).
|
||||
//! 2. [Signing it](#signing-it).
|
||||
//! 3. [Submitting it (optionally with some additional parameters)](#submitting-it).
|
||||
//!
|
||||
//! We'll look at each of these steps in turn.
|
||||
//!
|
||||
//! > As a side note, an _extrinsic_ is anything that can be added to a block, and a _transaction_
|
||||
//! > is an extrinsic that is submitted from a particular user (and is therefore also signed). Subxt
|
||||
//! > can construct unsigned extrinsics, but overwhelmingly you'll need to sign them, and so the
|
||||
//! > documentation tends to use the terms _extrinsic_ and _transaction_ interchangeably.
|
||||
//!
|
||||
//! ## Constructing an extrinsic payload
|
||||
//!
|
||||
//! We can use the statically generated interface to build extrinsic payloads:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
//! let remark = "Hello there".as_bytes().to_vec();
|
||||
//! let extrinsic_payload = polkadot::tx().system().remark(remark);
|
||||
//! ```
|
||||
//!
|
||||
//! > If you're not sure what types to import and use to build a given payload, you can use the
|
||||
//! > `subxt` CLI tool to generate the interface by using something like `subxt codegen | rustfmt >
|
||||
//! > interface.rs`, to see what types and things are available (or even just to use directly
|
||||
//! > instead of the [`#[subxt]`](crate::subxt) macro).
|
||||
//!
|
||||
//! Alternately, we can dynamically construct an extrinsic payload. This will not be type checked or
|
||||
//! validated until it's submitted:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use subxt::dynamic::Value;
|
||||
//!
|
||||
//! let extrinsic_payload = subxt::dynamic::tx("System", "remark", vec![
|
||||
//! Value::from_bytes("Hello there")
|
||||
//! ]);
|
||||
//! ```
|
||||
//!
|
||||
//! The [`crate::dynamic::Value`] type is a dynamic type much like a `serde_json::Value` but instead
|
||||
//! represents any type of data that can be SCALE encoded or decoded. It can be serialized,
|
||||
//! deserialized and parsed from/to strings.
|
||||
//!
|
||||
//! A valid extrinsic payload is just something that implements the [`crate::tx::TxPayload`] trait;
|
||||
//! you can implement this trait on your own custom types if the built-in ones are not suitable for
|
||||
//! your needs.
|
||||
//!
|
||||
//! ## Signing it
|
||||
//!
|
||||
//! You'll normally need to sign an extrinsic to prove that it originated from an account that you
|
||||
//! control. To do this, you will typically first create an [`crate::tx::Signer`], which tells Subxt
|
||||
//! who the extrinsic is from, and takes care of signing the relevant details to prove this.
|
||||
//!
|
||||
//! Subxt provides a [`crate::tx::PairSigner`] which implements this trait (if the
|
||||
//! `substrate-compat` feature is enabled) which accepts any valid [`sp_core::Pair`] and uses that
|
||||
//! to sign transactions:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use subxt::tx::PairSigner;
|
||||
//! use sp_core::Pair;
|
||||
//! use subxt::config::PolkadotConfig;
|
||||
//!
|
||||
//! // Get hold of a `Signer` given a test account:
|
||||
//! let pair = sp_keyring::AccountKeyring::Alice.pair();
|
||||
//! let signer = PairSigner::<PolkadotConfig,_>::new(pair);
|
||||
//!
|
||||
//! // Or generate an `sr25519` keypair to use:
|
||||
//! let (pair, _, _) = sp_core::sr25519::Pair::generate_with_phrase(Some("password"));
|
||||
//! let signer = PairSigner::<PolkadotConfig,_>::new(pair);
|
||||
//! ```
|
||||
//!
|
||||
//! See the [`sp_core::Pair`] docs for more ways to generate them.
|
||||
//!
|
||||
//! If this isn't suitable/available, you can either implement [`crate::tx::Signer`] yourself to use
|
||||
//! custom signing logic, or you can use some external signing logic, like so:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! # #[tokio::main]
|
||||
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! use subxt::client::OnlineClient;
|
||||
//! use subxt::config::PolkadotConfig;
|
||||
//! use subxt::dynamic::Value;
|
||||
//!
|
||||
//! // Create client:
|
||||
//! let client = OnlineClient::<PolkadotConfig>::new().await?;
|
||||
//!
|
||||
//! // Create a dummy extrinsic payload to sign:
|
||||
//! let payload = subxt::dynamic::tx("System", "remark", vec![
|
||||
//! Value::from_bytes("Hello there")
|
||||
//! ]);
|
||||
//!
|
||||
//! // Construct the extrinsic but don't sign it. You need to provide the nonce
|
||||
//! // here, or can use `create_partial_signed` to fetch the correct nonce.
|
||||
//! let partial_extrinsic = client.tx().create_partial_signed_with_nonce(
|
||||
//! &payload,
|
||||
//! 0,
|
||||
//! Default::default()
|
||||
//! )?;
|
||||
//!
|
||||
//! // Fetch the payload that needs to be signed:
|
||||
//! let signer_payload = partial_extrinsic.signer_payload();
|
||||
//!
|
||||
//! // ... At this point, we can hand off the `signer_payload` to be signed externally.
|
||||
//! // Ultimately we need to be given back a `signature` (or really, anything
|
||||
//! // that can be SCALE encoded) and an `address`:
|
||||
//! let signature;
|
||||
//! let address;
|
||||
//! # use subxt::tx::Signer;
|
||||
//! # let pair = sp_keyring::AccountKeyring::Alice.pair();
|
||||
//! # let signer = subxt::tx::PairSigner::<PolkadotConfig,_>::new(pair);
|
||||
//! # signature = signer.sign(&signer_payload);
|
||||
//! # address = signer.address();
|
||||
//!
|
||||
//! // Now we can build an extrinsic, which one can call `submit` or `submit_and_watch`
|
||||
//! // on to submit to a node and optionally watch the status.
|
||||
//! let extrinsic = partial_extrinsic.sign_with_address_and_signature(
|
||||
//! &address,
|
||||
//! &signature
|
||||
//! );
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Submitting it
|
||||
//!
|
||||
//! Once we are able to sign the extrinsic, we need to submit it.
|
||||
//!
|
||||
//! ### The high level API
|
||||
//!
|
||||
//! The highest level approach to doing this is to call
|
||||
//! [`crate::tx::TxClient::sign_and_submit_then_watch_default`]. This hands back a
|
||||
//! [`crate::tx::TxProgress`] struct which will monitor the transaction status. We can then call
|
||||
//! [`crate::tx::TxProgress::wait_for_finalized_success()`] to wait for this transaction to make it
|
||||
//! into a finalized block, check for an `ExtrinsicSuccess` event, and then hand back the events for
|
||||
//! inspection. This looks like:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/balance_transfer_basic.rs")]
|
||||
//! ```
|
||||
//! ### Providing extrinsic parameters
|
||||
//!
|
||||
//! If you'd like to provide extrinsic parameters (such as mortality), you can use
|
||||
//! [`crate::tx::TxClient::sign_and_submit_then_watch`] instead, and provide parameters for your
|
||||
//! chain:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/balance_transfer_with_params.rs")]
|
||||
//! ```
|
||||
//! This example doesn't wait for the extrinsic to be included in a block; it just submits it and
|
||||
//! hopes for the best!
|
||||
//!
|
||||
//! ### Custom handling of transaction status updates
|
||||
//!
|
||||
//! If you'd like more control or visibility over exactly which status updates are being emitted for
|
||||
//! the transaction, you can monitor them as they are emitted and react however you choose:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/balance_transfer_status_stream.rs")]
|
||||
//! ```
|
||||
//! Take a look at the API docs for [`crate::tx::TxProgress`], [`crate::tx::TxStatus`] and
|
||||
//! [`crate::tx::TxInBlock`] for more options.
|
||||
//!
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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 modules contains examples of using Subxt; follow the links for more:
|
||||
//!
|
||||
//! - [Extrinsics](extrinsics)
|
||||
//! - [Storage](storage)
|
||||
//! - [Events](events)
|
||||
//! - [Constants](constants)
|
||||
//! - [Blocks](blocks)
|
||||
//! - [Runtime APIs](runtime_apis)
|
||||
//!
|
||||
//! Alternately, [go back](super).
|
||||
|
||||
pub mod blocks;
|
||||
pub mod constants;
|
||||
pub mod events;
|
||||
pub mod extrinsics;
|
||||
pub mod runtime_apis;
|
||||
pub mod storage;
|
||||
@@ -0,0 +1,81 @@
|
||||
// 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.
|
||||
|
||||
//! # Runtime API interface
|
||||
//!
|
||||
//! The Runtime API interface allows Subxt to call runtime APIs exposed by certain pallets in order
|
||||
//! to obtain information. Much like [`super::storage`] and [`super::extrinsics`], Making a runtime
|
||||
//! call to a node and getting the response back takes the following steps:
|
||||
//!
|
||||
//! 1. [Constructing a runtime call](#constructing-a-runtime-call)
|
||||
//! 2. [Submitting it to get back the response](#submitting-it)
|
||||
//!
|
||||
//! **Note:** Runtime APIs are only available when using V15 metadata, which is currently unstable.
|
||||
//! You'll need to use `subxt metadata --version unstable` command to download the unstable V15 metadata,
|
||||
//! and activate the `unstable-metadata` feature in Subxt for it to also use this metadata from a node. The
|
||||
//! metadata format is unstable because it may change and break compatibility with Subxt at any moment, so
|
||||
//! use at your own risk.
|
||||
//!
|
||||
//! ## Constructing a runtime call
|
||||
//!
|
||||
//! We can use the statically generated interface to build runtime calls:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
//! let runtime_call = polkadot::apis().metadata().metadata_versions();
|
||||
//! ```
|
||||
//!
|
||||
//! Alternately, we can dynamically construct a runtime call:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//! use subxt::dynamic::Value;
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id();
|
||||
//! let runtime_call = subxt::dynamic::runtime_api_call(
|
||||
//! "Metadata_metadata_versions",
|
||||
//! Vec::<Value<()>>::new()
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! All valid runtime calls implement [`crate::runtime_api::RuntimeApiPayload`], a trait which
|
||||
//! describes how to encode the runtime call arguments and what return type to decode from the
|
||||
//! response.
|
||||
//!
|
||||
//! ## Submitting it
|
||||
//!
|
||||
//! Runtime calls can be handed to [`crate::runtime_api::RuntimeApi::call()`], which will submit
|
||||
//! them and hand back the associated response.
|
||||
//!
|
||||
//! ### Making a static Runtime API call
|
||||
//!
|
||||
//! The easiest way to make a runtime API call is to use the statically generated interface.
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/runtime_apis_static.rs")]
|
||||
//! ```
|
||||
//!
|
||||
//! ### Making a dynamic Runtime API call
|
||||
//!
|
||||
//! If you'd prefer to construct the call at runtime, you can do this using the
|
||||
//! [`crate::dynamic::runtime_api_call`] method.
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/runtime_apis_dynamic.rs")]
|
||||
//! ```
|
||||
//!
|
||||
//! ### Making a raw call
|
||||
//!
|
||||
//! This is generally discouraged in favour of one of the above, but may be necessary (especially if
|
||||
//! the node you're talking to does not yet serve V15 metadata). Here, you must manually encode
|
||||
//! the argument bytes and manually provide a type for the response bytes to be decoded into.
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/runtime_apis_raw.rs")]
|
||||
//! ```
|
||||
//!
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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.
|
||||
|
||||
//! # Storage
|
||||
//!
|
||||
//! A Substrate based chain has storage, whose values are determined by the extrinsics added to past
|
||||
//! blocks. Subxt allows you to query the storage of a node, which consists of the following steps:
|
||||
//!
|
||||
//! 1. [Constructing a storage query](#constructing-a-storage-query).
|
||||
//! 2. [Submitting the query to get back the associated values](#submitting-it).
|
||||
//!
|
||||
//! ## Constructing a storage query
|
||||
//!
|
||||
//! We can use the statically generated interface to build storage queries:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id().into();
|
||||
//! let storage_query = polkadot::storage().system().account(&account);
|
||||
//! ```
|
||||
//!
|
||||
//! Alternately, we can dynamically construct a storage query. This will not be type checked or
|
||||
//! validated until it's submitted:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//! use subxt::dynamic::Value;
|
||||
//!
|
||||
//! let account = AccountKeyring::Alice.to_account_id();
|
||||
//! let storage_query = subxt::dynamic::storage("System", "Account", vec![
|
||||
//! Value::from_bytes(account)
|
||||
//! ]);
|
||||
//! ```
|
||||
//!
|
||||
//! As well as accessing specific entries, some storage locations can also be iterated over (such as
|
||||
//! the map of account information). To do this, suffix `_root` onto the query constructor (this
|
||||
//! will only be available on static constructors when iteration is actually possible):
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use sp_keyring::AccountKeyring;
|
||||
//!
|
||||
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
|
||||
//! pub mod polkadot {}
|
||||
//!
|
||||
//! // A static query capable of iterating over accounts:
|
||||
//! let storage_query = polkadot::storage().system().account_root();
|
||||
//! // A dynamic query to do the same:
|
||||
//! let storage_query = subxt::dynamic::storage_root("System", "Account");
|
||||
//! ```
|
||||
//!
|
||||
//! All valid storage queries implement [`crate::storage::StorageAddress`]. As well as describing
|
||||
//! how to build a valid storage query, this trait also has some associated types that determine the
|
||||
//! shape of the result you'll get back, and determine what you can do with it (ie, can you iterate
|
||||
//! over storage entries using it).
|
||||
//!
|
||||
//! Static queries set appropriate values for these associated types, and can therefore only be used
|
||||
//! where it makes sense. Dynamic queries don't know any better and can be used in more places, but
|
||||
//! may fail at runtime instead if they are invalid in those places.
|
||||
//!
|
||||
//! ## Submitting it
|
||||
//!
|
||||
//! Storage queries can be handed to various functions in [`crate::storage::Storage`] in order to
|
||||
//! obtain the associated values (also referred to as storage entries) back.
|
||||
//!
|
||||
//! ### Fetching storage entries
|
||||
//!
|
||||
//! The simplest way to access storage entries is to construct a query and then call either
|
||||
//! [`crate::storage::Storage::fetch()`] or [`crate::storage::Storage::fetch_or_default()`] (the
|
||||
//! latter will only work for storage queries that have a default value when empty):
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/storage_fetch.rs")]
|
||||
//! ```
|
||||
//! For completeness, below is an example using a dynamic query instead. The return type from a
|
||||
//! dynamic query is a [`crate::dynamic::DecodedValueThunk`], which can be decoded into a
|
||||
//! [`crate::dynamic::Value`], or else the raw bytes can be accessed instead.
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/storage_fetch_dynamic.rs")]
|
||||
//! ```
|
||||
//! ### Iterating storage entries
|
||||
//!
|
||||
//! Many storage entries are maps of values; as well as fetching individual values, it's possible to
|
||||
//! iterate over all of the values stored at that location:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/storage_iterating.rs")]
|
||||
//! ```
|
||||
//! Here's the same logic but using dynamically constructed values instead:
|
||||
//!
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
#![doc = include_str!("../../../../examples/examples/storage_iterating_dynamic.rs")]
|
||||
//! ```
|
||||
//! ### Advanced
|
||||
//!
|
||||
//! For more advanced use cases, have a look at [`crate::storage::Storage::fetch_raw`] and
|
||||
//! [`crate::storage::Storage::fetch_keys`]. Both of these take raw bytes as arguments, which can be
|
||||
//! obtained from a [`crate::storage::StorageAddress`] by using
|
||||
//! [`crate::storage::StorageClient::address_bytes()`] or
|
||||
//! [`crate::storage::StorageClient::address_root_bytes()`].
|
||||
//!
|
||||
Reference in New Issue
Block a user