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 must
  • Block
  • 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
*/