Example: How to connect to parachain (#1043)

* parachain rpc lists

* guide almost done

* add the 3rd config

* subscribe to block with configs

* delete table file

* spaces instead of tabs

* remove original ajuna example

* zombienet setup

* nft minting example

* include port, use different names

* link the example from the book

* format book

* add config creation to book, simplify example structure

* fix the nft creation script

* fix doc ref

* fixing links to foreign crates

* fix table formatting

* include nits

* move more docs to book, and simplify parachain-example

* another pass over docs and link to exampels from guide

* nit: adjust comment to numbers

* teeny README fix for parachain-example

* fix command in readme

* add CI for examples and fix parachain-example bug I left in

* add target arch

* cargo fmt

* make CI not fail

* remove index from docs

---------

Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
Tadeo Hepperle
2023-07-21 13:55:48 +02:00
committed by GitHub
parent fd8f60c8a9
commit 8b23b2b83c
19 changed files with 6123 additions and 77 deletions
+31 -3
View File
@@ -24,7 +24,7 @@ env:
WASM_BINDGEN_TEST_TIMEOUT: 60
jobs:
build:
check:
name: Cargo check
runs-on: ubuntu-latest
steps:
@@ -83,6 +83,34 @@ jobs:
- name: Cargo hack; check each feature/crate on its own
run: cargo hack --exclude subxt --exclude subxt-signer --exclude subxt-lightclient --exclude-all-features --each-feature check --workspace
# Check examples, which aren't a part of the workspace and so are otherwise missed:
- name: Cargo check examples
run: |
cargo check --manifest-path examples/parachain-example/Cargo.toml
wasm_check:
name: Cargo check (WASM)
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install Rust stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
target: wasm32-unknown-unknown
override: true
- name: Rust Cache
uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1
# Check WASM examples, which aren't a part of the workspace and so are otherwise missed:
- name: Cargo check WASM examples
run: |
cargo check --manifest-path examples/wasm-example/Cargo.toml --target wasm32-unknown-unknown
fmt:
name: Cargo fmt
runs-on: ubuntu-latest
@@ -141,7 +169,7 @@ jobs:
command: test
args: --doc
nonwasm_tests:
tests:
name: "Test non-wasm"
runs-on: ubuntu-latest-16-cores
steps:
@@ -175,7 +203,7 @@ jobs:
command: nextest
args: run --workspace
nonwasm_light_client_tests:
light_client_tests:
name: "Test Light Client"
runs-on: ubuntu-latest-16-cores
timeout-minutes: 25
+2
View File
@@ -4,3 +4,5 @@
cargo-timing*
/examples/wasm-example/dist
/examples/wasm-example/target
/examples/parachain-example/target
/examples/parachain-example/metadata/target
+2 -1
View File
@@ -20,7 +20,8 @@ exclude = [
"testing/wasm-rpc-tests",
"testing/wasm-lightclient-tests",
"signer/wasm-tests",
"examples/wasm-example"
"examples/wasm-example",
"examples/parachain-example"
]
resolver = "2"
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "parachain-example"
version = "0.1.0"
edition = "2021"
[workspace]
[dependencies]
subxt = { path = "../../subxt" }
subxt-signer = { path = "../../signer", features = ["subxt"] }
futures = { version = "0.3.27", default-features = false, features = ["std"] }
tokio = { version = "1.28", features = ["macros", "time", "rt-multi-thread"] }
sp-core = "21.0.0"
sp-runtime = "24.0.0"
codec = { package = "parity-scale-codec", version = "3.4.0", default-features = false }
scale-decode = "0.7.0"
scale-encode = "0.3.0"
+78
View File
@@ -0,0 +1,78 @@
# parachain-example
This example showcases working with Subxt and Zombienet to try out connecting to a locally deployed parachain, here
["Statemint"](https://parachains.info/details/statemint), also known as "Asset Hub".
## Running the example
### 1. Install `zombienet`
[Zombienet](https://github.com/paritytech/zombienet) is a tool for quickly spinning up a (local) blockchain
network. We will use it to start up a local Asset Hub for us.
Please follow the install guide in the [zombienet github repo](https://github.com/paritytech/zombienet) to
install it.
### 2. `polkadot`
We need a relay chain. Build the polkadot binary from the [polkadot github repo](https://github.com/paritytech/polkadot)
and install it in your path:
```txt
git clone https://github.com/paritytech/polkadot.git
cd polkadot
cargo install --path .
```
### 3. `polkadot-parachain`
The Asset Hub is part of the [cumulus github repo](https://github.com/paritytech/cumulus), an SDK for developing
parachains. Building the cumulus workspace produces a binary called `polkadot-parachain` which can be used to run
Asset Hub nodes.
```txt
git clone https://github.com/paritytech/cumulus.git
cd cumulus
cargo install --path polkadot-parachain
```
### 4. Run the parachain locally
With these binaries installed, Zombienet can now get the parachain running locally from a configuration file, `asset-hub-zombienet.toml`
in this case. We need to have at least 2 validator nodes running via the `polkadot` binary, and an Asset Hub node running via the
`polkadot-parachain` binary. Zombienet starts these up, and gets the parachain registered with the validator nodes for us. To do that,
run:
```txt
zombienet -p native spawn asset-hub-zombienet.toml
```
Zombienet uses Kubernetes by default, but we can use it without Kubernetes by providing the `-p native` flag.
You might have noticed that we use `chain = "rococo-local"` in the `asset-hub-zombienet.toml` file for the relay chain. This is just to
make the epoch time shorter and should have no effect on your interactions with the parachain. Polkadot / Kusama / Rococo have different
epoch times of `24h` / `2h` / `2min` respectively.
### 5. Run the example
The parachain is only registered after the first epoch. So after the previous step, we need to wait 2 minutes until the parachain becomes
interactive and produces blocks. At this point, we can run:
```
cargo run --bin parachain-example
```
To run our example code.
## Dev notes
We can obtain the metadata for Statemint via the [subxt cli](https://crates.io/crates/subxt-cli) tool, like so:
```txt
subxt metadata --url wss://polkadot-asset-hub-rpc.polkadot.io:443 > statemint_metadata.scale
```
It is important to explicitly specify the port as `443`.
One way to find a suitable URL to obtain this from is by looking through the sidebar on [Polkadot.js](https://polkadot.js.org/apps/)
to find the Asset Hub entry, and seeing which RPC node URLs it uses.
@@ -0,0 +1,26 @@
[relaychain]
default_image = "docker.io/parity/polkadot:latest"
default_command = "polkadot"
default_args = ["-lparachain=debug"]
chain = "rococo-local"
[[relaychain.nodes]]
name = "alice"
validator = true
[[relaychain.nodes]]
name = "bob"
validator = true
[[parachains]]
id = 100
chain = "asset-hub-polkadot-local"
[parachains.collator]
name = "collator01"
image = "docker.io/parity/polkadot-parachain:latest"
ws_port = 42069
command = "polkadot-parachain"
args = ["-lparachain=debug"]
+90
View File
@@ -0,0 +1,90 @@
use subxt::{
Config, PolkadotConfig, SubstrateConfig,
utils::{AccountId32, MultiAddress},
OnlineClient,
};
use subxt_signer::sr25519::dev::{self};
#[subxt::subxt(runtime_metadata_path = "statemint_metadata.scale")]
pub mod statemint {}
/// Custom config that works with Statemint:
pub enum StatemintConfig {}
impl Config for StatemintConfig {
type Hash = <PolkadotConfig as Config>::Hash;
type AccountId = <PolkadotConfig as Config>::AccountId;
type Address = <PolkadotConfig as Config>::Address;
type Signature = <PolkadotConfig as Config>::Signature;
type Hasher = <PolkadotConfig as Config>::Hasher;
type Header = <PolkadotConfig as Config>::Header;
type ExtrinsicParams = <SubstrateConfig as Config>::ExtrinsicParams;
}
#[tokio::main]
pub async fn main() {
if let Err(err) = run().await {
eprintln!("{err}");
}
}
async fn run() -> Result<(), Box<dyn std::error::Error>> {
// (the port 42069 is specified in the asset-hub-zombienet.toml)
let api = OnlineClient::<StatemintConfig>::from_url("ws://127.0.0.1:42069").await?;
println!("Connection with parachain established.");
let alice: MultiAddress<AccountId32, ()> = dev::alice().public_key().into();
let alice_pair_signer = dev::alice();
const COLLECTION_ID: u32 = 12;
const NTF_ID: u32 = 234;
// create a collection with id `12`
let collection_creation_tx = statemint::tx()
.uniques()
.create(COLLECTION_ID, alice.clone());
let _collection_creation_events = api
.tx()
.sign_and_submit_then_watch_default(&collection_creation_tx, &alice_pair_signer)
.await
.map(|e| {
println!("Collection creation submitted, waiting for transaction to be finalized...");
e
})?
.wait_for_finalized_success()
.await?;
println!("Collection created.");
// create an nft in that collection with id `234`
let nft_creation_tx = statemint::tx()
.uniques()
.mint(COLLECTION_ID, NTF_ID, alice.clone());
let _nft_creation_events = api
.tx()
.sign_and_submit_then_watch_default(&nft_creation_tx, &alice_pair_signer)
.await
.map(|e| {
println!("NFT creation submitted, waiting for transaction to be finalized...");
e
})?
.wait_for_finalized_success()
.await?;
println!("NFT created.");
// check in storage, that alice is the official owner of the NFT:
let nft_owner_storage_query = statemint::storage().uniques().asset(COLLECTION_ID, NTF_ID);
let nft_storage_details = api
.storage()
.at_latest()
.await?
.fetch(&nft_owner_storage_query)
.await?
.ok_or("The NFT should have an owner (alice)")?;
// make sure that alice is the owner of the NFT:
assert_eq!(nft_storage_details.owner, dev::alice().public_key().into());
println!("Storage Item Details: {:?}", nft_storage_details);
Ok(())
}
Binary file not shown.
+973 -53
View File
File diff suppressed because it is too large Load Diff
+23
View File
@@ -0,0 +1,23 @@
# wasm-example
This is a small WASM app using the Yew UI framework to showcase how to use Subxt's features in a WASM environment.
To run the app locally we first install Trunk, a WASM bundler:
```
cargo install --locked trunk
```
You need to have a local polkadot/substrate node with it's JSON-RPC HTTP server running at 127.0.0.1:9933 in order for the examples to be working.
If you have a `polkadot` binary already, running this should be sufficient:
```
polkadot --dev
```
Then, in another terminal, run the app locally with:
```
trunk serve --open
```
-12
View File
@@ -1,15 +1,3 @@
//! This is a small WASM app using the Yew UI framework showcasing how to use Subxt's features in a WASM environment.
//!
//! To run the app locally use Trunk, a WASM bundler:
//! ```
//! cargo install --locked trunk
//! ```
//! Run the app locally:
//! ```
//! trunk serve --open
//! ```
//! You need to have a local polkadot/substrate node with it's JSON-RPC HTTP server running at 127.0.0.1:9933 in order for the examples to be working.
//! Also make sure your browser supports WASM.
use futures::{self, FutureExt};
use yew::prelude::*;
+95
View File
@@ -0,0 +1,95 @@
use codec::Encode;
use primitive_types::H256;
use subxt::config::{Config, ExtrinsicParams};
// We don't need to construct this at runtime,
// so an empty enum is appropriate:
pub enum StatemintConfig {}
impl Config for StatemintConfig {
type Hash = subxt::utils::H256;
type AccountId = subxt::utils::AccountId32;
type Address = subxt::utils::MultiAddress<Self::AccountId, ()>;
type Signature = subxt::utils::MultiSignature;
type Hasher = subxt::config::substrate::BlakeTwo256;
type Header = subxt::config::substrate::SubstrateHeader<u32, Self::Hasher>;
type ExtrinsicParams = StatemintExtrinsicParams;
}
#[derive(Encode, Debug, Clone, Eq, PartialEq)]
pub struct StatemintExtrinsicParams {
extra_params: StatemintExtraParams,
additional_params: StatemintAdditionalParams,
}
impl ExtrinsicParams<H256> for StatemintExtrinsicParams {
// We need these additional values that aren't otherwise
// provided. Calls like api.tx().sign_and_submit_then_watch()
// allow the user to provide an instance of these, so it's wise
// to give this a nicer interface in reality:
type OtherParams = (
sp_core::H256,
sp_runtime::generic::Era,
ChargeAssetTxPayment,
);
// Gather together all of the params we will need to encode:
fn new(
spec_version: u32,
tx_version: u32,
nonce: u64,
genesis_hash: H256,
other_params: Self::OtherParams,
) -> Self {
let (mortality_hash, era, charge) = other_params;
let extra_params = StatemintExtraParams { era, nonce, charge };
let additional_params = StatemintAdditionalParams {
spec_version,
tx_version,
genesis_hash,
mortality_hash,
};
Self {
extra_params,
additional_params,
}
}
// Encode the relevant params when asked:
fn encode_extra_to(&self, v: &mut Vec<u8>) {
self.extra_params.encode_to(v);
}
fn encode_additional_to(&self, v: &mut Vec<u8>) {
self.additional_params.encode_to(v);
}
}
#[derive(Encode, Debug, Clone, Eq, PartialEq)]
pub struct StatemintExtraParams {
era: sp_runtime::generic::Era,
nonce: u64,
charge: ChargeAssetTxPayment,
}
#[derive(Encode, Debug, Clone, Eq, PartialEq)]
pub struct ChargeAssetTxPayment {
#[codec(compact)]
tip: u128,
asset_id: Option<u32>,
}
#[derive(Encode, Debug, Clone, Eq, PartialEq)]
pub struct StatemintAdditionalParams {
spec_version: u32,
tx_version: u32,
genesis_hash: sp_core::H256,
mortality_hash: sp_core::H256,
}
#[tokio::main]
async fn main() {
// With the config defined, it can be handed to Subxt as follows:
let _client_fut = subxt::OnlineClient::<StatemintConfig>::new();
}
+12 -2
View File
@@ -71,8 +71,9 @@
//! 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).
//! 1. [Generate an interface](setup::codegen)
//! 2. [Create a config](setup::config)
//! 3. [Use the config to instantiate the client](setup::client)
//!
//! Follow the above links to learn more about each step.
//!
@@ -92,5 +93,14 @@
//! - [Runtime APIs](usage::runtime_apis): Subxt can make calls into pallet runtime APIs to retrieve
//! data.
//!
//! ## Examples
//!
//! Some complete, self contained examples which are not a part of this guide:
//!
//! - [`parachain-example`](https://github.com/paritytech/subxt/tree/master/examples/parachain-example) is an example
//! which uses Zombienet to spawn a parachain locally, and then connects to it using Subxt.
//! - [`wasm-example`](https://github.com/paritytech/subxt/tree/master/examples/wasm-example) is an example of writing
//! a Rust app that contains a Yew based UI, uses Subxt to interact with a chain, and compiles to WASM in order to
//! run entirely in the browser.
pub mod setup;
pub mod usage;
+6 -4
View File
@@ -11,14 +11,16 @@
//!
//! 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:
//! or possible to include in the node metadata.
//!
//! The [`crate::config::Config`] trait mimics the `frame_system::Config` trait and
//! 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`).
//! The latter will generally work in many cases, but [may need special customization](super::config) if
//! the node differs in any of the types the [`Config`](crate::config::Config) trait wants to know about.
//!
//! In the case of the [`crate::OnlineClient`], we have a few options to instantiate it:
//!
+160
View File
@@ -0,0 +1,160 @@
//! # Creating a Config
//!
//! Subxt requires you to provide a type implementing [`crate::config::Config`] in order to connect to a node.
//! The [`crate::config::Config`] trait for the most part mimics the `frame_system::Config` trait.
//! For most use cases, you can just use one of the following Configs shipped with Subxt:
//!
//! - [`PolkadotConfig`](crate::config::PolkadotConfig) for talking to Polkadot nodes, and
//! - [`SubstrateConfig`](crate::config::SubstrateConfig) for talking to generic nodes built with Substrate.
//!
//! # How to create a Config for a custom chain?
//!
//! Some chains may use config that is not compatible with our [`PolkadotConfig`](crate::config::PolkadotConfig) or
//! [`SubstrateConfig`](crate::config::SubstrateConfig).
//!
//! We now walk through creating a [`crate::config::Config`] for a parachain, using the
//! ["Statemint"](https://parachains.info/details/statemint) parachain, also known as "Asset Hub", as an example. It
//! is currently (as of 2023-06-26) deployed on Polkadot and [Kusama (as "Statemine")](https://parachains.info/details/statemine).
//!
//! To construct a config, we need to investigate which types Statemint uses as `AccountId`, `Hasher`, etc.
//! We need to take a look at the source code of Statemint and find out how it implements some substrate functionalities.
//! Statemint (Polkadot Asset Hub) is part of the [Cumulus Github repository](https://github.com/paritytech/cumulus).
//! The crate defining the parachains runtime can be found [here](https://github.com/paritytech/cumulus/tree/master/parachains/runtimes/assets/asset-hub-polkadot).
//!
//! ## Creating the `Config` from scratch
//!
//! Creating the config from scratch is the most arduous approach but also the most flexible, so first we'll walk through
//! how to do this, and then we'll show how to simplify the process where possible.
//!
//! ### AccountId, Hash, Hasher and Header
//!
//! For these config types, we need to find out where the parachain runtime implements the `frame_system::Config` trait.
//! Look for a code fragment like `impl frame_system::Config for Runtime { ... }` In the source code.
//! For Statemint it looks like [this](https://github.com/paritytech/cumulus/blob/e2b7ad2061824f490c08df27a922c64f50accd6b/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs#L179)
//! at the time of writing. The `AccountId`, `Hash` and `Header` types of the [frame_system::pallet::Config](https://docs.rs/frame-system/latest/frame_system/pallet/trait.Config.html)
//! correspond to the ones we want to use for implementing [crate::Config]. In the Case of Statemint (Asset Hub) they are:
//!
//! - AccountId: [`sp_core::crypto::AccountId32`]
//! - Hash: [`sp_core::H256`]
//! - Hasher (type `Hashing` in [frame_system::pallet::Config](https://docs.rs/frame-system/latest/frame_system/pallet/trait.Config.html)): [`sp_runtime::traits::BlakeTwo256`]
//! - Header: [`sp_runtime::generic::Header<u32, sp_runtime::traits::BlakeTwo256>`](sp_runtime::generic::Header)
//!
//! Subxt has its own versions of some of these types in order to avoid needing to pull in Substrate dependencies:
//!
//! - [`sp_core::crypto::AccountId32`] can be swapped with [`crate::utils::AccountId32`].
//! - [`sp_core::H256`] is a re-export which subxt also provides as [`crate::config::substrate::H256`].
//! - [`sp_runtime::traits::BlakeTwo256`] can be swapped with [`crate::config::substrate::BlakeTwo256`].
//! - [`sp_runtime::generic::Header`] can be swapped with [`crate::config::substrate::SubstrateHeader`].
//!
//! Having a look at how those types are implemented can give some clues as to how to implement other custom types that
//! you may need to use as part of your config.
//!
//! ### Address, Signature
//!
//! A Substrate runtime is typically constructed by using the [frame_support::construct_runtime](https://docs.rs/frame-support/latest/frame_support/macro.construct_runtime.html) macro.
//! In this macro, we need to specify the type of an `UncheckedExtrinsic`. Most of the time, the `UncheckedExtrinsic` will be of the type
//! [sp_runtime::generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>](sp_runtime::generic::UncheckedExtrinsic).
//! The generic parameters `Address` and `Signature` specified when declaring the `UncheckedExtrinsic` type
//! are the types for `Address` and `Signature` we should use when implementing the [crate::Config] trait. This information can
//! also be obtained from the metadata (see [`frame_metadata::v15::ExtrinsicMetadata`]). In case of Statemint (Polkadot Asset Hub)
//! we see the following types being used in `UncheckedExtrinsic`:
//!
//! - Address: [sp_runtime::MultiAddress<Self::AccountId, ()>](sp_runtime::MultiAddress)
//! - Signature: [sp_runtime::MultiSignature]
//!
//! As above, Subxt has its own versions of these types that can be used instead to avoid pulling in Substrate dependencies.
//! Using the Subxt versions also makes interacting with generated code (which uses them in some places) a little nicer:
//!
//! - [`sp_runtime::MultiAddress`] can be swapped with [`crate::utils::MultiAddress`].
//! - [`sp_runtime::MultiSignature`] can be swapped with [`crate::utils::MultiSignature`].
//!
//! ### ExtrinsicParams
//!
//! Chains each have a set of "signed extensions" configured. Signed extensions provide a means to extend how transactions
//! work. Each signed extension can potentially encode some "extra" data which is sent along with a transaction, as well as some
//! "additional" data which is included in the transaction signer payload, but not transmitted along with the transaction. On
//! a node, signed extensions can then perform additional checks on the submitted transactions to ensure their validity.
//!
//! The `ExtrinsicParams` config type expects to be given an implementation of the [`crate::config::ExtrinsicParams`] trait.
//! Implementations of the [`crate::config::ExtrinsicParams`] trait are handed some parameters from Subxt itself, and can
//! accept arbitrary `OtherParams` from users, and are then expected to provide this "extra" and "additional" data when asked.
//!
//! In order to construct a valid implementation of the `ExtrinsicParams` trait, you must first find out which signed extensions
//! are in use by a chain. This information can be obtained from the `SignedExtra` parameter of the `UncheckedExtrinsic` of your
//! parachain, which will be a tuple of signed extensions. It can also be obtained from the metadata (see
//! [`frame_metadata::v15::SignedExtensionMetadata`]).
//!
//! For statemint, the signed extensions look like
//! [this](https://github.com/paritytech/cumulus/tree/master/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs#L779):
//!
//! ```rs
//! pub type SignedExtra = (
//! frame_system::CheckNonZeroSender<Runtime>,
//! frame_system::CheckSpecVersion<Runtime>,
//! frame_system::CheckTxVersion<Runtime>,
//! frame_system::CheckGenesis<Runtime>,
//! frame_system::CheckEra<Runtime>,
//! frame_system::CheckNonce<Runtime>,
//! frame_system::CheckWeight<Runtime>,
//! pallet_asset_tx_payment::ChargeAssetTxPayment<Runtime>,
//! );
//! ```
//!
//! Each element of the `SignedExtra` tuple implements [codec::Encode] and [sp_runtime::traits::SignedExtension]
//! which has an associated type `AdditionalSigned` that also implements [codec::Encode]. Let's look at the underlying types
//! for each tuple element. All zero-sized types have been replaced by `()` for simplicity.
//!
//! | tuple element | struct type | `AdditionalSigned` type |
//! | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
//! | [`frame_system::CheckNonZeroSender`](https://docs.rs/frame-system/latest/frame_system/struct.CheckNonZeroSender.html) | () | () |
//! | [`frame_system::CheckSpecVersion`](https://docs.rs/frame-system/latest/frame_system/struct.CheckSpecVersion.html) | () | [u32] |
//! | [`frame_system::CheckTxVersion`](https://docs.rs/frame-system/latest/frame_system/struct.CheckTxVersion.html) | () | [u32] |
//! | [`frame_system::CheckGenesis`](https://docs.rs/frame-system/latest/frame_system/struct.CheckGenesis.html) | () | `Config::Hash` = [sp_core::H256] |
//! | [`frame_system::CheckMortality`](https://docs.rs/frame-system/latest/frame_system/struct.CheckMortality.html) | [sp_runtime::generic::Era] | `Config::Hash` = [sp_core::H256] |
//! | [`frame_system::CheckNonce`](https://docs.rs/frame-system/latest/frame_system/struct.CheckNonce.html) | `frame_system::pallet::Config::Index` = u32 | () |
//! | [`frame_system::CheckWeight`](https://docs.rs/frame-system/latest/frame_system/struct.CheckWeight.html) | () | () |
//! | [`frame_system::ChargeAssetTxPayment`](https://docs.rs/frame-system/latest/frame_system/struct.ChargeAssetTxPayment.html) | [pallet_asset_tx_payment::ChargeAssetTxPayment](https://docs.rs/pallet-asset-tx-payment/latest/pallet_asset_tx_payment/struct.ChargeAssetTxPayment.html) | () |
//!
//! All types in the `struct type` column make up the "extra" data that we're expected to provide. All types in the
//! `AdditionalSigned` column make up the "additional" data that we're expected to provide. The goal of an
//! [`crate::config::ExtrinsicParams`] impl then is to provide the appropriate (SCALE encoded) data for these via
//! [`crate::config::ExtrinsicParams::encode_extra_to()`] and [`crate::config::ExtrinsicParams::encode_additional_to()`]
//! respectively. If the [`crate::config::ExtrinsicParams`] impl needs additional data to be able to do this, it can use
//! the [`crate::config::ExtrinsicParams::OtherParams`] associated type to obtain it from the user.
//!
//! Given the above information, here is a fairly naive approach to implementing config for Statemint, including the
//! [`crate::config::ExtrinsicParams`] trait, in a compatible way:
//!
//! ```rust,ignore
#![doc = include_str ! ("../../../examples/setup_config_custom.rs")]
//! ```
//!
//! ## Using [`PolkadotConfig`](crate::PolkadotConfig) and [`SubstrateConfig`](crate::SubstrateConfig) values to compose a Config
//!
//! Subxt already provides [`PolkadotConfig`](crate::config::PolkadotConfig) and [`SubstrateConfig`](crate::SubstrateConfig). These
//! two configs are actually very similar, and only differ slightly in the `MultiAddress` type, and in terms of the type of `Tip` provided
//! as part of the `ExtrinsicParams`. Many chains share a lot of the same types as these, and so often times you can just reuse parts
//! of these implementations.
//!
//! From looking at the types needed for the Statemint config above, we can see that it is indeed very similar to these. It ultimately
//! uses the same signed extensions that Substrate uses, and otherwise uses the same types as Polkadot. So, we can create a config which
//! just reuses these existing types:
//!
//! ```rust,ignore
//! use subxt::{Config, PolkadotConfig, SubstrateConfig};
//!
//! pub enum StatemintConfig {}
//!
//! impl Config for StatemintConfig {
//! type Hash = <PolkadotConfig as Config>::Hash;
//! type AccountId = <PolkadotConfig as Config>::AccountId;
//! type Address = <PolkadotConfig as Config>::Address;
//! type Signature = <PolkadotConfig as Config>::Signature;
//! type Hasher = <PolkadotConfig as Config>::Hasher;
//! type Header = <PolkadotConfig as Config>::Header;
//! // this is the only difference to the PolkadotConfig:
//! type ExtrinsicParams = <SubstrateConfig as Config>::ExtrinsicParams;
//! }
//! ```
//!
//! When the types are very similar, building a custom config like this is much simpler than implementing one from scratch.
+1
View File
@@ -11,3 +11,4 @@
pub mod client;
pub mod codegen;
pub mod config;
+4 -2
View File
@@ -468,7 +468,7 @@ mod jsonrpsee_helpers {
}
// helpers for a jsonrpsee specific OnlineClient.
#[cfg(all(feature = "jsonrpsee", feature = "web"))]
#[cfg(all(feature = "jsonrpsee", feature = "web", target_arch = "wasm32"))]
mod jsonrpsee_helpers {
pub use jsonrpsee::{
client_transport::web,
@@ -480,7 +480,9 @@ mod jsonrpsee_helpers {
/// Build web RPC client from URL
pub async fn client(url: &str) -> Result<Client, Error> {
let (sender, receiver) = web::connect(url).await.unwrap();
let (sender, receiver) = web::connect(url)
.await
.map_err(|e| Error::Transport(e.into()))?;
Ok(ClientBuilder::default()
.max_notifs_per_subscription(4096)
.build_with_wasm(sender, receiver))
+16
View File
@@ -135,6 +135,14 @@ mod substrate_impls {
}
}
impl Hasher for sp_runtime::traits::BlakeTwo256 {
type Output = H256;
fn hash(s: &[u8]) -> Self::Output {
<Self as sp_core::Hasher>::hash(s)
}
}
impl Hasher for sp_core::KeccakHasher {
type Output = H256;
@@ -142,4 +150,12 @@ mod substrate_impls {
<Self as sp_core::Hasher>::hash(s)
}
}
impl Hasher for sp_runtime::traits::Keccak256 {
type Output = H256;
fn hash(s: &[u8]) -> Self::Output {
<Self as sp_core::Hasher>::hash(s)
}
}
}