diff --git a/Cargo.lock b/Cargo.lock index 0e07690990..d9b0d15e29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5818,6 +5818,7 @@ dependencies = [ "primitive-types", "serde", "serde_json", + "subxt", "subxt-lightclient", "thiserror 2.0.12", "tokio", diff --git a/rpcs/Cargo.toml b/rpcs/Cargo.toml index 3cfc929529..18ae7cfaad 100644 --- a/rpcs/Cargo.toml +++ b/rpcs/Cargo.toml @@ -89,6 +89,9 @@ tower = { workspace = true } hyper = { workspace = true } http-body = { workspace = true } jsonrpsee = { workspace = true, features = ["server"] } +tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } +futures = { workspace = true } +subxt = { path = "../subxt", default-features = false, features = ["native"] } [package.metadata.docs.rs] default-features = true diff --git a/rpcs/examples/basic_rpc_usage.rs b/rpcs/examples/basic_rpc_usage.rs new file mode 100644 index 0000000000..15cca6e69b --- /dev/null +++ b/rpcs/examples/basic_rpc_usage.rs @@ -0,0 +1,41 @@ +//! This example demonstrates basic usage of `subxt-rpcs` in isolation, +//! showing how to set up an RPC client and make simple RPC calls. + +use subxt::{config::PolkadotConfig, config::RpcConfigFor}; +use subxt_rpcs::{client::RpcClient, methods::LegacyRpcMethods}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create an RPC client from a URL. This connects to a Polkadot node. + // You can replace this with any Substrate-based node URL. + let rpc_client = RpcClient::from_url("wss://rpc.polkadot.io").await?; + + // Create RPC methods using RpcConfigFor. + // This tells the RPC methods to use the same types as PolkadotConfig. + let rpc = LegacyRpcMethods::>::new(rpc_client); + + // Now we can make RPC calls! Let's get the chain name: + let chain_name = rpc.system_chain().await?; + println!("Connected to chain: {}", chain_name); + + // Get the latest finalized block hash: + let finalized_hash = rpc.chain_get_finalized_head().await?; + println!("Latest finalized block hash: {:?}", finalized_hash); + + // Get the block header for that hash: + let header = rpc.chain_get_header(Some(finalized_hash)).await?; + if let Some(header) = header { + println!("Block number: {}", header.number); + println!("Parent hash: {:?}", header.parent_hash); + } + + // Get the runtime version: + let runtime_version = rpc.state_get_runtime_version(None).await?; + println!("Runtime spec version: {}", runtime_version.spec_version); + println!( + "Runtime transaction version: {}", + runtime_version.transaction_version + ); + + Ok(()) +} diff --git a/rpcs/examples/chainhead_subscription.rs b/rpcs/examples/chainhead_subscription.rs new file mode 100644 index 0000000000..b54116fb6b --- /dev/null +++ b/rpcs/examples/chainhead_subscription.rs @@ -0,0 +1,40 @@ +//! This example demonstrates using the newer ChainHead RPC methods +//! for subscribing to blocks. + +use subxt::{config::PolkadotConfig, config::RpcConfigFor}; +use subxt_rpcs::{client::RpcClient, methods::ChainHeadRpcMethods}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create an RPC client + let rpc_client = RpcClient::from_url("wss://rpc.polkadot.io").await?; + + // Create ChainHead RPC methods using RpcConfigFor + let rpc = ChainHeadRpcMethods::>::new(rpc_client); + + println!("Subscribing to finalized blocks..."); + + // Subscribe to finalized blocks using the chainHead API + let mut block_sub = rpc.chainhead_v1_follow(true).await?; + + // Process the first few events + let mut count = 0; + while let Some(event) = block_sub.next().await { + match event { + Ok(event) => { + println!("Received event: {:?}", event); + count += 1; + if count >= 3 { + println!("Received 3 events, stopping..."); + break; + } + } + Err(e) => { + eprintln!("Error receiving event: {}", e); + break; + } + } + } + + Ok(()) +} diff --git a/rpcs/examples/custom_config.rs b/rpcs/examples/custom_config.rs new file mode 100644 index 0000000000..af5094282b --- /dev/null +++ b/rpcs/examples/custom_config.rs @@ -0,0 +1,50 @@ +//! This example shows how to define a custom RpcConfig for chains +//! that might have different types than the standard configuration. +//! +//! For most Substrate-based chains, you can use `RpcConfigFor` +//! as shown in the basic_rpc_usage example. This example shows how to create +//! a completely custom configuration if needed. + +#![allow(missing_docs)] + +use subxt_rpcs::{RpcConfig, client::RpcClient, methods::LegacyRpcMethods}; + +// Define a custom config for your chain +pub enum MyCustomConfig {} + +impl RpcConfig for MyCustomConfig { + // For this example, we'll use the same types as Polkadot. + // In a real custom chain, you might have different types. + type Header = subxt::config::substrate::SubstrateHeader; + type Hash = subxt::config::substrate::H256; + type AccountId = subxt::config::substrate::AccountId32; +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create an RPC client + let rpc_client = RpcClient::from_url("wss://rpc.polkadot.io").await?; + + // Use our custom config + let rpc = LegacyRpcMethods::::new(rpc_client); + + // Make RPC calls as usual + let chain_name = rpc.system_chain().await?; + println!("Connected to chain: {}", chain_name); + + let finalized_hash = rpc.chain_get_finalized_head().await?; + println!("Latest finalized block: {:?}", finalized_hash); + + // Get a header using our custom header type + let header = rpc.chain_get_header(Some(finalized_hash)).await?; + if let Some(header) = header { + println!("Block number: {}", header.number); + println!("Parent hash: {:?}", header.parent_hash); + } + + println!("\nThis example demonstrates that you can define your own RpcConfig"); + println!("to match the types used by your specific blockchain."); + println!("However, for most Substrate chains, using RpcConfigFor is simpler!"); + + Ok(()) +}