diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index f31f70ab67..1e4d902ff5 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -11,7 +11,7 @@ readme = "../README.md" repository.workspace = true documentation.workspace = true homepage.workspace = true -description = "Submit extrinsics (transactions) to a substrate node via RPC" +description = "Interact with Substrate based chains on the Polkadot Network" keywords = ["parity", "substrate", "blockchain"] [lints] diff --git a/subxt/build.rs b/subxt/build.rs new file mode 100644 index 0000000000..b21264e2bf --- /dev/null +++ b/subxt/build.rs @@ -0,0 +1,29 @@ +//! Build + +use std::process::Command; + +fn main() { + // We want to be able to link to examples on GitHub specific to the current version. + // So, output an env var with either the git tag if one points to the current code, or + // the git hash pointing to the current code if no tag, or master if we + // encounter some error and can't be more specific. + if let Ok(tag) = Command::new("git").args(["tag", "--points-at"]).output() + && tag.status.success() + && !tag.stdout.is_empty() + { + write_to_out(&tag.stdout) + } else if let Ok(commit_hash) = Command::new("git").args(["rev-parse", "HEAD"]).output() + && commit_hash.status.success() + && !commit_hash.stdout.is_empty() + { + write_to_out(&commit_hash.stdout) + } else { + write_to_out(b"master") + } +} + +fn write_to_out(v: &[u8]) { + let out = String::from_utf8_lossy(v); + println!("cargo::rustc-env=SUBXT_REF={out}"); + println!("cargo::rerun-if-env-changed=SUBXT_REF"); +} diff --git a/subxt/src/backend/archive.rs b/subxt/src/backend/archive.rs index 6dd8b91252..3051e32f7f 100644 --- a/subxt/src/backend/archive.rs +++ b/subxt/src/backend/archive.rs @@ -32,7 +32,7 @@ pub struct ArchiveBackend { } impl ArchiveBackend { - /// Configure and construct an [`ArchiveBackend`] and the associated [`ChainHeadBackendDriver`]. + /// Configure and construct an [`ArchiveBackend`]. pub fn new(client: impl Into) -> ArchiveBackend { let methods = ChainHeadRpcMethods::new(client.into()); diff --git a/subxt/src/book.rs b/subxt/src/book.rs new file mode 100644 index 0000000000..6abf916f7f --- /dev/null +++ b/subxt/src/book.rs @@ -0,0 +1,87 @@ +// 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. + +//! # The book +//! +//! Subxt is a library for interacting with Substrate based chains. In the early days, it had a focus on +//! **sub**mitting e**xt**rinsics, hence the name, however it has since evolved into a full featured library for +//! interacting with many aspects of chains across the Polkadot Network. +//! +//! ## The Polkadot Network +//! +//! The Polkadot Network is a collection of interconnected Blockchains. Each chain can accept different +//! transactions and store different data, as well as fundamentally differing in other areas, such as the +//! size and format of account addresses, the data expected to be provided alongside transactions, and +//! even more fundamental properties such at the number of bytes used to represent a block number. +//! +//! Blockchains on the Polkadot network are essentially distributed databases: +//! - To write to a chain, users submit _transactions_. Transactions are packets of data that are submitted to a +//! chain, usually _from_ a specific account. The result of executing these transactions (assuming everything was +//! successful) is that one or more _storage entries_ in the blockchain will be updated. +//! - _Storage Entries_ are sets of values of a given shape. We can read these in order to see what the current +//! state of affairs is. +//! +//! Transactions are appended to the blockchain in batches known as blocks, where each block points to the previous +//! one. Blocks are immutable and cannot be altered once added, and so the blockchain is essentially a big append-only +//! log of all of the transactions every submitted. Storage entries update at each block in response to the transactions +//! in it. Interactions with a blockchain happen _at_ a certain block; transactions are submitted to update the state +//! at a given block, and state can be read at a given block. +//! +//! Chains on the Polkadot network are typically created using the Substrate library. This library provides +//! various primitives and defaults which make it much simpler to build a new blockchain. Substrate based chains group the +//! functionality that they expose to users into _pallets_, where each pallet is a self contained module which contains +//! its own storage entries and accepts its own set of transactions. For example, the _Balances_ pallet would accept +//! transactions related to transferring tokens between users, and expose storage indicating which user has what tokens. +//! +//! Aside from transactions and storage entries, pallets also expose: +//! - _Constants_, which are fixed values at a given runtime version. +//! - _View Functions_, which are read-only functions that can be called and return some result. +//! +//! Outside of pallets, _Runtime APIs_ also exist, which are read-only functions that can be called and return some result. +//! +//! All of this logic lives inside the _runtime_ of a chain. An important aspect of Substrate based chains is that this +//! runtime can be upgraded. Runtime upgrades allow the functionality of a chain to be changed over time. This means +//! that the values that you can read and write from can change from one block to the next. +//! +//! In order to understand what interactions are possible at a specific runtime version, each runtime exposes +//! [_metadata_](https://github.com/paritytech/frame-metadata/). Metadata contains all of the information needed to +//! understand what interactions are possible at this runtime. The shape of metadata itself can also change, which +//! is why metadatas are versioned. Typically, we refer to metadata at version 14 or above as "modern" metadata, and +//! metadata older than this as "historic" or "legacy" metadata. In order to interact with blocks at runtimes which expose +//! historic metadata, additional type information needs to be provided by the user, as it was not present in the +//! metadata. +//! +//! ### TL;DR: +//! - Each chain can be configured differently. +//! - Transactions write to the blockchain, and update storage entries which can be read from. +//! - Reading and writing to a chain happens in the context of a specific block. +//! - Functionality is organized into _pallets_. +//! - This functionality can change over time as Runtime updates occur. +//! - Metadata describes what functionality is available for a given runtime. +//! +//! ## Interacting with the Polkadot Network +//! +//! Subxt is built for interacting with Substrate based chains on the Polkadot. The basic steps for using Subxt are: +//! +//! 0. (Optional) Generate an interface to the chain you wish to interact with. This provides type safe APIs. +//! 1. Create/instantiate some configuration for the chain you wish to interact with. Subxt provides a default +//! [`crate::config::SubstrateConfig`] which works with most chains, or [`crate::config::PolkadotConfig`] which +//! is configured specifically for the Polkadot Relay Chain. +//! 2. Create a _client_ for interacting with the chain, which consumes this configuration. typically, you'll create +//! an [`crate::client::OnlineClient`] which will connect to the chain. It's also possible to create an +//! [`crate::client::OfflineClient`] in the event that you want to avoid any network connection, although in this +//! case you'll obviously have much more limited functionality available to you. +//! 3. Pick a block to work at. To work at the current block at the time of calling, you'd use +//! [`crate::client::OnlineClient::at_current_block()`]. +//! 4. Do things in the context of this block. +//! +//! Behind the scenes, Subxt takes are of things like: +//! - Downloading the metadata at the given blocks where needed. +//! - Ensuring that anything you try to do is actually valid at the given block. +//! - Encoding and decoding the various data sent back and forth. +//! - Translating older metadatas into a useful format +//! +//! See +#![doc = concat!("[the examples](https://github.com/paritytech/subxt/tree/", env!("SUBXT_REF"), "/subxt/examples)")] +//! for more. diff --git a/subxt/src/config.rs b/subxt/src/config.rs index ab77732b42..de8639bb8a 100644 --- a/subxt/src/config.rs +++ b/subxt/src/config.rs @@ -98,7 +98,7 @@ pub trait Config: Clone + Debug + Sized + Send + Sync + 'static { fn set_metadata_for_spec_version(&self, _spec_version: u32, _metadata: ArcMetadata) {} /// Return legacy types (ie types to use with Runtimes that return pre-V14 metadata) for a given spec version. - /// If this returns `None`, [`subxt`] will return an error if type definitions are needed to access some older + /// If this returns `None`, [`subxt`](crate) will return an error if type definitions are needed to access some older /// block. /// /// This doesn't need to live for long; it will be used to translate any older metadata returned from the node diff --git a/subxt/src/events.rs b/subxt/src/events.rs index 1479758c2d..cbe122ae11 100644 --- a/subxt/src/events.rs +++ b/subxt/src/events.rs @@ -160,7 +160,7 @@ impl Events { /// Iterate through the events, Decoding and returning any that match the given type. /// - /// This is a convenience function for calling [`Self::iter`] and then [`Event::decode_call_data_fields_as`] + /// This is a convenience function for calling [`Events::iter`] and then [`Event::decode_fields_as`] /// on each event that we iterate over, filtering those that don't match. pub fn find(&self) -> impl Iterator> { self.iter() @@ -381,9 +381,9 @@ impl<'events, T: Config> Event<'events, T> { Ok(decoded) } - /// Decode the event call data fields into some type which implements [`DecodeAsEvent`]. + /// Decode the event fields into some type which implements [`DecodeAsEvent`]. /// - /// Event types generated via the [`crate::subxt!`] macro implement this. + /// Event types generated via the [`macro@crate::subxt`] macro implement this. pub fn decode_fields_as(&self) -> Option> { if self.is::() { Some(self.decode_fields_unchecked_as::()) @@ -392,12 +392,12 @@ impl<'events, T: Config> Event<'events, T> { } } - /// Decode the event call data fields into some type which implements [`DecodeAsFields`]. + /// Decode the event fields into some type which implements [`DecodeAsFields`]. /// /// This ignores the pallet and event name information, so you should check those via [`Self::pallet_name()`] /// and [`Self::event_name()`] to confirm that this event is the one you are intending to decode. /// - /// Prefer to use [`Self::decode_call_data_fields_as`] where possible. + /// Prefer to use [`Self::decode_fields_as`] where possible. pub fn decode_fields_unchecked_as(&self) -> Result { let bytes = &mut self.field_bytes(); let event_metadata = self.event_metadata(); diff --git a/subxt/src/extrinsics.rs b/subxt/src/extrinsics.rs index 222df77d5a..fe53e2fdf2 100644 --- a/subxt/src/extrinsics.rs +++ b/subxt/src/extrinsics.rs @@ -242,7 +242,7 @@ where /// /// # Note /// - /// This is a subset of [`Self::call_bytes`] that does not include the + /// This is a subset of [`Self::call_data_bytes`] that does not include the /// first two bytes that denote the pallet index and the variant index. pub fn call_data_field_bytes(&self) -> &[u8] { &self.bytes()[self.info.call_data_args_range()] @@ -314,7 +314,7 @@ where /// Decode the extrinsic call data fields into some type which implements [`DecodeAsExtrinsic`]. /// - /// Extrinsic types generated via the [`crate::subxt!`] macro implement this. + /// Extrinsic types generated via the [`macro@crate::subxt`] macro implement this. pub fn decode_call_data_fields_as( &self, ) -> Option> { diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index bf499f7f69..4b9d727a7e 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -2,13 +2,24 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -//! Subxt is a library for interacting with Substrate based nodes. Using it looks something like this: +//! Subxt is a library for interacting with Substrate based nodes. Here's what it looks like: //! //! ```rust,ignore #![doc = include_str!("../examples/submit_transaction.rs")] //! ``` //! -//! Take a look at [the Subxt guide](book) to learn more about how to use Subxt. +//! Take a look at +#![doc = concat!("[the examples](https://github.com/paritytech/subxt/tree/", env!("SUBXT_REF"), "/subxt/examples)")] +//! to get off to a quick start, or check out [the book](book) for a more in depth introduction to Subxt. +//! +//! In addition to the single-file examples above, we have a few self contained project examples to show off specific +//! use cases: +#![doc = concat!("- [The parachain example](https://github.com/paritytech/subxt/tree/", env!("SUBXT_REF"), "/examples/parachain-example)")] +//! is an example which uses Zombienet to spawn a parachain locally, and then connects to it using Subxt. +#![doc = concat!("- [The WASM example](https://github.com/paritytech/subxt/tree/", env!("SUBXT_REF"), "/examples/wasm-example)")] +//! is an example of writing a Rust web application which uses Subxt to interact with a chain entirely in the browser. +#![doc = concat!("- [The WASM example](https://github.com/paritytech/subxt/tree/", env!("SUBXT_REF"), "/examples/ffi-example)")] +//! shows off how Subxt can be called via FFI from Node.JS and Python. #[cfg(any( all(feature = "web", feature = "native"), @@ -36,6 +47,7 @@ compile_error!("subxt: exactly one of the 'web' and 'native' features should be pub extern crate alloc; pub mod backend; +pub mod book; pub mod client; pub mod config; pub mod constants; diff --git a/subxt/src/transactions.rs b/subxt/src/transactions.rs index 9edc61fed3..4afef1c08b 100644 --- a/subxt/src/transactions.rs +++ b/subxt/src/transactions.rs @@ -34,7 +34,7 @@ use std::borrow::Cow; pub use default_params::DefaultParams; pub use payload::{DynamicPayload, Payload, StaticPayload, dynamic}; pub use signer::Signer; -pub use transaction_progress::{TransactionProgress, TransactionStatus}; +pub use transaction_progress::{TransactionInBlock, TransactionProgress, TransactionStatus}; pub use validation_result::{ TransactionInvalid, TransactionUnknown, TransactionValid, ValidationResult, }; @@ -570,10 +570,10 @@ impl<'atblock, T: Config, Client: OfflineClientAtBlockT> self.sign_with_account_and_signature(&signer.account_id(), &signature) } - /// Convert this [`PartialTransaction`] into a [`SubmittableTransaction`], ready to submit. + /// Convert this [`SignableTransaction`] into a [`SubmittableTransaction`], ready to submit. /// An address, and something representing a signature that can be SCALE encoded, are both /// needed in order to construct it. If you have a `Signer` to hand, you can use - /// [`PartialTransaction::sign()`] instead. + /// [`SignableTransaction::sign()`] instead. pub fn sign_with_account_and_signature( &mut self, account_id: &T::AccountId, diff --git a/subxt/src/transactions/default_params.rs b/subxt/src/transactions/default_params.rs index 51314c157e..797d945d83 100644 --- a/subxt/src/transactions/default_params.rs +++ b/subxt/src/transactions/default_params.rs @@ -4,8 +4,9 @@ /// given that we aren't far off having more than 12 transaction extensions already. /// /// If you have params which are _not_ a tuple and which you'd like to be instantiated automatically -/// when calling [`TxClient::sign_and_submit_default()`] or [`TxClient::sign_and_submit_then_watch_default()`], -/// then you'll need to implement this trait for them. +/// when calling [`crate::transactions::TransactionsClient::sign_and_submit_default()`] or +/// [`crate::transactions::TransactionsClient::sign_and_submit_then_watch_default()`], then you'll +/// need to implement this trait for them. pub trait DefaultParams: Sized { /// Instantiate a default instance of the parameters. fn default_params() -> Self; diff --git a/subxt/src/transactions/payload.rs b/subxt/src/transactions/payload.rs index 6dde8893a8..debd6c438b 100644 --- a/subxt/src/transactions/payload.rs +++ b/subxt/src/transactions/payload.rs @@ -75,26 +75,24 @@ pub struct ValidationDetails<'a> { /// A transaction payload containing some generic `CallData`. #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct DefaultPayload { +pub struct StaticPayload { pallet_name: Cow<'static, str>, call_name: Cow<'static, str>, call_data: CallData, validation_hash: Option<[u8; 32]>, } -/// The payload type used by static codegen. -pub type StaticPayload = DefaultPayload; /// The type of a payload typically used for dynamic transaction payloads. -pub type DynamicPayload = DefaultPayload>; +pub type DynamicPayload = StaticPayload; -impl DefaultPayload { - /// Create a new [`DefaultPayload`]. +impl StaticPayload { + /// Create a new [`StaticPayload`]. pub fn new( pallet_name: impl Into, call_name: impl Into, call_data: CallData, ) -> Self { - DefaultPayload { + StaticPayload { pallet_name: Cow::Owned(pallet_name.into()), call_name: Cow::Owned(call_name.into()), call_data, @@ -102,7 +100,7 @@ impl DefaultPayload { } } - /// Create a new [`DefaultPayload`] using static strings for the pallet and call name. + /// Create a new [`StaticPayload`] using static strings for the pallet and call name. /// This is only expected to be used from codegen. #[doc(hidden)] pub fn new_static( @@ -111,7 +109,7 @@ impl DefaultPayload { call_data: CallData, validation_hash: [u8; 32], ) -> Self { - DefaultPayload { + StaticPayload { pallet_name: Cow::Borrowed(pallet_name), call_name: Cow::Borrowed(call_name), call_data, @@ -143,8 +141,8 @@ impl DefaultPayload { } } -impl DefaultPayload> { - /// Convert the dynamic `Composite` payload into a [`Value`]. +impl StaticPayload> { + /// Convert the `Composite` payload into a [`Value`]. /// This is useful if you want to use this as an argument for a /// larger dynamic call that wants to use this as a nested call. pub fn into_value(self) -> Value<()> { @@ -160,7 +158,7 @@ impl DefaultPayload> { } } -impl Payload for DefaultPayload { +impl Payload for StaticPayload { fn encode_call_data_to( &self, metadata: &Metadata, @@ -202,14 +200,13 @@ impl Payload for DefaultPayload { } } -/// Construct a transaction at runtime; essentially an alias to [`DefaultPayload::new()`] -/// which provides a [`Composite`] value for the call data. -pub fn dynamic( +/// Construct a transaction at runtime; essentially an alias to [`DynamicPayload::new()`]. +pub fn dynamic( pallet_name: impl Into, call_name: impl Into, - call_data: impl Into>, -) -> DynamicPayload { - DefaultPayload::new(pallet_name, call_name, call_data.into()) + call_data: CallData, +) -> DynamicPayload { + StaticPayload::new(pallet_name, call_name, call_data.into()) } #[cfg(test)] @@ -232,7 +229,7 @@ mod tests { ("value", scale_value::Value::string("not_a_number")), // String instead of u128 ]); - let payload = DefaultPayload::new("Balances", "transfer_allow_death", incompatible_data); + let payload = StaticPayload::new("Balances", "transfer_allow_death", incompatible_data); let mut out = Vec::new(); let result = payload.encode_call_data_to(&metadata, &mut out); @@ -257,7 +254,7 @@ mod tests { ("value", scale_value::Value::u128(1000)), ]); - let payload = DefaultPayload::new("Balances", "transfer_allow_death", valid_data); + let payload = StaticPayload::new("Balances", "transfer_allow_death", valid_data); // This should succeed let mut out = Vec::new(); diff --git a/subxt/src/transactions/transaction_progress.rs b/subxt/src/transactions/transaction_progress.rs index 559e7b9fb9..b32ee2756c 100644 --- a/subxt/src/transactions/transaction_progress.rs +++ b/subxt/src/transactions/transaction_progress.rs @@ -66,7 +66,7 @@ where /// instance when it is, or an error if there was a problem waiting for finalization. /// /// **Note:** consumes `self`. If you'd like to perform multiple actions as the state of the - /// transaction progresses, use [`TxProgress::next()`] instead. + /// transaction progresses, use [`TransactionProgress::next()`] instead. /// /// **Note:** transaction statuses like `Invalid`/`Usurped`/`Dropped` indicate with some /// probability that the transaction will not make it into a block but there is no guarantee @@ -101,7 +101,7 @@ where /// as well as a couple of other details (block hash and extrinsic hash). /// /// **Note:** consumes self. If you'd like to perform multiple actions as progress is made, - /// use [`TxProgress::next()`] instead. + /// use [`TransactionProgress::next()`] instead. /// /// **Note:** transaction statuses like `Invalid`/`Usurped`/`Dropped` indicate with some /// probability that the transaction will not make it into a block but there is no guarantee @@ -199,7 +199,7 @@ pub enum TransactionStatus<'atblock, T: Config, C> { impl<'atblock, T: Config, C> TransactionStatus<'atblock, T, C> { /// A convenience method to return the finalized details. Returns - /// [`None`] if the enum variant is not [`TxStatus::InFinalizedBlock`]. + /// [`None`] if the enum variant is not [`TransactionStatus::InFinalizedBlock`]. pub fn as_finalized(&self) -> Option<&TransactionInBlock<'atblock, T, C>> { match self { Self::InFinalizedBlock(val) => Some(val), @@ -208,7 +208,7 @@ impl<'atblock, T: Config, C> TransactionStatus<'atblock, T, C> { } /// A convenience method to return the best block details. Returns - /// [`None`] if the enum variant is not [`TxStatus::InBestBlock`]. + /// [`None`] if the enum variant is not [`TransactionStatus::InBestBlock`]. pub fn as_in_block(&self) -> Option<&TransactionInBlock<'atblock, T, C>> { match self { Self::InBestBlock(val) => Some(val), @@ -258,7 +258,7 @@ impl<'atblock, T: Config, C: OnlineClientAtBlockT> TransactionInBlock<'atbloc /// **Note:** If multiple `ExtrinsicFailed` errors are returned (for instance /// because a pallet chooses to emit one as an event, which is considered /// abnormal behaviour), it is not specified which of the errors is returned here. - /// You can use [`TxInBlock::fetch_events`] instead if you'd like to + /// You can use [`TransactionInBlock::fetch_events`] instead if you'd like to /// work with multiple "error" events. /// /// **Note:** This has to download block details from the node and decode events diff --git a/subxt/src/transactions/validation_result.rs b/subxt/src/transactions/validation_result.rs index d421122f3e..86defd01cd 100644 --- a/subxt/src/transactions/validation_result.rs +++ b/subxt/src/transactions/validation_result.rs @@ -1,7 +1,7 @@ use crate::error::ExtrinsicError; use codec::Decode; -/// The result of performing [`SubmittableTransaction::validate()`]. +/// The result of performing [`crate::transactions::SubmittableTransaction::validate()`]. #[derive(Clone, Debug, PartialEq)] pub enum ValidationResult { /// The transaction is valid