substrate-api-client a library written in Rust for connecting to the substrate's RPC interface via WebSockets allowing to
- Compose extrinsics, send them and subscribe to updates (synchronously).
- supports
no_std
builds. Only the rpc-client is std only. Forno_std
builds, a custom rpc client needs to be implemented. - Watch events and execute code upon events.
- Parse and print the node metadata.
In order to build the substrate-api-client and the examples, Rust and the wasm target are needed. For Linux:
curl https://sh.rustup.rs -sSf | sh
# Install the rust toolchain specified in rust-toolchain.toml
rustup show
To execute the examples, a running substrate node is needed. You can download a node artifact from substrate directly: https://github.com/paritytech/substrate or run the kitchensink-node with docker:
docker run -p 9944:9944 -p 9933:9933 -p 30333:30333 parity/substrate:latest --dev --ws-external --rpc-external
For more information, please refer to the substrate repository.
To run an example, clone the substrate-api-client
repository and run the desired example directly with the cargo command:
git clone https://github.com/scs/substrate-api-client.git
cd substrate-api-client
cargo run -p ac-examples --example get_storage
or download the already built binaries from GitHub Actions and run them without any previous building:
# Add execution rights to the chosen example.
chmod +x <example>
# And run it.
./<example>
Set the output verbosity by prepending RUST_LOG=info
or RUST_LOG=debug
.
The following examples can be found in the examples folder:
- benchmark_bulk_xt: Float the node with a series of transactions.
- compose_extrinsic_offline: Compose an extrinsic without interacting with the node.
- custom_nonce: Compose an with a custom nonce.
- contract_instantiate_with_code: Instantiate a contract on the chain.
- event_callback: Subscribe and react on events.
- event_error_details: Listen to error events from the node to determine if an extrinsic was successful or not.
- get_account_identity: Create an custom Unchecked Extrinsic to set an account identity and retrieve it afterwards with a getter.
- get_block: Read header, block and signed block from storage.
- get_storage: Read storage values.
- print_metadata: Print the metadata of the node in a readable way.
- staking_batch_payout: Batch reward payout for validator.
- sudo: Create and send a sudo wrapped call.
- transfer_with_tungstenite_client: Transfer tokens by using a wrapper of compose_extrinsic with an account generated with a seed.
- transfer_with_ws_client: Transfer tokens by using a wrapper of compose_extrinsic with an account generated with a seed.
Almost everything in the api-client, except for the rpc-clients and a few additional features, is no_std
compatible.
Many helpful features, such as extrinsic and call creation (see the macros), metadata and event types (see the node-api and primitives) are available in no_std
right away. However, to directly connect to a Substrate node a RPC client is necessary. Because websocket connection features are often hardware dependent, a generic no_std
RPC client implementation is hardly possible. So for most use cases a self-implemented RPC client is required. To make this as simple as possible, the interface between the Api
, which provides all the features, and the RPC client, providing the node connection, is kept very basic. Check out the following explanations for more info.
To import the api-client in no_std
make sure the default features are turned off and disable_target_static_assertions
is enabled:
# In the Cargo.toml import the api-client as following:
substrate-api-client = { git = "https://github.com/scs/substrate-api-client.git", default-features = false, features = ["disable_target_static_assertions"] }
Depending on the usage, there are two traits that the RPC Client needs to implement.
For simple requests (send one request and receive one answer) the trait Request
is required:
/// Trait to be implemented by the ws-client for sending rpc requests and extrinsic.
pub trait Request {
/// Sends a RPC request to the substrate node and returns the answer as string.
fn request<R: DeserializeOwned>(&self, method: &str, params: RpcParams) -> Result<R>;
}
By implementing this trait with a custom RPC client, most basic functionalities of the Api
can already be used.
Currently, there is no no_std
example available. But the tungstenite_client
provides a relatively simple std
example. If a websocket library is available in your no_std
environment, then your implementation may look similar.
A little more complex is the second trait Subscribe
, which does not only send a subscription request to the node, it also keeps listening and updating accordingly.
Two traits need to be implemented for this feature.
The Subscribe
trait itself:
/// Trait to be implemented by the ws-client for subscribing to the substrate node.
pub trait Subscribe {
type Subscription<Notification>: HandleSubscription<Notification>
where
Notification: DeserializeOwned;
fn subscribe<Notification: DeserializeOwned>(
&self,
sub: &str,
params: RpcParams,
unsub: &str,
) -> Result<Self::Subscription<Notification>>;
}
and the HandleSubscription
trait, which is returned by the subscribe
function:
/// Trait to use the full functionality of jsonrpseee Subscription type
/// without actually enforcing it.
pub trait HandleSubscription<Notification: DeserializeOwned> {
/// Returns the next notification from the stream.
/// This may return `None` if the subscription has been terminated,
/// which may happen if the channel becomes full or is dropped.
fn next(&mut self) -> Option<Result<Notification>>;
/// Unsubscribe and consume the subscription.
fn unsubscribe(self) -> Result<()>;
}
Refering to the std
example of the tungstenite, the HandleSubscription
impl can be looked up here. It implements a simple channel receiver, waiting for the sender of the websocket client to send something.
The Subscribe
implementation can be found here.
A more complex RPC client, but also with more functionalities, is the jsonrpsee client.
Parity offers a Rust client with similar functionality: https://github.com/paritytech/subxt
The development of substrate-api-client is financed by web3 foundation's grant programme.
We also thank the teams at
- Parity Technologies for building substrate and supporting us during development.
If you intend to or are using substrate-api-client, please add your project here
In alphabetical order