pezkuwi-subxt 0.44.0

Submit extrinsics (transactions) to a Pezkuwi/Bizinikiwi node via RPC
Documentation
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

//! # Transactions
//!
//! A transaction is an extrinsic that's signed (ie it originates from a given address). The purpose
//! of extrinsics is to modify the node storage in a deterministic way, and so being able to submit
//! transactions to a node is one of the core features of Subxt.
//!
//! > Note: the documentation tends to use the terms _extrinsic_ and _transaction_ interchangeably;
//! > An extrinsic is some data that can be added to a block, and is either signed (a _transaction_)
//! > or unsigned (an _inherent_). Subxt can construct either, but overwhelmingly you'll need to
//! > sign the payload you'd like to submit.
//!
//! Submitting a transaction to a node consists of the following steps:
//!
//! 1. [Constructing a transaction payload to submit](#constructing-a-transaction-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.
//!
//! ## Constructing a transaction payload
//!
//! We can use the statically generated interface to build transaction payloads:
//!
//! ```rust,no_run,standalone_crate
//! #[pezkuwi_subxt::subxt(runtime_metadata_path = "../artifacts/pezkuwi_metadata_small.scale")]
//! pub mod pezkuwi {}
//!
//! let remark = "Hello there".as_bytes().to_vec();
//! let tx_payload = pezkuwi::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 a transaction payload. This will not be type checked
//! or validated until it's submitted:
//!
//! ```rust,no_run,standalone_crate
//! use pezkuwi_subxt::dynamic::Value;
//!
//! let tx_payload = pezkuwi_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 transaction payload is just something that implements the [`crate::tx::Payload`] 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 a [`crate::tx::Signer`] instance, which
//! tells Subxt who the extrinsic is from, and takes care of signing the relevant details to prove
//! this.
//!
//! There are two main ways to create a compatible signer instance:
//! 1. The `pezkuwi_subxt_signer` crate provides a WASM compatible implementation of
//!    [`crate::tx::Signer`]
//! for chains which require sr25519 or ecdsa signatures (requires the `subxt` feature to be
//! enabled).
//! 2. Alternately, implement your own [`crate::tx::Signer`] instance by wrapping it in a new type
//!    pattern.
//!
//! Going for 1 leads to fewer dependencies being imported and WASM compatibility out of the box via
//! the `web` feature flag. Going for 2 is useful if you're already using the Bizinikiwi dependencies
//! or need additional signing algorithms that `pezkuwi_subxt_signer` doesn't support, and don't
//! care about WASM compatibility.
//!
//! Because 2 is more complex and require more code, we'll focus on 1 here.
//! For 2, see the example in `subxt/examples/bizinikiwi_compat_signer.rs` how
//! you can integrate things like sp_core's signer in subxt.
//!
//! Let's go through how to create a signer using the `pezkuwi_subxt_signer` crate:
//!
//! ```rust,standalone_crate
//! use pezkuwi_subxt::config::PezkuwiConfig;
//! use std::str::FromStr;
//!
//! use pezkuwi_subxt_signer::{SecretUri, sr25519};
//!
//! // Get hold of a `Signer` for a test account:
//! let alice = sr25519::dev::alice();
//!
//! // Or generate a keypair, here from an SURI:
//! let uri = SecretUri::from_str("vessel ladder alter error federal sibling chat ability sun glass valve picture/0/1///Password")
//!     .expect("valid URI");
//! let keypair = sr25519::Keypair::from_uri(&uri)
//!     .expect("valid keypair");
//! ```
//!
//! After initializing the signer, let's also go through how to create a transaction and sign it:
//!
//! ```rust,no_run,standalone_crate
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use pezkuwi_subxt::client::OnlineClient;
//! use pezkuwi_subxt::config::PezkuwiConfig;
//! use pezkuwi_subxt::dynamic::Value;
//!
//! // Create client:
//! let client = OnlineClient::<PezkuwiConfig>::new().await?;
//!
//! // Create a dummy tx payload to sign:
//! let payload = pezkuwi_subxt::dynamic::tx("System", "remark", vec![
//!     Value::from_bytes("Hello there")
//! ]);
//!
//! // Construct the tx but don't sign it. The account nonce here defaults to 0.
//! // You can use `create_partial` to fetch the correct nonce.
//! let mut partial_tx = client.tx().create_partial_offline(
//!     &payload,
//!     Default::default()
//! )?;
//!
//! // Fetch the payload that needs to be signed:
//! let signer_payload = partial_tx.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 account_id;
//! # use pezkuwi_subxt::tx::Signer;
//! # let signer = pezkuwi_subxt_signer::sr25519::dev::alice();
//! # signature = signer.sign(&signer_payload).into();
//! # account_id = signer.public_key().to_account_id();
//!
//! // Now we can build an tx, which one can call `submit` or `submit_and_watch`
//! // on to submit to a node and optionally watch the status.
//! let tx = partial_tx.sign_with_account_and_signature(
//!     &account_id,
//!     &signature
//! );
//! # Ok(())
//! # }
//! ```
//!
//! ## Submitting it
//!
//! Once we have signed the transaction, 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/tx_basic.rs")]
//! ```
//!
//! ### Providing transaction parameters
//!
//! If you'd like to provide parameters (such as mortality) to the transaction, you can use
//! [`crate::tx::TxClient::sign_and_submit_then_watch`] instead:
//! ```rust,ignore
#![doc = include_str!("../../../examples/tx_with_params.rs")]
//! ```
//!
//! This example doesn't wait for the transaction to be included in a block; it just submits it and
//! hopes for the best!
//!
//! ### Boxing transaction payloads
//!
//! Transaction payloads can be boxed so that they all share a common type and can be stored together.
//! ```rust,ignore
#![doc = include_str!("../../../examples/tx_boxed.rs")]
//! ```
//!
//! ### 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/tx_status_stream.rs")]
//! ```
//!
//! ### Signing transactions externally
//!
//! Subxt also allows you to get hold of the signer payload and hand that off to something else to be
//! signed. The signature can then be provided back to Subxt to build the final transaction to submit:
//! ```rust,ignore
#![doc = include_str!("../../../examples/tx_partial.rs")]
//! ```
//!
//! Take a look at the API docs for [`crate::tx::TxProgress`], [`crate::tx::TxStatus`] and
//! [`crate::tx::TxInBlock`] for more options.