SDK Version
This Documentation is based upon avail-rust version v0.1.0-rc5
Basics
The goal of this guide is to learn how to use subxt and then in a iterative way enhance and tidy the code using avail-rust SDK.
Transaction - 1
Every transaction consists of the following parts:
- Signature
- Payload
- Transaction Parameters
The Signature defines who is accountable and who's funds will be taken in order to pay for transaction execution.
The Payload is the function (together with the data) that will be executed.
The Transaction Parameters define additional information about our transaction. Here we would say how much tip we want to give, what nonce to use, etc.
In order for our transaction to be executed we need the following parts:
- Establish WebSocket or HTTP connection with a network
- Way to submit a transaction
- Way to check if that transaction was successfully included
Setting up the stage
Our initial setup will have nothing more than the bare minimum to compile our code.
Most of the types that we need are included in the prelude
import collection but because we are not going to use any of it (for now) we will have to manually import modules.
All the future code that we will write will go inside the main
function.
use avail_rust::error::ClientError;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
// Code goes here
Ok(())
}
Connection
The first thing that we need to do is to establish a connection with an existing network. For the sake of brevity, we will cover only how to do it using websockets but you can find in other examples on how to do it either using HTTP or a custom solution.
use avail_rust::{
subxt::backend::rpc::{
reconnecting_rpc_client::RpcClient as ReconnectingRpcClient, RpcClient,
},
AOnlineClient,
};
let endpoint = "ws://127.0.0.1:9944";
let rpc_client = ReconnectingRpcClient::builder().build(endpoint).await;
let rpc_client = rpc_client.map_err(|e| e.to_string())?;
let rpc_client = RpcClient::new(rpc_client);
let online_client = AOnlineClient::from_rpc_client(rpc_client.clone()).await?;
rpc_client
is a low level API that allows us to communicate with our network via rpc calls.
online_client
is a higher level API that provides many helper functions and abstractions.
Accounts
An account represents an identity—usually of a person or an organization—that is capable of making transactions or holding funds. In general, every account has an owner who possesses a public and private key pair. The private key is a cryptographically-secure sequence of randomly-generated numbers. For human readability, the private key generates a random sequence of words called a secret seed phrase or mnemonic.
Substrate - Accounts, Addresses, Keys
To create an account we paste our secret seed as an argument to SecretUri
and then pass that Keypair
. In this case, we will use the default development account named Alice
.
In production you would pass your secret seed via env variable or read it from file.
For Bob use //Bob
, for Eve use //Eve
, etc.
use avail_rust::subxt_signer::{sr25519::Keypair, SecretUri};
use std::str::FromStr;
let secret_uri = SecretUri::from_str("//Alice")?;
let account = Keypair::from_uri(&secret_uri)?;
let account_id = account.public_key().to_account_id();
// 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
let _account_address = account_id.to_string();
Payload
Payload defines what operation will be executed on the chain. Payload consists of three components:
- Pallet Name
- Call Name
- Call Data
What you need to know is that all the payloads are defines in the following path avail_rust::avail::*::calls::types::**;
where *
represents the pallet name and **
represents the call type.
For more examples go to the next page.
use avail_rust::{
avail::runtime_types::bounded_collections::bounded_vec::BoundedVec,
subxt::{blocks::StaticExtrinsic, ext::subxt_core::tx::payload::StaticPayload},
};
use avail_rust::avail::data_availability::calls::types::SubmitData;
let pallet_name = SubmitData::PALLET;
let call_name = SubmitData::CALL;
let data = String::from("My Data").into_bytes();
let data = BoundedVec(data);
let call_data = SubmitData { data };
let payload = StaticPayload::new(pallet_name, call_name, call_data);
Transaction Parameters
There are four transaction parameters:
- nonce
- app_id
- tip
- mortality
Manually building the transaction parameters is a tedious and convoluted job so here we are using a helper object to do that for us.
With the Options
object we can set what parameters we want to use and with calling build()
it populates all the non-set params with default values.
Here are the default values for all the parameters:
- nonce: It uses the best block nonce and it increments it if there are existing transaction in the tx pool with the same nonce
- app_id: 0
- tip: 0
- mortality: The transaction will be alive for 32 blocks starting from current best block hash(height)
use avail_rust::Options;
let options = Options::new()
.build(&online_client, &rpc_client, &account_id)
.await?;
let params = options.build().await?;
Signature
Adding signature to an existing payload and transaction params allows us to build an transaction that is ready to be submitted.
let submittable_tx = online_client
.tx()
.create_signed(&payload, &account, params)
.await?;
Submission
Submission is done by calling .submit()
. There is another method available as well, .submit_and_watch()
, but that one isn't working correctly.
Submitting a transaction yields back the transaction hash.
let tx_hash = submittable_tx.submit().await?;
Watcher
Just because we have submitted our transaction it doesn't mean it was successful or that it got executed at all.
We need to implement a watcher
that will check the next N blocks to see if our tx hash is included in the block.
Once found, we need to search for the ExtrinsicSuccess
event in order to determine if the transaction was successful or not.
use avail_rust::avail::system::events::ExtrinsicSuccess;
let mut block_sub = online_client.blocks().subscribe_all().await?;
while let Some(block) = block_sub.next().await {
let block = block?;
let block_txs = block.extrinsics().await?;
let tx = block_txs.iter().find(|tx| tx.hash() == tx_hash);
if let Some(tx) = tx {
println!("Transaction was found.");
println!("Block Hash: {:?}", block.hash()); // Block Hash: 0x61415b6012005665bac0cf8575a94e509d079a762be2ba6a71a04633efd01c1b
println!("Block Number: {:?}", block.number()); // Block Number: 200
println!("Tx Hash: {:?}", tx.hash()); // Tx Hash: 0x01651a93d55bde0f258504498d4f2164416df5331794d9c905d4c8711d9537ef
println!("Tx Index: {:?}", tx.index()); // Tx Index: 1
let events = tx.events().await?;
println!("Event count: {}", events.iter().count()); // Event count: 7
if events
.find_first::<ExtrinsicSuccess>()
.ok()
.flatten()
.is_some()
{
println!("Transaction was successful");
}
break;
}
}
Source Code
use avail_rust::error::ClientError;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
// RPC Connection
// ANCHOR: connection
use avail_rust::{
subxt::backend::rpc::{
reconnecting_rpc_client::RpcClient as ReconnectingRpcClient, RpcClient,
},
AOnlineClient,
};
let endpoint = "ws://127.0.0.1:9944";
let rpc_client = ReconnectingRpcClient::builder().build(endpoint).await;
let rpc_client = rpc_client.map_err(|e| e.to_string())?;
let rpc_client = RpcClient::new(rpc_client);
let online_client = AOnlineClient::from_rpc_client(rpc_client.clone()).await?;
// ANCHOR_END: connection
// Accounts
// ANCHOR: accounts
use avail_rust::subxt_signer::{sr25519::Keypair, SecretUri};
use std::str::FromStr;
let secret_uri = SecretUri::from_str("//Alice")?;
let account = Keypair::from_uri(&secret_uri)?;
let account_id = account.public_key().to_account_id();
// 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
let _account_address = account_id.to_string();
// ANCHOR_END: accounts
// Payload
// ANCHOR: payload
use avail_rust::{
avail::runtime_types::bounded_collections::bounded_vec::BoundedVec,
subxt::{blocks::StaticExtrinsic, ext::subxt_core::tx::payload::StaticPayload},
};
use avail_rust::avail::data_availability::calls::types::SubmitData;
let pallet_name = SubmitData::PALLET;
let call_name = SubmitData::CALL;
let data = String::from("My Data").into_bytes();
let data = BoundedVec(data);
let call_data = SubmitData { data };
let payload = StaticPayload::new(pallet_name, call_name, call_data);
// ANCHOR_END: payload
// Transaction Parameters
// ANCHOR: params
use avail_rust::Options;
let options = Options::new()
.build(&online_client, &rpc_client, &account_id)
.await?;
let params = options.build().await?;
// ANCHOR_END: params
// Signature
// ANCHOR: signature
let submittable_tx = online_client
.tx()
.create_signed(&payload, &account, params)
.await?;
// ANCHOR_END: signature
// Submission
// ANCHOR: submission
let tx_hash = submittable_tx.submit().await?;
// ANCHOR_END: submission
// Watcher
// ANCHOR: watcher
use avail_rust::avail::system::events::ExtrinsicSuccess;
let mut block_sub = online_client.blocks().subscribe_all().await?;
while let Some(block) = block_sub.next().await {
let block = block?;
let block_txs = block.extrinsics().await?;
let tx = block_txs.iter().find(|tx| tx.hash() == tx_hash);
if let Some(tx) = tx {
println!("Transaction was found.");
println!("Block Hash: {:?}", block.hash()); // Block Hash: 0x61415b6012005665bac0cf8575a94e509d079a762be2ba6a71a04633efd01c1b
println!("Block Number: {:?}", block.number()); // Block Number: 200
println!("Tx Hash: {:?}", tx.hash()); // Tx Hash: 0x01651a93d55bde0f258504498d4f2164416df5331794d9c905d4c8711d9537ef
println!("Tx Index: {:?}", tx.index()); // Tx Index: 1
let events = tx.events().await?;
println!("Event count: {}", events.iter().count()); // Event count: 7
if events
.find_first::<ExtrinsicSuccess>()
.ok()
.flatten()
.is_some()
{
println!("Transaction was successful");
}
break;
}
}
// ANCHOR_END: watcher
Ok(())
}
Transaction - 1.1
Here you can find additional examples on how to correctly construct the payload.
use avail_rust::{account::account_id_from_str, error::ClientError};
#[tokio::main]
async fn main() -> Result<(), ClientError> {
data_availability_create_key().await?;
balances_transfer_keep_alive().await?;
identity_set_identity().await?;
// Payload
Ok(())
}
async fn data_availability_create_key() -> Result<(), ClientError> {
use avail_rust::{
avail::runtime_types::bounded_collections::bounded_vec::BoundedVec,
subxt::{blocks::StaticExtrinsic, ext::subxt_core::tx::payload::StaticPayload},
};
use avail_rust::avail::data_availability::calls::types::CreateApplicationKey;
let pallet_name = CreateApplicationKey::PALLET;
let call_name = CreateApplicationKey::CALL;
let key = String::from("My Data").into_bytes();
let key = BoundedVec(key);
let call_data = CreateApplicationKey { key };
let _payload = StaticPayload::new(pallet_name, call_name, call_data);
Ok(())
}
async fn balances_transfer_keep_alive() -> Result<(), ClientError> {
use avail_rust::{
subxt::{blocks::StaticExtrinsic, ext::subxt_core::tx::payload::StaticPayload},
SDK,
};
use avail_rust::avail::balances::calls::types::TransferKeepAlive;
let pallet_name = TransferKeepAlive::PALLET;
let call_name = TransferKeepAlive::CALL;
let dest = account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")?; // Bob
let value = SDK::one_avail();
let call_data = TransferKeepAlive {
dest: dest.into(),
value,
};
let _payload = StaticPayload::new(pallet_name, call_name, call_data);
Ok(())
}
async fn identity_set_identity() -> Result<(), ClientError> {
use avail_rust::subxt::{blocks::StaticExtrinsic, ext::subxt_core::tx::payload::StaticPayload};
use avail_rust::avail::{
identity::calls::types::{set_identity::Info, SetIdentity},
runtime_types::{
bounded_collections::bounded_vec::BoundedVec, pallet_identity::types::Data,
},
};
let pallet_name = SetIdentity::PALLET;
let call_name = SetIdentity::CALL;
let display_name: [u8; 7] = String::from("My Name").into_bytes().try_into().unwrap();
let info = Info {
additional: BoundedVec(vec![(Data::None, Data::None)]),
display: Data::Raw7(display_name),
legal: Data::None,
web: Data::None,
riot: Data::None,
email: Data::None,
pgp_fingerprint: None,
image: Data::None,
twitter: Data::None,
};
let call_data = SetIdentity {
info: Box::new(info),
};
let _payload = StaticPayload::new(pallet_name, call_name, call_data);
Ok(())
}
Transaction - 2
With everything in place, we can slowly replace and tidy up our code.
Connection
The first change that we have does was to simplify the creation of online and rpc client. Instead of manually creating them there is an convenient helper function that will set it up for us.
use avail_rust::sdk::reconnecting_api;
let endpoint = "ws://127.0.0.1:9944";
let (online_client, rpc_client) = reconnecting_api(endpoint).await?;
The reconnecting_api
create an rpc with the following parameters:
ReconnectingRpcClient::builder().retry_policy(ExponentialBackoff::from_millis(1000).max_delay(Duration::from_secs(3)).take(3))
Accounts
There are already premade accounts available in the SDK interface. There is one as well for Bob, Eve, and Charlie.
use avail_rust::SDK;
let account = SDK::alice()?;
Payload
Manually passing the pallet name, call name and call data is error prone and that's why there is an better way.
All the payloads are defined in the following path avail_rust::avail::tx().*().**(data)
where *
is the pallet name and **
is the call type.
For more examples go to the next page.
use avail_rust::avail::runtime_types::bounded_collections::bounded_vec::BoundedVec;
let data = String::from("My Data").into_bytes();
let data = BoundedVec(data);
let payload = avail_rust::avail::tx()
.data_availability()
.submit_data(data);
Transaction Parameters, Signature, Submission
Transaction parameters, signature, and submission can be combined all into one single call.
Because we are using the default transaction parameters, we are passing None
as the argument. If we wish to alter the parameters, we would pass an optional Options
object.
use avail_rust::transaction::utils::sign_and_send;
let tx_hash = sign_and_send(&online_client, &rpc_client, &account, &payload, None).await?;
Watcher
Just like the rest, the watching part can be abstracted as well.
Finding if a transaction was successful or not is now just a matter of calling is_successful()
. If the transaction failed, it will return an error with the description on why it failed.
The last arguments, Some(3)
, tells the watcher to read the next 4 (this is not a typo, it's X + 1) blocks and if none of the contains the target transaction hash it will return an error.
use avail_rust::{transaction::utils::watch, WaitFor};
let tx_details = watch(&online_client, tx_hash, WaitFor::BlockInclusion, Some(3)).await?;
println!("Transaction was found.");
println!("Block Hash: {:?}", tx_details.block_hash); // Block Hash: 0x61415b6012005665bac0cf8575a94e509d079a762be2ba6a71a04633efd01c1b
println!("Block Number: {:?}", tx_details.block_number); // Block Number: 200
println!("Tx Hash: {:?}", tx_details.tx_hash); // Tx Hash: 0x01651a93d55bde0f258504498d4f2164416df5331794d9c905d4c8711d9537ef
println!("Tx Index: {:?}", tx_details.tx_index); // Tx Index: 1
println!("Event count: {}", tx_details.events.iter().count()); // Event count: 7
match tx_details.is_successful(&online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
Source Code
use avail_rust::error::ClientError;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
// RPC Connection
// ANCHOR: connection
use avail_rust::sdk::reconnecting_api;
let endpoint = "ws://127.0.0.1:9944";
let (online_client, rpc_client) = reconnecting_api(endpoint).await?;
// ANCHOR_END: connection
// Accounts
// ANCHOR: accounts
use avail_rust::SDK;
let account = SDK::alice()?;
// ANCHOR_END: accounts
// Payload
// ANCHOR: payload
use avail_rust::avail::runtime_types::bounded_collections::bounded_vec::BoundedVec;
let data = String::from("My Data").into_bytes();
let data = BoundedVec(data);
let payload = avail_rust::avail::tx()
.data_availability()
.submit_data(data);
// ANCHOR_END: payload
// Transaction Params, Signature, Submission
// ANCHOR: signsend
use avail_rust::transaction::utils::sign_and_send;
let tx_hash = sign_and_send(&online_client, &rpc_client, &account, &payload, None).await?;
// ANCHOR_END: signsend
// Watcher
// ANCHOR: watcher
use avail_rust::{transaction::utils::watch, WaitFor};
let tx_details = watch(&online_client, tx_hash, WaitFor::BlockInclusion, Some(3)).await?;
println!("Transaction was found.");
println!("Block Hash: {:?}", tx_details.block_hash); // Block Hash: 0x61415b6012005665bac0cf8575a94e509d079a762be2ba6a71a04633efd01c1b
println!("Block Number: {:?}", tx_details.block_number); // Block Number: 200
println!("Tx Hash: {:?}", tx_details.tx_hash); // Tx Hash: 0x01651a93d55bde0f258504498d4f2164416df5331794d9c905d4c8711d9537ef
println!("Tx Index: {:?}", tx_details.tx_index); // Tx Index: 1
println!("Event count: {}", tx_details.events.iter().count()); // Event count: 7
match tx_details.is_successful(&online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
// ANCHOR_END: watcher
Ok(())
}
Transaction - 2.1
Here you can find additional examples on how to correctly construct the payload.
use avail_rust::error::ClientError;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
data_availability_create_key().await?;
balances_transfer_keep_alive().await?;
identity_set_identity().await?;
// Payload
Ok(())
}
async fn data_availability_create_key() -> Result<(), ClientError> {
let key = String::from("My Data").into_bytes();
let key = avail_rust::BoundedVec(key);
let _payload = avail_rust::avail::tx()
.data_availability()
.create_application_key(key);
Ok(())
}
async fn balances_transfer_keep_alive() -> Result<(), ClientError> {
let dest = avail_rust::account::account_id_from_str(
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
)?; // Bob
let value = avail_rust::SDK::one_avail();
let _payload = avail_rust::avail::tx()
.balances()
.transfer_keep_alive(dest.into(), value);
Ok(())
}
async fn identity_set_identity() -> Result<(), ClientError> {
use avail_rust::avail::{
identity::calls::types::set_identity::Info,
runtime_types::{
bounded_collections::bounded_vec::BoundedVec, pallet_identity::types::Data,
},
};
let display_name: [u8; 7] = String::from("My Name").into_bytes().try_into().unwrap();
let info = Info {
additional: BoundedVec(vec![(Data::None, Data::None)]),
display: Data::Raw7(display_name),
legal: Data::None,
web: Data::None,
riot: Data::None,
email: Data::None,
pgp_fingerprint: None,
image: Data::None,
twitter: Data::None,
};
let _payload = avail_rust::avail::tx().identity().set_identity(info);
Ok(())
}
Transaction - 3
We are not done yet.
Setting up the stage
Here we are using the prelude import to import all the necessary type declarations.
use avail_rust::prelude::*;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
// Code goes here
Ok(())
}
Connection
Both online_client and rpc_client can be grouped together and now we are using a wrapper to get the local node endpoint instead of writing it manually.
Together with local_endpoint
we have the following other endpoints:
local_http_endpoint
turing_endpoint
turing_http_endpoint
mainnet_endpoint
mainnet_http_endpoint
let sdk = SDK::new(SDK::local_endpoint()).await?;
new()
method will use a reconnecting websocket rpc. If just HTTP is needed then new_http()
will do the trick otherwise for custom clients new_custom(online_client: AOnlineClient, rpc_client: RpcClient)
can be used.
Payload
Payload creation has been abstracted away and can now be accessed via sdk.tx.*.**
path where *
is pallet name and **
is call name. Not all transaction have been abstracted away and for some you will still need to use the following path avail_rust::avail::tx().*().**()
.
let data = String::from("My Data").into_bytes();
let tx = sdk.tx.data_availability.submit_data(data);
The object that is returned by the interface has many different helper functions attach to it. Make sure to check them out.
For some of them you will need to import either avail_rust::transaction::WebSocket
or avail_rust::transaction::HTTP
in order to get access to the desired execute_*
call.
The prelude import automatically imports the WebSocket
one and if that's not desired then you will need avoid prelude import and manually import all the type declarations.
Transaction Parameters, Signature, Submission, Watcher
The watcher is now combined with transaction submission. We can now choose if we want to wait for block inclusion, block finalization, and or fire and forget.
If we choose to wait, the system will automatically resubmit our transaction in case it didn't found it the next X blocks by using the same transaction parameters.
WebSocket and HTTP interface differ here in types and implementation:
- WebSocket uses block subscription to fetch blocks.
- HTTP cannot use block subscription because that's a websocket features thus we are forced to fetch headers every N (set to 3 by default) seconds in order to know if a new block has been produced.
The default 3 seconds (sleep_duration) for HTTP can be configured by calling execute_and_watch
instead of execute_and_watch_inclusion
or execute_and_watch_finalization
.
let tx_details = tx.execute_and_watch_inclusion(&account, None).await?;
println!("Transaction was found.");
println!("Block Hash: {:?}", tx_details.block_hash); // Block Hash: 0x61415b6012005665bac0cf8575a94e509d079a762be2ba6a71a04633efd01c1b
println!("Block Number: {:?}", tx_details.block_number); // Block Number: 200
println!("Tx Hash: {:?}", tx_details.tx_hash); // Tx Hash: 0x01651a93d55bde0f258504498d4f2164416df5331794d9c905d4c8711d9537ef
println!("Tx Index: {:?}", tx_details.tx_index); // Tx Index: 1
println!("Event count: {}", tx_details.events.iter().count()); // Event count: 7
match tx_details.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
Source Code
use avail_rust::prelude::*;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
// RPC Connection
// ANCHOR: connection
let sdk = SDK::new(SDK::local_endpoint()).await?;
// ANCHOR_END: connection
// Accounts
let account = SDK::alice()?;
// Payload
// ANCHOR: payload
let data = String::from("My Data").into_bytes();
let tx = sdk.tx.data_availability.submit_data(data);
// ANCHOR_END: payload
// Transaction Params, Signature, Submission, Watcher
// ANCHOR: signsend
let tx_details = tx.execute_and_watch_inclusion(&account, None).await?;
println!("Transaction was found.");
println!("Block Hash: {:?}", tx_details.block_hash); // Block Hash: 0x61415b6012005665bac0cf8575a94e509d079a762be2ba6a71a04633efd01c1b
println!("Block Number: {:?}", tx_details.block_number); // Block Number: 200
println!("Tx Hash: {:?}", tx_details.tx_hash); // Tx Hash: 0x01651a93d55bde0f258504498d4f2164416df5331794d9c905d4c8711d9537ef
println!("Tx Index: {:?}", tx_details.tx_index); // Tx Index: 1
println!("Event count: {}", tx_details.events.iter().count()); // Event count: 7
match tx_details.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
// ANCHOR_END: signsend
Ok(())
}
Transaction - 4
The SDK offer much more than just a couple of helper functions for transaction submission. To get a better understanding on what can be done, check all the other examples, especially the following ones:
Data Submission
- This one is a mustBlock
Transactions
Events
Not everything is shown in our examples. Open the SDK and take a look what interfaces are available.
Events and Block
Here is just a sneak peak on the events and block api that we offer.
use avail_rust::avail::data_availability::events::ApplicationKeyCreated;
let Some(event) = tx_details.find_first_event::<ApplicationKeyCreated>() else {
return Err("Failed to find event".into());
};
println!("App id: {}", event.id.0);
let block = Block::new(&sdk.online_client, tx_details.block_hash).await?;
let tx_count = block.transactions.iter().count();
println!("Transaction count in a block: {}", tx_count);
Transaction with custom payload
Transaction interface can be created using custom payload.
// ! Check Transaction 1(basics_1) or Transaction 2(basics_2) example for custom payload. !
let data = String::from("Data").into_bytes();
let data = BoundedVec(data);
let payload = avail_rust::avail::tx()
.data_availability()
.submit_data(data);
let tx = Transaction::new(sdk.online_client.clone(), sdk.rpc_client.clone(), payload);
let tx_details = tx.execute_and_watch_inclusion(&account, None).await?;
// Checking if the transaction was successful
match tx_details.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
Source Code
use avail_rust::prelude::*;
#[tokio::main]
async fn main() -> Result<(), ClientError> {
// RPC Connection
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Accounts
let account = SDK::alice()?;
// ANCHOR: success
let key = String::from("My Data").into_bytes();
let tx = sdk.tx.data_availability.create_application_key(key);
let tx_details = tx.execute_and_watch_inclusion(&account, None).await?;
// Checking if the transaction was successful
match tx_details.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
// ANCHOR_END: success
// Finding ApplicationKeyCreated event
// ANCHOR: event
use avail_rust::avail::data_availability::events::ApplicationKeyCreated;
let Some(event) = tx_details.find_first_event::<ApplicationKeyCreated>() else {
return Err("Failed to find event".into());
};
println!("App id: {}", event.id.0);
// ANCHOR_END: event
// Fetching block
// ANCHOR: block
let block = Block::new(&sdk.online_client, tx_details.block_hash).await?;
let tx_count = block.transactions.iter().count();
println!("Transaction count in a block: {}", tx_count);
// ANCHOR_END: block
// Using custom payload with Transaction object
// ANCHOR: custompayload
// ! Check Transaction 1(basics_1) or Transaction 2(basics_2) example for custom payload. !
let data = String::from("Data").into_bytes();
let data = BoundedVec(data);
let payload = avail_rust::avail::tx()
.data_availability()
.submit_data(data);
let tx = Transaction::new(sdk.online_client.clone(), sdk.rpc_client.clone(), payload);
let tx_details = tx.execute_and_watch_inclusion(&account, None).await?;
// Checking if the transaction was successful
match tx_details.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
// ANCHOR_END: custompayload
Ok(())
}
RPC and Fee Details
use avail_rust::{prelude::*, primitives::kate, utils};
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// author_rotate_keys
let value = rpc::author::rotate_keys(&sdk.rpc_client).await?;
let value = utils::deconstruct_session_keys(value)?;
dbg!(value);
/* Output
SessionKeys {
babe: Public(...),
grandpa: Public(...),
im_online: Public(...),
authority_discovery: Public(...),
}
*/
// author_submit_extrinsic
let account = SDK::alice()?;
let account_id = account.public_key().to_account_id();
let call = avail::tx()
.data_availability()
.submit_data(BoundedVec(vec![0, 1, 2]));
let params = Options::new()
.build(&sdk.online_client, &sdk.rpc_client, &account_id)
.await?
.build()
.await?;
let signed_call = sdk
.online_client
.tx()
.create_signed(&call, &account, params)
.await?;
let extrinsic = signed_call.encoded();
let value = rpc::author::submit_extrinsic(&sdk.rpc_client, extrinsic).await?;
dbg!(value);
/* Output
"0x56edc7516bb403f0d812f0f91dea5e36b46bbb31f7b69e78469652f74882377d"
*/
// chain_get_block
let value = rpc::chain::get_block(&sdk.rpc_client, None).await?;
dbg!(value);
/* Output
BlockDetails {
block: Block {
header: AvailHeader {
parent_hash: 0x4753c70a0652f50ee24f19ea402c1377ce5ab08fc5e0f801123e8116e5e1fcf8,
number: 495,
state_root: 0x22470c3402bee3cd95c10b9303e61019aaec0603cbfc197eca646c94ba9332f1,
extrinsics_root: 0x609ed0e14f3252c9f59ab59004ea458d7927a5bd81f241651634266b7098f415,
digest: Digest {...},
extension: V3(
HeaderExtension {
app_lookup: CompactDataLookup {
size: 0,
index: [],
},
commitment: KateCommitment {
rows: 0,
cols: 0,
commitment: [],
data_root: 0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5,
},
},
),
},
extrinsics: [...],
},
justifications: None,
}
*/
// chain_get_block_hash
let value = rpc::chain::get_block_hash(&sdk.rpc_client, None).await?;
dbg!(value);
/* Output
0xc4e0a9a2ef80ddc1d70c9946d8a6f86ca4b15053b39ba56709222f01ddc64561
*/
// chain_get_finalized_head
let value = rpc::chain::get_finalized_head(&sdk.rpc_client).await?;
dbg!(value);
/* Output
0x2c896c9faae4e111f1fbeb955be5e999a328846969b59a7a7c64eadc4701122a
*/
// chain_get_header
let value = rpc::chain::get_header(&sdk.rpc_client, None).await?;
dbg!(value);
/* Output
AvailHeader {
parent_hash: 0x4753c70a0652f50ee24f19ea402c1377ce5ab08fc5e0f801123e8116e5e1fcf8,
number: 495,
state_root: 0x22470c3402bee3cd95c10b9303e61019aaec0603cbfc197eca646c94ba9332f1,
extrinsics_root: 0x609ed0e14f3252c9f59ab59004ea458d7927a5bd81f241651634266b7098f415,
digest: Digest {...},
extension: V3(
HeaderExtension {
app_lookup: CompactDataLookup {
size: 0,
index: [],
},
commitment: KateCommitment {
rows: 0,
cols: 0,
commitment: [],
data_root: 0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5,
},
},
),
}
*/
// system_account_next_index
let account = String::from("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY");
let value = rpc::system::account_next_index(&sdk.rpc_client, account).await?;
dbg!(value);
/* Output
2
*/
// system_chain
let value = rpc::system::chain(&sdk.rpc_client).await?;
dbg!(value);
/* Output
"Avail Development Network"
*/
// system_chain_type
let value = rpc::system::chain_type(&sdk.rpc_client).await?;
dbg!(value);
/* Output
"Development"
*/
// system_health
let value = rpc::system::health(&sdk.rpc_client).await?;
dbg!(value);
/* Output
SystemHealth {
peers: 0,
is_syncing: false,
should_have_peers: false,
}
*/
// system_local_listen_addresses
let value = rpc::system::local_listen_addresses(&sdk.rpc_client).await?;
dbg!(value);
/* Output
value = [
"/ip6/fe81::a234:6e32:1034:3c3b/tcp/30333/p2p/12D3KooWRajsCfp1NR15iN7PcwcFAG3LB7iGDKUBosHkevNRQLYs",
"/ip4/192.168.1.103/tcp/30333/p2p/12D3KooWRajsCfp1NR15iN7PcwcFAG3LB7iGDKUBosHkevNRQLYs",
"/ip6/::1/tcp/30333/p2p/12D3KooWRajsCfp1NR15iN7PcwcFAG3LB7iGDKUBosHkevNRQLYs",
"/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWRajsCfp1NR15iN7PcwcFAG3LB7iGDKUBosHkevNRQLYs",
]
*/
// system_local_peer_id
let value = rpc::system::local_peer_id(&sdk.rpc_client).await?;
dbg!(value);
/* Output
"12D3KooWRajsCfp1NR15iN7PcwcFAG3LB7iGDKUBosHkevNRQLYs"
*/
// system_name
let value = rpc::system::name(&sdk.rpc_client).await?;
dbg!(value);
/* Output
"Avail Node"
*/
// system_node_roles
let value = rpc::system::node_roles(&sdk.rpc_client).await?;
dbg!(value);
/* Output
[
Authority,
]
*/
// system_peers
let value = rpc::system::peers(&sdk.rpc_client).await?;
dbg!(value);
/* Output
[]
*/
// system_properties
let value = rpc::system::properties(&sdk.rpc_client).await?;
dbg!(value);
/* Output
{
"ss58Format": Number(42),
"tokenDecimals": Number(18),
"tokenSymbol": String("AVAIL"),
}
*/
// system_system_sync_state
let value = rpc::system::sync_state(&sdk.rpc_client).await?;
dbg!(value);
/* Output
SyncState {
starting_block: 0,
current_block: 495,
highest_block: 495,
}
*/
// system_version
let value = rpc::system::version(&sdk.rpc_client).await?;
dbg!(value);
/* Output
"2.2.1-55da578d34b"
*/
// TransactionPaymentApi_query_info
let payload = avail::tx()
.data_availability()
.submit_data(BoundedVec(vec![1]));
let keypair = SDK::alice()?;
let account = keypair.public_key().to_account_id();
let options = Options::new().app_id(1);
let populated_options = options
.build(&sdk.online_client, &sdk.rpc_client, &account)
.await?;
let params = populated_options.build().await?;
let tx = sdk
.online_client
.tx()
.create_signed(&payload, &keypair, params)
.await?;
let partial_fee_estimate = tx.partial_fee_estimate().await?;
dbg!(partial_fee_estimate);
/* Output
124684322202721409
*/
// TransactionPaymentApi_query_fee_details
let len_bytes: [u8; 4] = (tx.encoded().len() as u32).to_le_bytes();
let encoded_with_len = [tx.encoded(), &len_bytes[..]].concat();
let fee_details =
rpc::payment::query_fee_details(&sdk.rpc_client, encoded_with_len.into(), None).await?;
dbg!(fee_details);
/* Output
FeeDetails {
inclusion_fee: Some(
InclusionFee {
base_fee: 124414000000000000,
len_fee: 11400000000000,
adjusted_weight_fee: 259321813738397,
},
),
tip: 0,
}
*/
// state_get_runtime_version
let value = rpc::state::get_runtime_version(&sdk.rpc_client, None).await?;
dbg!(value);
/* Output
RuntimeVersion {
spec_version: 39,
transaction_version: 1,
other: {
"stateVersion": Number(1),
"authoringVersion": Number(12),
"specName": String("avail"),
"implVersion": Number(0),
"apis": Array [...],
"implName": String("avail"),
},
}
*/
// kate_block_length
let value = rpc::kate::block_length(&sdk.rpc_client, None).await?;
dbg!(value);
/* Output
BlockLength {
max: PerDispatchClass {
normal: 2097152,
operational: 2097152,
mandatory: 2097152,
},
cols: BlockLengthColumns(
256,
),
rows: BlockLengthRows(
256,
),
chunk_size: 32,
}
*/
// kate_query_data_proof
let data = String::from("My Data").into_bytes();
let tx = sdk.tx.data_availability.submit_data(data);
let result = tx.execute_and_watch_finalization(&keypair, None).await?;
let (tx_index, block_hash) = (result.tx_index, Some(result.block_hash));
let value = rpc::kate::query_data_proof(&sdk.rpc_client, tx_index, block_hash).await?;
dbg!(value);
/* Output
ProofResponse {
data_proof: DataProof {
roots: TxDataRoots {
data_root: 0xd6e516bbf0b0d964a6a6a41a18c58a2eac4757001c2338a8601c4cc961332fda,
blob_root: 0x29c73490baca9fe2b11095a69294de4b4a86bcb3a2eb3cd04b51dfdd0b4030f9,
bridge_root: 0x0000000000000000000000000000000000000000000000000000000000000000,
},
proof: [],
number_of_leaves: 1,
leaf_index: 0,
leaf: 0x47a59a7805e0bfe350ee0395d426c15770edc03fee72aa6532b5bbcffaf28030,
},
message: None,
}
*/
// kate_query_proof
let cells = vec![kate::Cell::from((0u32, 0u32))];
let value = rpc::kate::query_proof(&sdk.rpc_client, cells, block_hash).await?;
dbg!(value);
/* Output
[
(
2178534751726990040338027377623275511556638494274780568875624948149315822336,
GProof(
[...],
),
),
]
*/
// kate_query_rows
let rows = vec![0u32];
let value = rpc::kate::query_rows(&sdk.rpc_client, rows, block_hash).await?;
dbg!(value);
/* Output
[
[
2178534751726990040338027377623275511556638494274780568875624948149315822336,
69809044805081050561201039752112594468796256047454289799440609083602104564736,
26941852917393734161602180963833199552029986735939578666038548832600818441216,
14351520191331507525755130937317610561547699892218140156652644610507664261120,
],
]
*/
Ok(())
}
Storage
use avail_rust::{account, avail, error::ClientError, AccountId, Block, SDK};
pub async fn run() -> Result<(), ClientError> {
println!("da_app_keys");
da_app_keys().await?;
println!("da_app_keys_iter");
da_app_keys_iter().await?;
println!("da_next_app_id");
da_next_app_id().await?;
println!("staking_active_era");
staking_active_era().await?;
println!("staking_bonded");
staking_bonded().await?;
println!("staking_bonded_iter");
staking_bonded_iter().await?;
println!("system_account");
system_account().await?;
println!("system_account_iter");
system_account_iter().await?;
Ok(())
}
pub async fn da_app_keys() -> Result<(), ClientError> {
use avail::data_availability::storage::types::app_keys::Param0;
let sdk = SDK::new(SDK::local_endpoint()).await?;
let (online_client, rpc_client) = (&sdk.online_client, &sdk.rpc_client);
let key = String::from("Reserved-1").as_bytes().to_vec();
let key = Param0 { 0: key };
let block_hash = Block::fetch_best_block_hash(rpc_client).await?;
let storage = online_client.storage().at(block_hash);
let address = avail::storage().data_availability().app_keys(key);
let result = storage.fetch(&address).await?;
dbg!(result);
/* Output
AppKeyInfo {
owner: AccountId32(...),
id: AppId(
1,
),
}
*/
Ok(())
}
pub async fn da_app_keys_iter() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let (online_client, rpc_client) = (&sdk.online_client, &sdk.rpc_client);
let block_hash = Block::fetch_best_block_hash(rpc_client).await?;
let storage = online_client.storage().at(block_hash);
let address = avail::storage().data_availability().app_keys_iter();
let mut results = storage.iter(address).await?;
while let Some(Ok(kv)) = results.next().await {
let key = (&kv.key_bytes[49..]).to_vec();
let key = String::from_utf8(key).unwrap();
println!("Key: {:?}", key);
println!("Value: {:?}", kv.value);
}
/* Output
Key: "Reserved-2"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(2) }
Key: "Reserved-8"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(8) }
Key: "Reserved-1"
Value: AppKeyInfo { owner: AccountId32(...) id: AppId(1) }
Key: "Reserved-9"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(9) }
Key: "Reserved-4"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(4) }
Key: "Reserved-5"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(5) }
Key: "Reserved-7"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(7) }
Key: "Avail"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(0) }
Key: "Reserved-3"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(3) }
Key: "Reserved-6"
Value: AppKeyInfo { owner: AccountId32(...), id: AppId(6) }
*/
Ok(())
}
pub async fn da_next_app_id() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let (online_client, rpc_client) = (&sdk.online_client, &sdk.rpc_client);
let block_hash = Block::fetch_best_block_hash(rpc_client).await?;
let storage = online_client.storage().at(block_hash);
let address = avail::storage().data_availability().next_app_id();
let result = storage.fetch_or_default(&address).await?;
dbg!(result);
/* Output
AppId(10)
*/
Ok(())
}
pub async fn staking_active_era() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let (online_client, rpc_client) = (&sdk.online_client, &sdk.rpc_client);
let block_hash = Block::fetch_best_block_hash(rpc_client).await?;
let storage = online_client.storage().at(block_hash);
let address = avail::storage().staking().active_era();
let result = storage.fetch(&address).await?;
dbg!(result);
/* Output
ActiveEraInfo {
index: 13,
start: Some(
1732612788000,
),
}
*/
Ok(())
}
pub async fn staking_bonded() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let (online_client, rpc_client) = (&sdk.online_client, &sdk.rpc_client);
let account_id =
account::account_id_from_str("5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY")?; // Alice_Stash
let block_hash = Block::fetch_best_block_hash(rpc_client).await?;
let storage = online_client.storage().at(block_hash);
let address = avail::storage().staking().bonded(account_id);
let result = storage.fetch(&address).await?;
dbg!(result);
/* Output
AccountId32(...)
*/
Ok(())
}
pub async fn staking_bonded_iter() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let (online_client, rpc_client) = (&sdk.online_client, &sdk.rpc_client);
let block_hash = Block::fetch_best_block_hash(rpc_client).await?;
let storage = online_client.storage().at(block_hash);
let storage_query = avail::storage().staking().bonded_iter();
let mut results = storage.iter(storage_query).await?;
while let Some(Ok(kv)) = results.next().await {
let key = kv.key_bytes.last_chunk::<32>().unwrap();
let key = AccountId::from(*key);
println!("Key: {:?}", key.to_string());
println!("Value: {:?}", kv.value);
}
/* Output
Key: "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY"
Value: AccountId32(...)
*/
Ok(())
}
pub async fn system_account() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let (online_client, rpc_client) = (&sdk.online_client, &sdk.rpc_client);
let account = SDK::alice()?;
let account_id = account.public_key().to_account_id();
let block_hash = Block::fetch_best_block_hash(rpc_client).await?;
let storage = online_client.storage().at(block_hash);
let address = avail::storage().system().account(account_id);
let result = storage.fetch(&address).await?;
if let Some(account) = result {
println!("Consumers: {}", account.consumers);
println!("Data: {:?}", account.data);
println!("Nonce: {}", account.nonce);
println!("Providers: {}", account.providers);
println!("Sufficients: {}", account.sufficients);
}
/* Output
Consumers: 0
Data: AccountData { free: 10000000000000000000000000, reserved: 0, frozen: 0, flags: ExtraFlags(170141183460469231731687303715884105728) }
Nonce: 0
Providers: 1
Sufficients: 0
*/
Ok(())
}
pub async fn system_account_iter() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let (online_client, rpc_client) = (&sdk.online_client, &sdk.rpc_client);
let block_hash = Block::fetch_best_block_hash(rpc_client).await?;
let storage = online_client.storage().at(block_hash);
let address = avail::storage().system().account_iter();
let mut results = storage.iter(address).await?;
while let Some(Ok(kv)) = results.next().await {
let key = kv.key_bytes.last_chunk::<32>().unwrap();
let key = AccountId::from(*key);
println!("Key: {:?}", key.to_string());
println!("Value: {:?}", kv.value);
}
/* Output
Key: "5FCfAonRZgTFrTd9HREEyeJjDpT397KMzizE6T3DvebLFE7n"
Value: AccountInfo { nonce: 0, consumers: 0, providers: 1, sufficients: 0, data: AccountData { free: 10000000000000000000000000, reserved: 0, frozen: 0, flags: ExtraFlags(170141183460469231731687303715884105728) } }
Key: "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"
Value: AccountInfo { nonce: 0, consumers: 0, providers: 1, sufficients: 0, data: AccountData { free: 10000000000000000000000000, reserved: 0, frozen: 0, flags: ExtraFlags(170141183460469231731687303715884105728) } }
Key: "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY"
Value: AccountInfo { nonce: 0, consumers: 3, providers: 1, sufficients: 0, data: AccountData { free: 10000001075151923366255874, reserved: 0, frozen: 100000000000000000000000, flags: ExtraFlags(170141183460469231731687303715884105728) } }
Key: "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
Value: AccountInfo { nonce: 0, consumers: 0, providers: 1, sufficients: 0, data: AccountData { free: 10000000000000000000000000, reserved: 0, frozen: 0, flags: ExtraFlags(170141183460469231731687303715884105728) } }
...
*/
Ok(())
}
Account Nonce
use avail_rust::{account, avail, error::ClientError, Block, SDK};
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let account_id = account.public_key().to_account_id();
let account_address = account_id.to_string();
// Fetch nonce from Node (this includes Tx Pool)
let nonce = account::fetch_nonce_node(&sdk.rpc_client, &account_address).await?;
println!("Nonce from Node: {}", nonce);
// Fetch nonce from best block state
let nonce =
account::fetch_nonce_state(&sdk.online_client, &sdk.rpc_client, &account_address, None)
.await?;
println!("Nonce from best block state: {}", nonce);
// Fetch nonce from custom block state
let block_hash = Block::fetch_finalized_block_hash(&sdk.rpc_client).await?;
let block = sdk.online_client.blocks().at(block_hash).await?;
let nonce = block.account_nonce(&account_id).await? as u32;
println!("Nonce from custom block state: {}", nonce);
// Fetch nonce from manually reading storage
let storage = sdk.online_client.storage().at(block_hash);
let address = avail::storage().system().account(account_id);
let result = storage.fetch_or_default(&address).await?;
println!("Nonce from manually reading storage: {}", result.nonce);
Ok(())
}
/*
Example Output:
Nonce from Node: 1
Nonce from best block state: 1
Nonce from custom block state: 1
Nonce from manually reading storage: 1
*/
Transaction Options
use avail_rust::prelude::*;
use std::time::Duration;
pub async fn run() -> Result<(), ClientError> {
nonce().await?;
app_id().await?;
tip().await?;
mortality().await?;
Ok(())
}
async fn nonce() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let account_address = account.public_key().to_account_id().to_string();
let dest = account::account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")?;
let mut options = Options::new();
let tx = sdk.tx.balances.transfer_keep_alive(dest, SDK::one_avail());
/*
Using finalized block nonce will not take into consideration nonces from non-finalized blocks.
*/
options = options.nonce(Nonce::FinalizedBlock);
tx.execute(&account, Some(options)).await?;
tx.execute(&account, Some(options)).await.expect_err("qed");
wait_n_blocks(&sdk, 3).await?;
/*
Using best block nonce will not take into consideration existing transactions in the
tx pool.
*/
options = options.nonce(Nonce::BestBlock);
tx.execute(&account, Some(options)).await?;
tx.execute(&account, Some(options)).await.expect_err("qed");
wait_n_blocks(&sdk, 1).await?;
/*
This is the most commonly used nonce. If correctness is needed, use `Nonce::FinalizedBlock`
This is the default behavior,
*/
options = options.nonce(Nonce::BestBlockAndTxPool);
tx.execute(&account, Some(options)).await?;
tx.execute(&account, Some(options)).await?;
/*
Managing the nonce manually
*/
let nonce = account::fetch_nonce_node(&sdk.rpc_client, &account_address).await?;
options = options.nonce(Nonce::Custom(nonce));
tx.execute(&account, Some(options)).await?;
options = options.nonce(Nonce::Custom(nonce + 1));
tx.execute(&account, Some(options)).await?;
Ok(())
}
async fn app_id() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let options = Options::new().app_id(1);
let res = tx
.execute_and_watch_inclusion(&account, Some(options))
.await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
Ok(())
}
async fn tip() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let dest = account::account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")?;
let tx = sdk.tx.balances.transfer_keep_alive(dest, SDK::one_avail());
let options = Options::new().tip(1);
let res = tx
.execute_and_watch_inclusion(&account, Some(options))
.await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
Ok(())
}
async fn mortality() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let dest = account::account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")?;
let tx = sdk.tx.balances.transfer_keep_alive(dest, SDK::one_avail());
let period = 3;
let block_hash = None;
let mortality = Mortality::new(period, block_hash);
let options = Options::new().mortality(mortality);
let res = tx
.execute_and_watch_inclusion(&account, Some(options))
.await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
Ok(())
}
async fn wait_n_blocks(sdk: &SDK, n: u32) -> Result<(), ClientError> {
let mut expected_block_number = None;
loop {
let current_block = rpc::chain::get_block(&sdk.rpc_client, None).await?;
let current_block_number = current_block.block.header.number;
if expected_block_number.is_none() {
expected_block_number = Some(current_block_number + n);
}
if expected_block_number.is_some_and(|x| x <= current_block_number) {
break;
}
tokio::time::sleep(Duration::from_secs(3)).await
}
Ok(())
}
Transaction Payment
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let key = String::from("My Key").into_bytes();
let tx = sdk.tx.data_availability.create_application_key(key);
let options = Options::new();
let fee_details = tx
.payment_query_fee_details(&account, Some(options))
.await?;
let query_info = tx.payment_query_info(&account, Some(options)).await?;
dbg!(fee_details);
dbg!(query_info);
Ok(())
}
/*
Example Output:
fee_details = FeeDetails {
inclusion_fee: Some(
InclusionFee {
base_fee: 124414000000000000,
len_fee: 11900000000000,
adjusted_weight_fee: 2743751768732346,
},
),
tip: 0,
query_info = 127169255884363086
}
*/
Batch
use avail_rust::prelude::*;
use avail::{
runtime_types::{
da_runtime::RuntimeCall,
pallet_balances::pallet::Call::transfer_keep_alive as TransferKeepAlive,
},
system::events as SystemEvents,
utility::events as UtilityEvents,
};
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let value_1 = SDK::one_avail();
let value_2 = SDK::one_avail() * 100_000_000;
let dest_bob =
account::account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")?;
let dest_charlie =
account::account_id_from_str("5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y")?;
let call_1 = TransferKeepAlive {
dest: dest_bob.into(),
value: value_1,
};
let call_1 = RuntimeCall::Balances(call_1);
let call_2 = TransferKeepAlive {
dest: dest_charlie.into(),
value: value_2,
};
let call_2 = RuntimeCall::Balances(call_2);
let calls = vec![call_1.into(), call_2.into()];
// Batch
// This will return `Ok` in all circumstances. To determine the success of the batch, an
// event is deposited. If a call failed and the batch was interrupted, then the
// `BatchInterrupted` event is deposited, along with the number of successful calls made
// and the error of the failed call. If all were successful, then the `BatchCompleted`
// event is deposited.
let payload = avail::tx().utility().batch(calls.clone());
let tx = Transaction::new(sdk.online_client.clone(), sdk.rpc_client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
println!("-- Batch Call --");
let batch_interrupted = res.find_event::<UtilityEvents::BatchInterrupted>();
if batch_interrupted.len() > 0 {
println!("At least one call has failed");
}
let batch_completed = res.find_first_event::<UtilityEvents::BatchCompleted>();
if batch_completed.is_some() {
println!("All calls were successful");
}
let batch_failed = res.find_first_event::<SystemEvents::ExtrinsicFailed>();
if batch_failed.is_some() {
println!("Batch call ExtrinsicFailed was emitted.");
}
let batch_success = res.find_first_event::<SystemEvents::ExtrinsicSuccess>();
if batch_success.is_some() {
println!("Batch call ExtrinsicSuccess was emitted.");
}
// Batch All
// Send a batch of dispatch calls and atomically execute them.
// The whole transaction will rollback and fail if any of the calls failed.
let payload = avail::tx().utility().batch_all(calls.clone());
let tx = Transaction::new(sdk.online_client.clone(), sdk.rpc_client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x.expect_err("Batch All should fail"),
None => panic!("Failed to decode events."),
};
println!("-- Batch All Call --");
let batch_failed = res.find_first_event::<SystemEvents::ExtrinsicFailed>();
if batch_failed.is_some() {
println!("Batch All call ExtrinsicFailed was emitted.");
}
let batch_success = res.find_first_event::<SystemEvents::ExtrinsicSuccess>();
if batch_success.is_some() {
println!("Batch All call ExtrinsicSuccess was emitted.");
}
// Force Batch
// Send a batch of dispatch calls.
// Unlike `batch`, it allows errors and won't interrupt.
let payload = avail::tx().utility().force_batch(calls.clone());
let tx = Transaction::new(sdk.online_client.clone(), sdk.rpc_client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
println!("-- Force Batch Call --");
let item_failed = res.find_event::<UtilityEvents::ItemFailed>();
if item_failed.len() > 0 {
println!("At least one call has failed");
}
let batch_completed_with_error =
res.find_first_event::<UtilityEvents::BatchCompletedWithErrors>();
if batch_completed_with_error.is_some() {
println!("Batch completed even though one or more calls have failed.");
}
let batch_completed = res.find_first_event::<UtilityEvents::BatchCompleted>();
if batch_completed.is_some() {
println!("All calls were successful");
}
let batch_failed = res.find_first_event::<SystemEvents::ExtrinsicFailed>();
if batch_failed.is_some() {
println!("Force Batch call ExtrinsicFailed was emitted.");
}
let batch_success = res.find_first_event::<SystemEvents::ExtrinsicSuccess>();
if batch_success.is_some() {
println!("Force Batch call ExtrinsicSuccess was emitted.");
}
Ok(())
}
/*
Example Output:
-- Batch Call --
At least one call has failed
Batch call ExtrinsicSuccess was emitted.
-- Batch All Call --
Batch All call ExtrinsicFailed was emitted.
-- Force Batch Call --
At least one call has failed
Batch completed even though one or more calls have failed.
Force Batch call ExtrinsicSuccess was emitted.
*/
Batch
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Setup
let data = String::from("My Data").into_bytes();
let tx = sdk.tx.data_availability.submit_data(data);
let res = tx.execute_and_watch_inclusion(&SDK::alice()?, None).await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
// Fetching
// Fetching best block
_ = Block::new_best_block(&sdk.online_client, &sdk.rpc_client).await?;
// Fetching finalized block
_ = Block::new_finalized_block(&sdk.online_client, &sdk.rpc_client).await?;
// Fetching block with hex string or hash
let hex_string = std::format!("{:?}", res.block_hash);
let block_hash = avail_rust::utils::hex_string_to_h256(&hex_string)?;
_ = Block::new(&sdk.online_client, block_hash).await?;
// Fetching block with block number
let block_number = 0;
_ = Block::from_block_number(&sdk.online_client, &sdk.rpc_client, block_number);
// Transactions
let block = Block::new(&sdk.online_client, res.block_hash).await?;
// Filtering by Transaction Index
let tx = block
.transaction_by_index(res.tx_index)
.ok_or(String::from("Failed to find tx"))?;
println!(
"Tx Pallet name: {}, Tx Name: {}",
tx.pallet_name()?,
tx.variant_name()?,
);
// Filtering by Transaction Index with Call Data
use avail::data_availability::calls::types::SubmitData;
let tx = block
.transaction_by_index_static::<SubmitData>(res.tx_index)
.ok_or(String::from("Failed to find tx"))?;
println!(
"Tx Pallet name: {}, Tx Name: {}",
tx.details.pallet_name()?,
tx.details.variant_name()?,
);
println!("Tx Call Data: {:?}", tx.value.data);
/*
Available methods:
transaction_all_static
transaction_count
transaction_by_signer
transaction_by_signer_static
transaction_by_index
transaction_by_index_static
transaction_by_hash
transaction_by_hash_static
transaction_by_app_id
transaction_by_app_id_static
*/
// Data Submission
// Filtering by Transaction Index
let ds = block
.data_submissions_by_index(res.tx_index)
.ok_or(String::from("Failed to find ds"))?;
println!(
"Tx Hash: {:?}, Tx Index: {}, Data {:?}, Tx Signer: {:?}, App Id: {}",
ds.tx_hash, ds.tx_index, ds.data, ds.tx_signer, ds.app_id
);
/*
Available methods:
data_submissions_all
data_submissions_by_signer
data_submissions_by_index
data_submissions_by_hash
data_submissions_by_app_id
*/
// Fetching all events from a block
let events = block.events(None).await?;
let total_event_counts: usize = events.iter().map(|e| e.iter().count()).sum();
println!(
"Events Groups count: {}. Total events count: {}",
events.len(),
total_event_counts
);
// Fetching all events from a block for a specific transaction
let events = block.events(Some(res.tx_index)).await?;
let total_event_counts: usize = events.iter().map(|e| e.iter().count()).sum();
println!(
"Events Groups count: {}. Total events count: {}",
events.len(),
total_event_counts
);
// Finding the tx index with tx hash
let tx_index = block
.transaction_hash_to_index(res.tx_hash)
.ok_or(String::from("Failed to find index"))?;
assert_eq!(tx_index, res.tx_index);
let address = avail::storage().data_availability().next_app_id();
let app_id = block.storage_fetch_or_default(&address).await?.0;
println!("Next App Id: {}", app_id);
/*
Available methods:
storage_fetch
storage_fetch_or_default
storage_iter
*/
Ok(())
}
/*
Example Output:
Tx Pallet name: DataAvailability, Tx Name: submit_data
Tx Pallet name: DataAvailability, Tx Name: submit_data
Tx Call Data: BoundedVec([77, 121, 32, 68, 97, 116, 97])
Tx Hash: 0xf0439041a4138217d042c4d2ef75657b3b5c98cfaa2e85dcca94a47a65472a31, Tx Index: 1, Data [77, 121, 32, 68, 97, 116, 97], Tx Signer: [1, 196, 39, 196, 81, 65, 82, 28, 80, 157, 36, 247, 217, 186, 203, 75, 149, 165, 250, 33, 198, 34, 57, 111, 250, 41, 65, 249, 148, 110, 42, 154, 19, 117, 38, 169, 162, 154, 87, 118, 88, 122, 225, 157, 246, 91, 82, 9, 171, 86, 42, 197, 63, 218, 111, 241, 64, 24, 13, 155, 47, 143, 160, 74, 132], App Id: 0
Events Groups count: 3. Total events count: 9
Events Groups count: 1. Total events count: 7
Next App Id: 10
*/
Data Submission
use avail_rust::prelude::*;
type DataSubmissionCall = avail::data_availability::calls::types::SubmitData;
type ApplicationKeyCreatedEvent = avail::data_availability::events::ApplicationKeyCreated;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let online_client = &sdk.online_client;
let account = SDK::alice()?;
// Application Key Creation
let key = String::from("My Key").into_bytes();
let tx = sdk.tx.data_availability.create_application_key(key);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
let Some(event) = res.find_first_event::<ApplicationKeyCreatedEvent>() else {
return Err("Failed to get Application Key Created Event".into());
};
let app_id = event.id.0;
// Data Submission
let data = String::from("My Data").into_bytes();
let options = Options::new().app_id(app_id);
let tx = sdk.tx.data_availability.submit_data(data);
let res = tx
.execute_and_watch_inclusion(&account, Some(options))
.await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
println!(
"Block Hash: {:?}, Block Number: {}, Tx Hash: {:?}, Tx Index: {}",
res.block_hash, res.block_number, res.tx_hash, res.tx_index
);
let Some(call_data) = res.get_call_data::<DataSubmissionCall>(online_client).await else {
return Err("Failed to get Data Submission Call data".into());
};
println!("Call data: {:?}", call_data.data);
// Getting Data Submission from Block #1
let block = Block::new(online_client, res.block_hash).await?;
// data_submissions_by_signer, data_submissions_by_index, data_submissions_by_hash, data_submissions_by_app_id
let data_submissions = block.data_submissions_all();
for ds in data_submissions {
println!(
"Tx Hash: {:?}, Tx Index: {}, Data {:?}, Tx Signer: {:?}, App Id: {}",
ds.tx_hash, ds.tx_index, ds.data, ds.tx_signer, ds.app_id
);
println!("Ascii data: {}", ds.to_ascii().expect("qed"));
}
// Getting Data Submission from Block #2
for tx in block.transaction_all_static::<DataSubmissionCall>() {
println!("Call data: {:?}", tx.value.data);
let ds = DataSubmission::from_static(tx);
println!(
"Tx Hash: {:?}, Tx Index: {}, Data {:?}, Tx Signer: {:?}, App Id: {}",
ds.tx_hash, ds.tx_index, ds.data, ds.tx_signer, ds.app_id
);
println!("Ascii data: {}", ds.to_ascii().expect("qed"));
}
Ok(())
}
/*
Example Output:
Block Hash: 0x95db9a398c60358ade0504bef2eb6bf77c6cf05dee0525f43516cefab763b60f, Block Number: 485, Tx Hash: 0x6b4abd33d1452c0aa3d2fb9f4f4bbeb4f9d2d20b0b5bfb55696eea551974dcd3, Tx Index: 1
Call data: BoundedVec([77, 121, 32, 68, 97, 116, 97])
Tx Hash: 0x6b4abd33d1452c0aa3d2fb9f4f4bbeb4f9d2d20b0b5bfb55696eea551974dcd3, Tx Index: 1, Data [77, 121, 32, 68, 97, 116, 97], Tx Signer: [1, 202, 251, 108, 14, 95, 87, 191, 103, 174, 23, 201, 117, 10, 32, 139, 45, 55, 84, 14, 101, 67, 180, 132, 224, 20, 88, 26, 241, 244, 83, 32, 2, 45, 179, 41, 23, 165, 8, 7, 65, 52, 143, 32, 5, 60, 109, 132, 22, 89, 98, 198, 151, 88, 202, 92, 229, 70, 49, 127, 101, 254, 166, 81, 131], App Id: 12
Ascii data: My Data
Call data: BoundedVec([77, 121, 32, 68, 97, 116, 97])
Tx Hash: 0x6b4abd33d1452c0aa3d2fb9f4f4bbeb4f9d2d20b0b5bfb55696eea551974dcd3, Tx Index: 1, Data [77, 121, 32, 68, 97, 116, 97], Tx Signer: [1, 202, 251, 108, 14, 95, 87, 191, 103, 174, 23, 201, 117, 10, 32, 139, 45, 55, 84, 14, 101, 67, 180, 132, 224, 20, 88, 26, 241, 244, 83, 32, 2, 45, 179, 41, 23, 165, 8, 7, 65, 52, 143, 32, 5, 60, 109, 132, 22, 89, 98, 198, 151, 88, 202, 92, 229, 70, 49, 127, 101, 254, 166, 81, 131], App Id: 12
Ascii data: My Data
*/
Events
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let dest = account::account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")?;
let tx = sdk.tx.balances.transfer_keep_alive(dest, SDK::one_avail());
let res = tx.execute_and_watch_inclusion(&account, None).await?;
let events = match &res.events {
Some(x) => x,
None => panic!("Failed to decode events."),
};
for event in events.iter() {
let Ok(event) = event else {
return Ok(());
};
println!(
"Pallet name: {}, Event Name: {}",
event.pallet_name(),
event.variant_name()
);
}
// find_first_event, find_last_event, find_event
let event = res.find_first_event::<avail::balances::events::Transfer>();
let Some(event) = event else {
return Ok(());
};
println!(
"Transfer from: {}, to: {}, amount: {}",
event.from, event.to, event.amount
);
Ok(())
}
/*
Example Output:
Pallet name: Balances, Event Name: Withdraw
Pallet name: Balances, Event Name: Transfer
Pallet name: Balances, Event Name: Deposit
Pallet name: Balances, Event Name: Deposit
Pallet name: Balances, Event Name: Deposit
Pallet name: TransactionPayment, Event Name: TransactionFeePaid
Pallet name: System, Event Name: ExtrinsicSuccess
Transfer from: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY, to: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty, amount: 1000000000000000000
*/
Transactions
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::alice()?;
let dest = account::account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")?;
let tx = sdk.tx.balances.transfer_keep_alive(dest, SDK::one_avail());
let res = tx.execute_and_watch_inclusion(&account, None).await?;
let block = Block::new(&sdk.online_client, res.block_hash).await?;
// transaction_all_static, transaction_count, transaction_by_signer, transaction_by_signer_static
// transaction_by_index, transaction_by_index_static, transaction_by_hash,
// transaction_by_hash_static, transaction_by_app_id, transaction_by_app_id_static
for tx in block.transactions.iter() {
println!(
"Tx Pallet name: {}, Tx Name: {}, Tx Hash: {:?}",
tx.pallet_name()?,
tx.variant_name()?,
tx.hash()
);
for event in tx.events().await?.iter() {
let Ok(event) = event else {
return Ok(());
};
println!(
"\tEvent Pallet name: {}, Event Name: {}",
event.pallet_name(),
event.variant_name()
);
}
let balance_tx = tx.as_extrinsic::<avail::balances::calls::types::TransferKeepAlive>();
if let Some(tx) = balance_tx.ok().flatten() {
println!("Transfer dest: {:?}, value: {}", tx.dest, tx.value);
}
}
// Transaction object can be used with custom payload.
// ! Check Transaction 1(basics_1) or Transaction 2(basics_2) example for custom payload. !
let dest = account::account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty")?;
let payload = avail_rust::avail::tx()
.balances()
.transfer_keep_alive(dest.into(), SDK::one_avail());
let tx = Transaction::new(sdk.online_client.clone(), sdk.rpc_client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
// Checking if the transaction was successful
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
Ok(())
}
/*
Example Output:
Tx Pallet name: Timestamp, Tx Name: set, Tx Hash: 0xdf4e9c7ae69b40936b580ddf2d7c9b0cf5adb55e64f8492d1e160cc0914a8889
Event Pallet name: System, Event Name: ExtrinsicSuccess
Tx Pallet name: Balances, Tx Name: transfer_keep_alive, Tx Hash: 0x748057951ff79cea6de0e13b2ef70a1e9f443e9c83ed90e5601f8b45144a4ed4
Event Pallet name: Balances, Event Name: Withdraw
Event Pallet name: Balances, Event Name: Transfer
Event Pallet name: Balances, Event Name: Deposit
Event Pallet name: Balances, Event Name: Deposit
Event Pallet name: Balances, Event Name: Deposit
Event Pallet name: TransactionPayment, Event Name: TransactionFeePaid
Event Pallet name: System, Event Name: ExtrinsicSuccess
Transfer dest: Id(AccountId32([142, 175, 4, 21, 22, 135, 115, 99, 38, 201, 254, 161, 126, 37, 252, 82, 135, 97, 54, 147, 201, 18, 144, 156, 178, 38, 170, 71, 148, 242, 106, 72])), value: 1000000000000000000
Tx Pallet name: Vector, Tx Name: failed_send_message_txs, Tx Hash: 0x92cdb77314063a01930b093516d19a453399710cc8ae635ff5ab6cf76b26f218
Event Pallet name: System, Event Name: ExtrinsicSuccess
*/
Validator
use avail_rust::{
prelude::*,
transactions::staking::{Commission, RewardDestination},
utils,
};
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = SDK::charlie()?;
// Bond min_validator_bond or 1 AVAIL token
let storage = sdk.online_client.storage().at_latest().await?;
let min_validator_bond = storage
.fetch(&avail::storage().staking().min_validator_bond())
.await?
.unwrap_or_else(|| SDK::one_avail());
let payee = RewardDestination::Staked;
// Bond
let tx = sdk.tx.staking.bond(min_validator_bond, payee);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
// Generate Session Keys
let keys = rpc::author::rotate_keys(&sdk.rpc_client).await?;
let keys = utils::deconstruct_session_keys(keys)?;
// Set Keys
let tx = sdk.tx.session.set_keys(keys);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
// Validate
let commission = Commission::new(10)?;
let tx = sdk.tx.staking.validate(commission, false);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
Ok(())
}
HTTP RPC Connection
use avail_rust::{
avail,
block::{Block, DataSubmission},
error::ClientError,
transaction::HTTP,
Options, SDK,
};
type DataSubmissionCall = avail::data_availability::calls::types::SubmitData;
type ApplicationKeyCreatedEvent = avail::data_availability::events::ApplicationKeyCreated;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new_http(SDK::local_http_endpoint()).await?;
let online_client = &sdk.online_client;
let account = SDK::alice()?;
// Application Key Creation
let key = String::from("My Key Http").into_bytes();
let tx = sdk.tx.data_availability.create_application_key(key);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
let Some(event) = res.find_first_event::<ApplicationKeyCreatedEvent>() else {
return Err("Failed to get Application Key Created Event".into());
};
let app_id = event.id.0;
// Data Submission
let data = String::from("My Data").into_bytes();
let options = Options::new().app_id(app_id);
let tx = sdk.tx.data_availability.submit_data(data);
let res = tx
.execute_and_watch_inclusion(&account, Some(options))
.await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
println!(
"Block Hash: {:?}, Block Number: {}, Tx Hash: {:?}, Tx Index: {}",
res.block_hash, res.block_number, res.tx_hash, res.tx_index
);
let Some(call_data) = res.get_call_data::<DataSubmissionCall>(online_client).await else {
return Err("Failed to get Data Submission Call data".into());
};
println!("Call data: {:?}", call_data.data);
// Getting Data Submission from Block #1
let block = Block::new(online_client, res.block_hash).await?;
// data_submissions_by_signer, data_submissions_by_index, data_submissions_by_hash, data_submissions_by_app_id
let data_submissions = block.data_submissions_all();
for ds in data_submissions {
println!(
"Tx Hash: {:?}, Tx Index: {}, Data {:?}, Tx Signer: {:?}, App Id: {}",
ds.tx_hash, ds.tx_index, ds.data, ds.tx_signer, ds.app_id
);
println!("Ascii data: {}", ds.to_ascii().expect("qed"));
}
// Getting Data Submission from Block #2
for tx in block.transaction_all_static::<DataSubmissionCall>() {
println!("Call data: {:?}", tx.value.data);
let ds = DataSubmission::from_static(tx);
println!(
"Tx Hash: {:?}, Tx Index: {}, Data {:?}, Tx Signer: {:?}, App Id: {}",
ds.tx_hash, ds.tx_index, ds.data, ds.tx_signer, ds.app_id
);
println!("Ascii data: {}", ds.to_ascii().expect("qed"));
}
Ok(())
}
/*
Example Output:
Block Hash: 0x434f28e191b0b2bf4e9e379fd21d8a53d52933f7f2df5829f36ec221c583b005, Block Number: 502, Tx Hash: 0xc5dfa3c4b62280febc3cdb9638650441596dbb02c427ebb77d25201b6e52e2ec, Tx Index: 1
Call data: BoundedVec([77, 121, 32, 68, 97, 116, 97])
Tx Hash: 0xc5dfa3c4b62280febc3cdb9638650441596dbb02c427ebb77d25201b6e52e2ec, Tx Index: 1, Data [77, 121, 32, 68, 97, 116, 97], Tx Signer: [1, 152, 76, 87, 244, 106, 83, 18, 214, 247, 138, 6, 162, 56, 34, 56, 182, 50, 22, 174, 89, 219, 133, 176, 244, 24, 155, 213, 201, 63, 146, 181, 36, 247, 60, 134, 221, 14, 102, 58, 148, 247, 218, 33, 47, 13, 103, 227, 186, 13, 221, 104, 243, 209, 74, 163, 74, 212, 168, 101, 255, 150, 88, 251, 142], App Id: 13
Ascii data: My Data
Call data: BoundedVec([77, 121, 32, 68, 97, 116, 97])
Tx Hash: 0xc5dfa3c4b62280febc3cdb9638650441596dbb02c427ebb77d25201b6e52e2ec, Tx Index: 1, Data [77, 121, 32, 68, 97, 116, 97], Tx Signer: [1, 152, 76, 87, 244, 106, 83, 18, 214, 247, 138, 6, 162, 56, 34, 56, 182, 50, 22, 174, 89, 219, 133, 176, 244, 24, 155, 213, 201, 63, 146, 181, 36, 247, 60, 134, 221, 14, 102, 58, 148, 247, 218, 33, 47, 13, 103, 227, 186, 13, 221, 104, 243, 209, 74, 163, 74, 212, 168, 101, 255, 150, 88, 251, 142], App Id: 13
Ascii data: My Data
*/
Custom Connection
use avail_rust::prelude::*;
use std::time::Duration;
use subxt::backend::rpc::{
reconnecting_rpc_client::{ExponentialBackoff, RpcClient as ReconnectingRpcClient},
RpcClient,
};
type DataSubmissionCall = avail::data_availability::calls::types::SubmitData;
type ApplicationKeyCreatedEvent = avail::data_availability::events::ApplicationKeyCreated;
pub async fn run() -> Result<(), ClientError> {
let rpc_client = ReconnectingRpcClient::builder()
.retry_policy(
ExponentialBackoff::from_millis(1000)
.max_delay(Duration::from_secs(3))
.take(3),
)
.build(SDK::local_endpoint())
.await
.map_err(|e| e.to_string())?;
let rpc_client = RpcClient::new(rpc_client);
let online_client = AOnlineClient::from_rpc_client(rpc_client.clone()).await?;
let sdk = SDK::new_custom(online_client, rpc_client).await?;
let online_client = &sdk.online_client;
let account = SDK::alice()?;
// Application Key Creation
let key = String::from("My Key Custom").into_bytes();
let tx = sdk.tx.data_availability.create_application_key(key);
let res = tx.execute_and_watch_inclusion(&account, None).await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
let Some(event) = res.find_first_event::<ApplicationKeyCreatedEvent>() else {
return Err("Failed to get Application Key Created Event".into());
};
let app_id = event.id.0;
// Data Submission
let data = String::from("My Data").into_bytes();
let options = Options::new().app_id(app_id);
let tx = sdk.tx.data_availability.submit_data(data);
let res = tx
.execute_and_watch_inclusion(&account, Some(options))
.await?;
match res.is_successful(&sdk.online_client) {
Some(x) => x?,
None => panic!("Failed to decode events."),
};
println!(
"Block Hash: {:?}, Block Number: {}, Tx Hash: {:?}, Tx Index: {}",
res.block_hash, res.block_number, res.tx_hash, res.tx_index
);
let Some(call_data) = res.get_call_data::<DataSubmissionCall>(online_client).await else {
return Err("Failed to get Data Submission Call data".into());
};
println!("Call data: {:?}", call_data.data);
// Getting Data Submission from Block #1
let block = Block::new(online_client, res.block_hash).await?;
// data_submissions_by_signer, data_submissions_by_index, data_submissions_by_hash, data_submissions_by_app_id
let data_submissions = block.data_submissions_all();
for ds in data_submissions {
println!(
"Tx Hash: {:?}, Tx Index: {}, Data {:?}, Tx Signer: {:?}, App Id: {}",
ds.tx_hash, ds.tx_index, ds.data, ds.tx_signer, ds.app_id
);
println!("Ascii data: {}", ds.to_ascii().expect("qed"));
}
// Getting Data Submission from Block #2
for tx in block.transaction_all_static::<DataSubmissionCall>() {
println!("Call data: {:?}", tx.value.data);
let ds = DataSubmission::from_static(tx);
println!(
"Tx Hash: {:?}, Tx Index: {}, Data {:?}, Tx Signer: {:?}, App Id: {}",
ds.tx_hash, ds.tx_index, ds.data, ds.tx_signer, ds.app_id
);
println!("Ascii data: {}", ds.to_ascii().expect("qed"));
}
Ok(())
}
/*
Example Output:
Block Hash: 0xb8b08997ab5e45c834e28c0ccab4d73eb94b95814500049b11fcf72f5e999c70, Block Number: 475, Tx Hash: 0xd7a62ff853acac85c042f2f42b123b9fab73f0290105107dd081d2aa3785877d, Tx Index: 1
Call data: BoundedVec([77, 121, 32, 68, 97, 116, 97])
Tx Hash: 0xd7a62ff853acac85c042f2f42b123b9fab73f0290105107dd081d2aa3785877d, Tx Index: 1, Data [77, 121, 32, 68, 97, 116, 97], Tx Signer: [1, 48, 143, 125, 91, 21, 60, 171, 139, 249, 159, 63, 160, 208, 148, 213, 254, 200, 128, 124, 163, 191, 18, 226, 173, 62, 86, 20, 219, 248, 100, 60, 71, 220, 201, 196, 171, 104, 203, 65, 100, 96, 66, 56, 89, 109, 100, 63, 40, 151, 206, 46, 200, 73, 10, 63, 154, 226, 232, 161, 146, 143, 249, 94, 142], App Id: 11
Ascii data: My Data
Call data: BoundedVec([77, 121, 32, 68, 97, 116, 97])
Tx Hash: 0xd7a62ff853acac85c042f2f42b123b9fab73f0290105107dd081d2aa3785877d, Tx Index: 1, Data [77, 121, 32, 68, 97, 116, 97], Tx Signer: [1, 48, 143, 125, 91, 21, 60, 171, 139, 249, 159, 63, 160, 208, 148, 213, 254, 200, 128, 124, 163, 191, 18, 226, 173, 62, 86, 20, 219, 248, 100, 60, 71, 220, 201, 196, 171, 104, 203, 65, 100, 96, 66, 56, 89, 109, 100, 63, 40, 151, 206, 46, 200, 73, 10, 63, 154, 226, 232, 161, 146, 143, 249, 94, 142], App Id: 11
Ascii data: My Data
*/