SDK Version
This Documentation is based upon avail-rust version v0.1.9
Account
Account Balance
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let alice_account = account::account_id_from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")?;
let info = account::account_info(&sdk.client, alice_account).await?;
println!("Flags: {:?}", info.data.flags);
println!("Free: {}", info.data.free);
println!("Frozen: {}", info.data.frozen);
println!("Reserved: {}", info.data.reserved);
Ok(())
}
Account Creation
use std::str::FromStr;
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
// Use SecretUri and Keypair to create your own account...
let secret_uri = SecretUri::from_str("//Alice")?;
let acc = Keypair::from_uri(&secret_uri)?;
println!("Alice Address: {}", acc.public_key().to_account_id());
// ... or from_secret_uri function
let acc = account::from_secret_uri("//Alice")?;
println!("Alice Address: {}", acc.public_key().to_account_id());
// There are predefined testing accounts available to be used on local dev networks.
let acc = account::alice();
println!("Alice Address: {}", acc.public_key().to_account_id());
let acc = account::bob();
println!("Bob Address: {}", acc.public_key().to_account_id());
let acc = account::charlie();
println!("Charlie Address: {}", acc.public_key().to_account_id());
let acc = account::dave();
println!("Dave Address: {}", acc.public_key().to_account_id());
let acc = account::eve();
println!("Eve Address: {}", acc.public_key().to_account_id());
let acc = account::ferdie();
println!("Ferdie Address: {}", acc.public_key().to_account_id());
// AccountId can be created form Keypair...
let account_id = acc.public_key().to_account_id();
println!("Ferdie Address: {}", account_id);
// ...or from SS58 address
let account_id = account::account_id_from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")?;
println!("Alice Address: {}", account_id);
// SS58 address can be created from Account ID
println!("Alice Address: {}", account_id);
// MultiAddress can be created from Public Key...
let _address = acc.public_key().to_address::<u32>();
// ...or from account id
let _address = MultiAddress::from(account_id);
Ok(())
}
Account Nonce
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let alice_address = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
// Fetch nonce via RPC
let nonce = account::nonce(&sdk.client, alice_address).await?;
println!("RPC Nonce: {}", nonce);
// Fetch none via Storage
let alice_account = account::account_id_from_str(alice_address)?;
let info = account::account_info(&sdk.client, alice_account).await?;
println!("Nonce: {}", info.nonce);
Ok(())
}
Data Submission
use avail_rust::prelude::*;
use std::time::SystemTime;
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 account = account::alice();
// Application Key Creation
let time = std::format!("{:?}", SystemTime::now());
let key = time.into_bytes();
let tx = sdk.tx.data_availability.create_application_key(key);
let res = tx.execute_and_watch_inclusion(&account, Options::default()).await?;
assert_eq!(res.is_successful(), Some(true), "Transactions must be successful");
let events = res.events.as_ref().unwrap();
let event = events.find_first::<ApplicationKeyCreatedEvent>().unwrap();
let Some(event) = event 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, options).await?;
assert_eq!(res.is_successful(), Some(true), "Transactions must be successful");
println!(
"Block Hash: {:?}, Block Number: {}, Tx Hash: {:?}, Tx Index: {}",
res.block_hash, res.block_number, res.tx_hash, res.tx_index
);
// Events
let events = res.events.as_ref().unwrap();
for event in events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Decoding
let decoded = res.decode_as::<DataSubmissionCall>().await?;
let Some(decoded) = decoded else {
return Err("Failed to get Data Submission Call data".into());
};
let data = to_ascii(decoded.data.0).unwrap();
println!("Call data: {:?}", data);
println!("Data Submission finished correctly");
Ok(())
}
Batch
use std::ops::Mul;
use avail_rust::prelude::*;
use avail::{
runtime_types::{da_runtime::RuntimeCall, pallet_balances::pallet::Call::transfer_keep_alive as TransferKeepAlive},
utility::events as UtilityEvents,
};
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = account::alice();
let value_1 = SDK::one_avail();
let value_2 = SDK::one_avail();
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.clone().into(),
value: value_1,
};
let call_2 = TransferKeepAlive {
dest: dest_charlie.clone().into(),
value: value_2,
};
let mut calls = Vec::new();
calls.push(RuntimeCall::Balances(call_1).into());
calls.push(RuntimeCall::Balances(call_2).into());
//
// Happy Path
//
// Batch Call
//
// 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.client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true), "Transactions must be successful");
let events = res.events.unwrap();
assert_eq!(events.has::<UtilityEvents::BatchCompleted>(), Some(true), "");
assert_eq!(events.count::<UtilityEvents::ItemCompleted>(), 2, "");
println!("-- Batch Call Done --");
// Batch All Call
//
// 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.client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true), "Transactions must be successful");
let events = res.events.unwrap();
assert_eq!(events.has::<UtilityEvents::BatchCompleted>(), Some(true), "");
assert_eq!(events.count::<UtilityEvents::ItemCompleted>(), 2, "");
println!("-- Batch All Call Done --");
// Force Batch Call
//
// 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.client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true), "Transactions must be successful");
let events = res.events.unwrap();
assert_eq!(events.has::<UtilityEvents::BatchCompleted>(), Some(true), "");
assert_eq!(events.count::<UtilityEvents::ItemCompleted>(), 2, "");
println!("-- Force Batch Call Done --");
//
// Things differ when we introduce a call that will fail
//
let call_3 = TransferKeepAlive {
dest: dest_charlie.into(),
value: SDK::one_avail().mul(1_000_000_000u128),
};
let call_4 = TransferKeepAlive {
dest: dest_bob.into(),
value: SDK::one_avail().mul(1u128),
};
// The 3. is poisoned with a too high transfer amount
calls.push(RuntimeCall::Balances(call_3).into());
// The 4. call is a normal one
calls.push(RuntimeCall::Balances(call_4).into());
// Batch Call
let payload = avail::tx().utility().batch(calls.clone());
let tx = Transaction::new(sdk.client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true), "Transactions must be successful");
let events = res.events.unwrap();
assert_eq!(events.has::<UtilityEvents::BatchInterrupted>(), Some(true), "");
assert_eq!(events.has::<UtilityEvents::BatchCompleted>(), Some(false), "");
assert_eq!(events.count::<UtilityEvents::ItemCompleted>(), 2, "");
println!("-- Batch Call Done --");
// Batch All Call
let payload = avail::tx().utility().batch_all(calls.clone());
let tx = Transaction::new(sdk.client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(false), "Transactions needs fail.");
println!("-- Batch All Call Done --");
// Force Batch Call
let payload = avail::tx().utility().force_batch(calls.clone());
let tx = Transaction::new(sdk.client.clone(), payload);
let res = tx.execute_and_watch_inclusion(&account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true), "Transactions must be successful");
let events = res.events.unwrap();
assert_eq!(events.has::<UtilityEvents::BatchCompletedWithErrors>(), Some(true), "");
assert_eq!(events.count::<UtilityEvents::ItemFailed>(), 1, "");
assert_eq!(events.count::<UtilityEvents::ItemCompleted>(), 3, "");
println!("-- Force Batch Call Done --");
println!("Batch finished correctly");
Ok(())
}
Block
All Transactions
use avail_rust::prelude::*;
type SubmitDataCall = avail::data_availability::calls::types::SubmitData;
type DataSubmittedEvent = avail::data_availability::events::DataSubmitted;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Transactions
let block_transactions = block.transactions(Filter::default());
assert_eq!(block_transactions.len(), 9, "Transaction count must be 9");
// Printout Block Transactions
for tx in block_transactions.iter() {
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
);
}
// Convert from Block Transaction to Specific Transaction
let da_tx = block_transactions.index(2).decode::<SubmitDataCall>();
assert!(da_tx.is_some(), "SubmitDataCall should exist");
let da_tx = da_tx.unwrap();
let data = to_ascii(da_tx.data.0).unwrap();
println!("Data: {}", data);
// Printout all Transaction Events
let tx_events = block_transactions.index(2).events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 7, "Event count must be 7");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<DataSubmittedEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "DataSubmittedEvent");
let event = event.unwrap().unwrap();
println!("Who: {}, Data Hash: {:?}", event.who, event.data_hash);
Ok(())
}
All Transactions Static
use avail_rust::prelude::*;
type SubmitDataCall = avail::data_availability::calls::types::SubmitData;
type DataSubmittedEvent = avail::data_availability::events::DataSubmitted;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Transactions
let block_transactions = block.transactions_static::<SubmitDataCall>(Filter::default());
assert_eq!(block_transactions.len(), 4, "Transaction count must be 4");
// Printout Block Transactions
for tx in block_transactions.iter() {
let data = to_ascii(tx.value.data.0.clone()).unwrap();
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}, Data: {}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
data
);
}
// Printout all Transaction Events
let tx_events = block_transactions[0].events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 7, "Event count must be 7");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<DataSubmittedEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "DataSubmittedEvent");
let event = event.unwrap().unwrap();
println!("Who: {}, Data Hash: {:?}", event.who, event.data_hash);
Ok(())
}
Transaction Filtered By App Id
use avail_rust::prelude::*;
type SubmitDataCall = avail::data_availability::calls::types::SubmitData;
type DataSubmittedEvent = avail::data_availability::events::DataSubmitted;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Transaction filtered by Signer
let app_id = 2;
let block_transactions = block.transactions(Filter::new().app_id(app_id));
assert_eq!(block_transactions.len(), 2, "Transaction count must be 2");
// Printout Block Transactions made by Signer
for tx in block_transactions.iter() {
assert_eq!(tx.app_id(), Some(app_id), "App Id must be the same");
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
);
}
// Convert from Block Transaction to Specific Transaction
let da_tx = block_transactions.index(0).decode::<SubmitDataCall>();
assert!(da_tx.is_some(), "SubmitDataCall should exist");
let da_tx = da_tx.unwrap();
let data = to_ascii(da_tx.data.0).unwrap();
println!("Data: {}", data);
// Printout all Transaction Events
let tx_events = block_transactions.index(0).events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 7, "Event count must be 7");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<DataSubmittedEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "DataSubmittedEvent");
let event = event.unwrap().unwrap();
println!("Who: {}, Data Hash: {:?}", event.who, event.data_hash);
Ok(())
}
Transaction Filtered By App Id Static
use avail_rust::prelude::*;
type SubmitDataCall = avail::data_availability::calls::types::SubmitData;
type DataSubmittedEvent = avail::data_availability::events::DataSubmitted;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Transaction filtered by Signer
let app_id = 2;
let block_transactions = block.transactions_static::<SubmitDataCall>(Filter::new().app_id(app_id));
assert_eq!(block_transactions.len(), 2, "Transaction count must be 2");
// Printout Block Transactions made by Signer
for tx in block_transactions.iter() {
assert_eq!(tx.app_id(), Some(app_id), "App Id must be the same");
let data = to_ascii(tx.value.data.0.clone()).unwrap();
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}, Data: {}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
data
);
}
// Printout all Transaction Events
let tx_events = block_transactions[0].events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 7, "Event count must be 7");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<DataSubmittedEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "DataSubmittedEvent");
let event = event.unwrap().unwrap();
println!("Who: {}, Data Hash: {:?}", event.who, event.data_hash);
Ok(())
}
Transaction Filtered By Transaction Hash
use avail_rust::prelude::*;
type TransferKeepAliveCall = avail::balances::calls::types::TransferKeepAlive;
type TransferEvent = avail::balances::events::Transfer;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// Transaction filtered by Transaction Hash
let tx_hash = new_h256_from_hex("0x19c486e107c926ff4af3fa9b1d95aaba130cb0bc89515d0f5b523ef6bac06338")?;
let txs = block.transactions(Filter::new().tx_hash(tx_hash));
assert_eq!(txs.len(), 1, "");
let tx = txs.index(0);
// Printout
assert_eq!(tx.tx_hash(), tx_hash, "Tx Hash must be the same");
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
);
// Convert from Block Transaction to Specific Transaction
let ba_tx = tx.decode::<TransferKeepAliveCall>();
assert!(ba_tx.is_some(), "TransferKeepAliveCall should exist");
let ba_tx = ba_tx.unwrap();
let account_id = match ba_tx.dest {
subxt::utils::MultiAddress::Id(x) => x,
_ => panic!("Not decodable."),
};
println!("Destination: {}, Value: {}", account_id, ba_tx.value);
// Printout all Transaction Events
let tx_events = tx.events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 7, "Event count must be 7");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<TransferEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "TransferEvent");
let event = event.unwrap().unwrap();
println!("From: {}, To: {}, Amount: {}", event.from, event.to, event.amount);
Ok(())
}
Transaction Filtered By Transaction Hash Static
use avail_rust::prelude::*;
type TransferKeepAliveCall = avail::balances::calls::types::TransferKeepAlive;
type TransferEvent = avail::balances::events::Transfer;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// Transaction filtered by Transaction Hash
let tx_hash = new_h256_from_hex("0x19c486e107c926ff4af3fa9b1d95aaba130cb0bc89515d0f5b523ef6bac06338")?;
let txs = block.transactions_static::<TransferKeepAliveCall>(Filter::new().tx_hash(tx_hash));
assert_eq!(txs.len(), 1, "");
let tx = &txs[0];
// Printout
assert_eq!(tx.tx_hash(), tx_hash, "Tx Hash must be the same");
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
);
let account_id = match &tx.value.dest {
subxt::utils::MultiAddress::Id(x) => x.clone(),
_ => panic!("Not decodable."),
};
println!("Destination: {}, Value: {}", account_id, tx.value.value);
// Printout all Transaction Events
let tx_events = tx.events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 7, "Event count must be 7");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<TransferEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "TransferEvent");
let event = event.unwrap().unwrap();
println!("From: {}, To: {}, Amount: {}", event.from, event.to, event.amount);
Ok(())
}
Transaction Filtered By Index
use avail_rust::prelude::*;
type TransferKeepAliveCall = avail::balances::calls::types::TransferKeepAlive;
type NetAccountEvent = avail::system::events::NewAccount;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// Transaction filtered by Transaction index
let tx_index = 1;
let txs = block.transactions(Filter::new().tx_index(tx_index));
assert_eq!(txs.len(), 1, "");
let tx = txs.index(0);
// Printout
assert_eq!(tx.tx_index(), tx_index, "Tx Index must be the same");
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
);
// Convert from Block Transaction to Specific Transaction
let ba_tx = tx.decode::<TransferKeepAliveCall>();
assert!(ba_tx.is_some(), "TransferKeepAliveCall should exist");
let ba_tx = ba_tx.unwrap();
let account_id = match ba_tx.dest {
subxt::utils::MultiAddress::Id(x) => x,
_ => panic!("Not decodable."),
};
println!("Destination: {}, Value: {}", account_id, ba_tx.value);
// Printout all Transaction Events
let tx_events = tx.events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 9, "Event count must be 9");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<NetAccountEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "NetAccountEvent");
let event = event.unwrap().unwrap();
println!("Account: {}", event.account);
Ok(())
}
Transaction Filtered By Index Static
use avail_rust::prelude::*;
type TransferKeepAliveCall = avail::balances::calls::types::TransferKeepAlive;
type NetAccountEvent = avail::system::events::NewAccount;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// Transaction filtered by Transaction index
let tx_index = 1;
let txs = block.transactions_static::<TransferKeepAliveCall>(Filter::new().tx_index(tx_index));
assert_eq!(txs.len(), 1, "");
let tx = &txs[0];
// Printout
assert_eq!(tx.tx_index(), tx_index, "Tx Index must be the same");
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
);
let account_id = match &tx.value.dest {
subxt::utils::MultiAddress::Id(x) => x.clone(),
_ => panic!("Not decodable."),
};
println!("Destination: {}, Value: {}", account_id, tx.value.value);
// Printout all Transaction Events
let tx_events = tx.events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 9, "Event count must be 9");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<NetAccountEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "NetAccountEvent");
let event = event.unwrap().unwrap();
println!("Account: {}", event.account);
Ok(())
}
Transaction Filtered By Signer
use avail_rust::prelude::*;
type CreateAppKeyCall = avail::data_availability::calls::types::CreateApplicationKey;
type AppKeyCreated = avail::data_availability::events::ApplicationKeyCreated;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Transaction filtered by Signer
let account_id = account_id_from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")?;
let block_transactions = block.transactions(Filter::new().tx_signer(account_id.clone()));
assert_eq!(block_transactions.len(), 5, "Transaction count must be 5");
// Printout Block Transactions made by Signer
for tx in block_transactions.iter() {
assert_eq!(tx.account_id(), Some(account_id.clone()), "Signer must be the same");
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
);
}
// Convert from Block Transaction to Specific Transaction
let da_tx = block_transactions.index(0).decode::<CreateAppKeyCall>();
assert!(da_tx.is_some(), "CreateAppKeyCall should exist");
let da_tx = da_tx.unwrap();
let key = to_ascii(da_tx.key.0).unwrap();
println!("Key: {}", key);
// Printout all Transaction Events
let tx_events = block_transactions.index(0).events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 7, "Event count must be 7");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<AppKeyCreated>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "AppKeyCreated");
let event = event.unwrap().unwrap();
let key = to_ascii(event.key.0).unwrap();
println!("App Id: {}, Owner: {}, Key: {}", event.id.0, event.owner, key);
Ok(())
}
Transaction Filtered By Signer Static
use avail_rust::prelude::*;
type CreateAppKeyCall = avail::data_availability::calls::types::CreateApplicationKey;
type AppKeyCreated = avail::data_availability::events::ApplicationKeyCreated;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Transaction filtered by Signer
let account_id = account_id_from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY")?;
let block_transactions = block.transactions_static::<CreateAppKeyCall>(Filter::new().tx_signer(account_id.clone()));
assert_eq!(block_transactions.len(), 1, "Transaction count must be 1");
// Printout Block Transactions made by Signer
for tx in block_transactions.iter() {
assert_eq!(tx.account_id(), Some(account_id.clone()), "Signer must be the same");
let key = to_ascii(tx.value.key.0.clone()).unwrap();
println!("Key: {}", key);
println!(
"Pallet Name: {:?}, Pallet Index: {}, Call Name: {:?}, Call Index: {:?}, Tx Hash: {:?}, Tx Index: {}",
tx.pallet_name(),
tx.pallet_index(),
tx.call_name(),
tx.call_index(),
tx.tx_hash(),
tx.tx_index()
);
println!(
"Tx Signer: {:?}, App Id: {:?}, Tip: {:?}, Mortality: {:?}, Nonce: {:?}",
tx.ss58address(),
tx.app_id(),
tx.tip(),
tx.mortality(),
tx.nonce(),
);
}
// Printout all Transaction Events
let tx_events = block_transactions[0].events().await;
assert!(tx_events.is_some(), "Events should exist");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 7, "Event count must be 7");
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Generic Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<AppKeyCreated>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "AppKeyCreated");
let event = event.unwrap().unwrap();
let key = to_ascii(event.key.0).unwrap();
println!("App Id: {}, Owner: {}, Key: {}", event.id.0, event.owner, key);
Ok(())
}
All Data Submissions
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Block Blobs
let blobs = block.data_submissions(Filter::default());
assert_eq!(blobs.len(), 4, "Blobs must present 4 times");
// Printout All Block Blobs
for blob in blobs {
let blob_data = blob.to_ascii().unwrap();
println!(
"Tx Hash: {:?}, Tx Index: {}, Data: {:?}, App Id: {}, Tx Singer: {:?}",
blob.tx_hash,
blob.tx_index,
blob_data,
blob.app_id,
blob.ss58address(),
);
}
Ok(())
}
Data Submissions Filtered by App Id
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Block Blobs by App Id
let app_id = 2u32;
let blobs = block.data_submissions(Filter::new().app_id(app_id));
assert_eq!(blobs.len(), 2, "Blobs must present 2 times");
// Printout All Block Blobs by App Id
for blob in blobs {
let blob_data = blob.to_ascii().unwrap();
assert_eq!(blob.app_id, app_id, "App Id must be 2");
println!(
"Tx Hash: {:?}, Tx Index: {}, Data: {:?}, App Id: {}, Tx Singer: {:?}",
blob.tx_hash,
blob.tx_index,
blob_data,
blob.app_id,
blob.ss58address(),
);
}
Ok(())
}
Data Submissions Filtered by Hash
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Block Blobs by Hash
let tx_hash = new_h256_from_hex("0xe7efa71363d11bce370fe71a33e5ff296775f37507075c49316132131420f793")?;
let blobs = block.data_submissions(Filter::new().tx_hash(tx_hash));
assert_eq!(blobs.len(), 1, "");
let blob = &blobs[0];
// Printout All Block Blobs by Hash
let blob_data = blob.to_ascii().unwrap();
assert_eq!(blob.tx_hash, tx_hash, "Tx Hash must be the same");
println!(
"Tx Hash: {:?}, Tx Index: {}, Data: {:?}, App Id: {}, Tx Singer: {:?}",
blob.tx_hash,
blob.tx_index,
blob_data,
blob.app_id,
blob.ss58address(),
);
Ok(())
}
Data Submissions Filtered by Index
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Block Blobs by Index
let tx_index = 6;
let blobs = block.data_submissions(Filter::new().tx_index(tx_index));
assert_eq!(blobs.len(), 1, "");
let blob = &blobs[0];
// Printout All Block Blobs by Index
let blob_data = blob.to_ascii().unwrap();
assert_eq!(blob.tx_index, tx_index, "Tx Index must be the same");
println!(
"Tx Hash: {:?}, Tx Index: {}, Data: {:?}, App Id: {}, Tx Singer: {:?}",
blob.tx_hash,
blob.tx_index,
blob_data,
blob.app_id,
blob.ss58address(),
);
Ok(())
}
Data Submissions Filtered by Signer
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Block Blobs by Signer
let account_id = account_id_from_str("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty").unwrap();
let blobs = block.data_submissions(Filter::new().tx_signer(account_id.clone()));
assert_eq!(blobs.len(), 1, "Blobs must present 1 time");
// Printout All Block Blobs by Signer
for blob in blobs {
let blob_data = blob.to_ascii().unwrap();
assert_eq!(blob.account_id(), Some(account_id.clone()), "Signer must be the same.");
println!(
"Tx Hash: {:?}, Tx Index: {}, Data: {:?}, App Id: {}, Tx Singer: {:?}",
blob.tx_hash,
blob.tx_index,
blob_data,
blob.app_id,
blob.ss58address(),
);
}
Ok(())
}
Block Events
use avail_rust::prelude::*;
type TransferEvent = avail::balances::events::Transfer;
type AppKeyCreatedEvent = avail::data_availability::events::ApplicationKeyCreated;
type DataSubmittedEvent = avail::data_availability::events::DataSubmitted;
type SuccessEvent = avail::system::events::ExtrinsicSuccess;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Block Events
let block_events = block.events().await;
assert!(block_events.is_some(), "Events must been present");
let block_events = block_events.unwrap();
assert_eq!(block_events.len(), 53, "Block event count must be 53");
// Printout All Block Events
for event in block_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Block Transaction Event to Specific Transaction Event
let events = block_events.find::<TransferEvent>();
assert_eq!(events.len(), 2, "Transfer event count must be 2");
for event in events {
println!("From: {}, To: {}, Amount: {}", event.from, event.to, event.amount)
}
// Convert from Block Transaction Event to Specific ApplicationKeyCreated Event
let event = block_events.find_first::<AppKeyCreatedEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "AppKeyCreatedEvent");
let event = event.unwrap().unwrap();
let key: String = to_ascii(event.key.0).unwrap();
println!("Owner: {}, App Id: {}, Key: {}", event.owner, event.id.0, key);
// Check
assert_eq!(
block_events.find::<DataSubmittedEvent>().len(),
4,
"DataSubmitted event count must be 4"
);
assert_eq!(
block_events.find::<AppKeyCreatedEvent>().len(),
1,
"AppKeyCreated event count must be 1"
);
// Events for Specific Transaction
let tx_index = 0u32;
let tx_events = block.tx_events(tx_index).await;
assert!(tx_events.is_some(), "Transaction Events must be present");
let tx_events = tx_events.unwrap();
assert_eq!(tx_events.len(), 1, "Transaction event count must be 1");
// Printout All Tx Events
for event in tx_events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Convert from Block Transaction Event to Specific Transaction Event
let event = tx_events.find_first::<SuccessEvent>();
assert!(event.as_ref().is_some_and(|x| x.is_some()), "SuccessEvent");
let event = event.unwrap().unwrap();
println!("Weight {:?}", event.dispatch_info.weight);
Ok(())
}
Transaction
Execute
use avail::data_availability::{calls::types::SubmitData, events::DataSubmitted};
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Transaction will be signed, and sent.
//
// There is no guarantee that the transaction was executed at all. It might have been
// dropped or discarded for various reasons. The caller is responsible for querying future
// blocks in order to determine the execution status of that transaction.
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let tx_hash = tx.execute(&account::alice(), Options::new().app_id(1)).await?;
println!("Tx Hash: {:?}", tx_hash);
// Checking if the transaction was included
//
// It's not necessary to use the builtin watcher. A custom watcher
// might yield better results in some cases.
let mut watcher = Watcher::new(sdk.client.clone(), tx_hash);
watcher.set_options(|opt: &mut WatcherOptions| opt.wait_for = WaitFor::BlockInclusion);
let res = watcher.run().await?;
let res = res.unwrap();
assert_eq!(res.is_successful(), Some(true));
// Printout Transaction Details
println!(
"Block Hash: {:?}, Block Number: {}, Tx Hash: {:?}, Tx Index: {}",
res.block_hash, res.block_number, res.tx_hash, res.tx_index
);
// Printout Transaction Events
let events = res.events.as_ref().unwrap();
for event in events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Converts generic event to a specific one
let event = events.find_first::<DataSubmitted>();
let event = event.unwrap().unwrap();
print!("Data Hash: {:?}, Who: {}", event.data_hash, event.who);
// Converts generic transaction to a specific one
let decoded = res.decode_as::<SubmitData>().await?.unwrap();
let data = to_ascii(decoded.data.0).unwrap();
println!("Data: {}", data);
println!("Transaction Execute finished correctly");
Ok(())
}
Execute and Watch For Inclusion
use avail::data_availability::{calls::types::SubmitData, events::DataSubmitted};
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Transaction will be signed, sent, and watched
// If the transaction was dropped or never executed, the system will retry it
// for 2 more times using the same nonce and app id.
//
// Waits for transaction inclusion. Most of the time you would want to call `ExecuteAndWatchFinalization` as
// inclusion doesn't mean that the transaction will be in the canonical chain.
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let res = tx
.execute_and_watch_inclusion(&account::alice(), Options::new().app_id(1))
.await?;
assert_eq!(res.is_successful(), Some(true));
// Printout Transaction Details
println!(
"Block Hash: {:?}, Block Number: {}, Tx Hash: {:?}, Tx Index: {}",
res.block_hash, res.block_number, res.tx_hash, res.tx_index
);
// Printout Transaction Events
let events = res.events.as_ref().unwrap();
for event in events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Converts generic event to a specific one
let event = events.find_first::<DataSubmitted>();
let event = event.unwrap().unwrap();
print!("Data Hash: {:?}, Who: {}", event.data_hash, event.who);
// Converts generic transaction to a specific one
let decoded = res.decode_as::<SubmitData>().await?.unwrap();
let data = to_ascii(decoded.data.0).unwrap();
println!("Data: {}", data);
println!("Transaction Execute Inclusion finished correctly");
Ok(())
}
Execute and Watch For Finalization
use avail::data_availability::{calls::types::SubmitData, events::DataSubmitted};
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Transaction will be signed, sent, and watched
// If the transaction was dropped or never executed, the system will retry it
// for 2 more times using the same nonce and app id.
//
// Waits for finalization to finalize the transaction.
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let res = tx
.execute_and_watch_finalization(&account::alice(), Options::new().app_id(1))
.await?;
assert_eq!(res.is_successful(), Some(true));
// Printout Transaction Details
println!(
"Block Hash: {:?}, Block Number: {}, Tx Hash: {:?}, Tx Index: {}",
res.block_hash, res.block_number, res.tx_hash, res.tx_index
);
// Printout Transaction Events
let events = res.events.as_ref().unwrap();
for event in events.iter() {
let tx_index = match event.phase() {
subxt::events::Phase::ApplyExtrinsic(x) => Some(x),
_ => None,
};
println!(
"Pallet Name: {}, Pallet Index: {}, Event Name: {}, Event Index: {}, Event Position: {}, Tx Index: {:?}",
event.pallet_name(),
event.pallet_index(),
event.variant_name(),
event.variant_index(),
event.index(),
tx_index,
);
}
// Converts generic event to a specific one
let event = events.find_first::<DataSubmitted>();
let event = event.unwrap().unwrap();
print!("Data Hash: {:?}, Who: {}", event.data_hash, event.who);
// Converts generic transaction to a specific one
let decoded = res.decode_as::<SubmitData>().await?.unwrap();
let data = to_ascii(decoded.data.0).unwrap();
println!("Data: {}", data);
println!("Transaction Execute Finalization finished correctly");
Ok(())
}
Options
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
run_app_id().await?;
run_nonce().await?;
run_tip().await?;
run_mortality().await?;
println!("Transaction Options finished correctly");
Ok(())
}
async fn run_app_id() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Executing Transaction
let app_id = 5u32;
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let options = Options::new().app_id(app_id);
let res = tx.execute_and_watch_inclusion(&account::alice(), options).await?;
assert_eq!(res.is_successful(), Some(true));
// Check if the correct app id has been used
let block = Block::new(&sdk.client, res.block_hash).await?;
let block_txs = block.transactions(Filter::new().tx_hash(res.tx_hash));
assert_eq!(block_txs.len(), 1);
assert_eq!(block_txs.index(0).app_id(), Some(app_id));
println!("Transaction Options App Id finished correctly");
Ok(())
}
async fn run_nonce() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = account::alice();
// Executing Transaction
let nonce = account::nonce(&sdk.client, &std::format!("{}", account.public_key().to_account_id())).await?;
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let options = Options::new().nonce(nonce);
let res = tx.execute_and_watch_inclusion(&account, options).await?;
assert_eq!(res.is_successful(), Some(true));
// Check if the correct app id has been used
let block = Block::new(&sdk.client, res.block_hash).await?;
let block_txs = block.transactions(Filter::new().tx_hash(res.tx_hash));
assert_eq!(block_txs.len(), 1);
assert_eq!(block_txs.index(0).nonce(), Some(nonce));
println!("Transaction Options Nonce finished correctly");
Ok(())
}
async fn run_tip() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Executing Transaction
let tip = SDK::one_avail();
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let options = Options::new().tip(tip);
let res = tx.execute_and_watch_inclusion(&account::alice(), options).await?;
assert_eq!(res.is_successful(), Some(true));
// Check if the correct app id has been used
let block = Block::new(&sdk.client, res.block_hash).await?;
let block_txs = block.transactions(Filter::new().tx_hash(res.tx_hash));
assert_eq!(block_txs.len(), 1);
assert_eq!(block_txs.index(0).tip(), Some(tip));
println!("Transaction Options Tip finished correctly");
Ok(())
}
async fn run_mortality() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Executing Transaction
let mortality = 8u64;
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let options = Options::new().mortality(mortality);
let res = tx.execute_and_watch_inclusion(&account::alice(), options).await?;
assert_eq!(res.is_successful(), Some(true));
// Check if the correct app id has been used
let block = Block::new(&sdk.client, res.block_hash).await?;
let block_txs = block.transactions(Filter::new().tx_hash(res.tx_hash));
assert_eq!(block_txs.len(), 1);
let actual_mortality = block_txs.index(0).mortality().unwrap();
let actual_mortality = match actual_mortality {
subxt::utils::Era::Mortal { period, phase: _ } => period,
_ => panic!("Should not be here"),
};
assert_eq!(actual_mortality, mortality);
println!("Transaction Options Mortality finished correctly");
Ok(())
}
Payment
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let account = account::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 inclusion_fee = fee_details.inclusion_fee.unwrap();
println!(
"Adjusted Weight Fee: {}, Len Fee: {}, Base Fee: {}",
inclusion_fee.adjusted_weight_fee, inclusion_fee.len_fee, inclusion_fee.base_fee
);
let fee_details = tx.payment_query_call_fee_details().await?;
let inclusion_fee = fee_details.inclusion_fee.unwrap();
println!(
"Adjusted Weight Fee: {}, Len Fee: {}, Base Fee: {}",
inclusion_fee.adjusted_weight_fee, inclusion_fee.len_fee, inclusion_fee.base_fee
);
let info = tx.payment_query_info(&account, Some(options)).await?;
println!(
"ProofSize: {}, RefTime: {}, Class: {:?}, Partial Fee: {}",
info.weight.proof_size, info.weight.ref_time, info.class, info.partial_fee
);
let info = tx.payment_query_call_info().await?;
println!(
"ProofSize: {}, RefTime: {}, Class: {:?}, Partial Fee: {}",
info.weight.proof_size, info.weight.ref_time, info.class, info.partial_fee
);
println!("Transaction Options finished correctly");
Ok(())
}
Proxy
use avail::{
proxy::events::{ProxyAdded, ProxyExecuted, ProxyRemoved, PureCreated},
runtime_types::{
da_control::pallet::Call::create_application_key, pallet_balances::pallet::Call::transfer_keep_alive,
},
};
use avail_rust::{
avail::runtime_types::da_runtime::{impls::ProxyType, RuntimeCall},
prelude::*,
};
use std::time::SystemTime;
pub async fn run() -> Result<(), ClientError> {
run_normal_proxy().await?;
run_pure_proxy().await?;
run_proxy_failure().await?;
println!("Proxy finished correctly");
Ok(())
}
pub async fn run_normal_proxy() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let proxy_account = account::bob();
let prox_account_multi = MultiAddress::from(proxy_account.public_key().to_account_id());
let main_account = account::ferdie();
let main_account_multi = MultiAddress::from(main_account.public_key().to_account_id());
// Creating Proxy
let proxy_type = ProxyType::Any;
let tx = sdk
.tx
.proxy
.add_proxy(prox_account_multi.clone(), proxy_type.clone(), 0);
let res = tx.execute_and_watch_inclusion(&main_account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Finding Proxy Added Event
let tx_events = res.events.as_ref().unwrap();
let event = tx_events.find_first::<ProxyAdded>();
let event = event.unwrap().unwrap();
println!(
"Delegatee: {}, Delegator: {}, ProxyTpe: {:?}, Delay: {}",
event.delegatee, event.delegator, event.proxy_type, event.delay
);
// Executing the Proxy.Proxy() call
let call = transfer_keep_alive {
dest: prox_account_multi.clone(),
value: SDK::one_avail(),
};
let call = RuntimeCall::Balances(call);
let tx = sdk.tx.proxy.proxy(main_account_multi, None, call);
let res = tx.execute_and_watch_inclusion(&proxy_account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Finding ProxyExecuted event
let tx_events = res.events.as_ref().unwrap();
let event = tx_events.find_first::<ProxyExecuted>();
let event = event.unwrap().unwrap();
assert!(event.result.is_ok());
// Removing Proxy
let tx = sdk.tx.proxy.remove_proxy(prox_account_multi, proxy_type, 0);
let res = tx.execute_and_watch_inclusion(&main_account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Finding for EventProxyRemoved event.
let tx_events = res.events.as_ref().unwrap();
let event = tx_events.find_first::<ProxyRemoved>();
let event = event.unwrap().unwrap();
println!(
"Delegatee: {}, Delegator: {}, ProxyTpe: {:?}, Delay: {}",
event.delegatee, event.delegator, event.proxy_type, event.delay
);
Ok(())
}
pub async fn run_pure_proxy() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let main_account = account::ferdie();
// Creating Proxy
let proxy_type = ProxyType::Any;
let tx = sdk.tx.proxy.create_pure(proxy_type.clone(), 0, 0);
let res = tx.execute_and_watch_inclusion(&main_account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Finding PureCreated Event
let tx_events = res.events.as_ref().unwrap();
let event = tx_events.find_first::<PureCreated>();
let event = event.unwrap().unwrap();
println!(
"Pure: {}, Who: {}, ProxyTpe: {:?}, Disambiguation Index: {}",
event.pure, event.who, event.proxy_type, event.disambiguation_index
);
let pure_proxy = event.pure;
let pure_proxy_multi = MultiAddress::from(pure_proxy);
// Executing the Proxy.Proxy() call
let time = std::format!("{:?}", SystemTime::now());
let key = time.into_bytes();
let call = create_application_key { key: BoundedVec(key) };
let call = RuntimeCall::DataAvailability(call);
let tx = sdk.tx.proxy.proxy(pure_proxy_multi, None, call);
let res = tx.execute_and_watch_inclusion(&main_account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Finding ProxyExecuted event
let tx_events = res.events.as_ref().unwrap();
let event = tx_events.find_first::<ProxyExecuted>();
let event = event.unwrap().unwrap();
assert!(event.result.is_ok());
Ok(())
}
pub async fn run_proxy_failure() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let proxy_account = account::bob();
let prox_account_multi = MultiAddress::from(proxy_account.public_key().to_account_id());
let main_account = account::ferdie();
let main_account_multi = MultiAddress::from(main_account.public_key().to_account_id());
// Creating Proxy
let proxy_type = ProxyType::NonTransfer;
let tx = sdk
.tx
.proxy
.add_proxy(prox_account_multi.clone(), proxy_type.clone(), 0);
let res = tx.execute_and_watch_inclusion(&main_account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Executing the Proxy.Proxy() call
let call = transfer_keep_alive {
dest: prox_account_multi.clone(),
value: SDK::one_avail(),
};
let call = RuntimeCall::Balances(call);
let tx = sdk.tx.proxy.proxy(main_account_multi, None, call);
let res = tx.execute_and_watch_inclusion(&proxy_account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Finding ProxyExecuted event
let tx_events = res.events.as_ref().unwrap();
let event = tx_events.find_first::<ProxyExecuted>();
let event = event.unwrap().unwrap();
assert!(event.result.is_err());
println!("Proxy error: {:?}", event.result.unwrap_err());
// Removing Proxy
let tx = sdk.tx.proxy.remove_proxy(prox_account_multi, proxy_type, 0);
let res = tx.execute_and_watch_inclusion(&main_account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
Ok(())
}
Custom Client
use avail_rust::prelude::*;
use std::time::Duration;
use subxt::backend::rpc::{
reconnecting_rpc_client::{ExponentialBackoff, RpcClient as ReconnectingRpcClient},
RpcClient,
};
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 client = Client::new(online_client, rpc_client);
let _sdk = SDK::new_custom(client).await?;
Ok(())
}
HTTP Client
use avail_rust::prelude::*;
use std::time::SystemTime;
type ApplicationKeyCreatedEvent = avail::data_availability::events::ApplicationKeyCreated;
pub async fn run() -> Result<(), ClientError> {
run_transaction().await?;
run_block().await?;
println!("HTTP Client finished correctly");
Ok(())
}
pub async fn run_transaction() -> Result<(), ClientError> {
let sdk = SDK::new_http(SDK::local_http_endpoint()).await?;
let account = account::alice();
// Application Key Creation
let time = std::format!("{:?}", SystemTime::now());
let key = time.into_bytes();
let tx = sdk.tx.data_availability.create_application_key(key);
let res = tx.execute_and_watch_inclusion(&account, Options::default()).await?;
assert_eq!(res.is_successful(), Some(true));
let events = res.events.unwrap();
let event = events.find_first::<ApplicationKeyCreatedEvent>().unwrap();
let Some(event) = event 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, options).await?;
assert_eq!(res.is_successful(), Some(true));
Ok(())
}
pub async fn run_block() -> Result<(), ClientError> {
let sdk = SDK::new_http(SDK::turing_http_endpoint()).await?;
let block_hash = new_h256_from_hex("0x94746ba186876d7407ee618d10cb6619befc59eeb173cacb00c14d1ff492fc58")?;
let block = Block::new(&sdk.client, block_hash).await?;
// All Transactions
let block_transactions = block.transactions(Filter::default());
assert_eq!(block_transactions.len(), 9, "Transaction count must be 9");
// Printout Block Transactions
for tx in block_transactions.iter().take(2) {
println!("Tx Index: {}", tx.tx_index());
}
let blobs = block.data_submissions(Filter::default());
assert_eq!(blobs.len(), 4, "Blobs must present 4 times");
// Printout All Block Blobs
for blob in blobs.iter().take(2) {
println!("Tx Index: {}", blob.tx_index,);
}
Ok(())
}
RPC
use avail_rust::{prelude::*, primitives::kate};
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// author_rotate_keys
let value = rpc::author::rotate_keys(&sdk.client).await?;
dbg!(value);
/* Output
SessionKeys {
babe: Public(...),
grandpa: Public(...),
im_online: Public(...),
authority_discovery: Public(...),
}
*/
// author_submit_extrinsic
let account = account::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.client, &account_id).await?.build().await?;
let signed_call = sdk
.client
.online_client
.tx()
.create_signed(&call, &account, params)
.await?;
let extrinsic = signed_call.encoded();
let value = rpc::author::submit_extrinsic(&sdk.client, extrinsic).await?;
dbg!(value);
/* Output
"0x56edc7516bb403f0d812f0f91dea5e36b46bbb31f7b69e78469652f74882377d"
*/
// chain_get_block
let value = rpc::chain::get_block(&sdk.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.client, None).await?;
dbg!(value);
/* Output
0xc4e0a9a2ef80ddc1d70c9946d8a6f86ca4b15053b39ba56709222f01ddc64561
*/
// chain_get_finalized_head
let value = rpc::chain::get_finalized_head(&sdk.client).await?;
dbg!(value);
/* Output
0x2c896c9faae4e111f1fbeb955be5e999a328846969b59a7a7c64eadc4701122a
*/
// chain_get_header
let value = rpc::chain::get_header(&sdk.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.client, account).await?;
dbg!(value);
/* Output
2
*/
// system_chain
let value = rpc::system::chain(&sdk.client).await?;
dbg!(value);
/* Output
"Avail Development Network"
*/
// system_chain_type
let value = rpc::system::chain_type(&sdk.client).await?;
dbg!(value);
/* Output
"Development"
*/
// system_health
let value = rpc::system::health(&sdk.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.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.client).await?;
dbg!(value);
/* Output
"12D3KooWRajsCfp1NR15iN7PcwcFAG3LB7iGDKUBosHkevNRQLYs"
*/
// system_name
let value = rpc::system::name(&sdk.client).await?;
dbg!(value);
/* Output
"Avail Node"
*/
// system_node_roles
let value = rpc::system::node_roles(&sdk.client).await?;
dbg!(value);
/* Output
[
Authority,
]
*/
// system_peers
let value = rpc::system::peers(&sdk.client).await?;
dbg!(value);
/* Output
[]
*/
// system_properties
let value = rpc::system::properties(&sdk.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.client).await?;
dbg!(value);
/* Output
SyncState {
starting_block: 0,
current_block: 495,
highest_block: 495,
}
*/
// system_version
let value = rpc::system::version(&sdk.client).await?;
dbg!(value);
/* Output
"2.2.1-55da578d34b"
*/
// state_get_runtime_version
let value = rpc::state::get_runtime_version(&sdk.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.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(&account::alice(), Options::new().app_id(1))
.await?;
let (tx_index, block_hash) = (result.tx_index, Some(result.block_hash));
let value = rpc::kate::query_data_proof(&sdk.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.client, cells, block_hash).await?;
dbg!(value);
/* Output
[
(
2178534751726990040338027377623275511556638494274780568875624948149315822336,
GProof(
[...],
),
),
]
*/
// kate_query_rows
let rows = vec![0u32];
let value = rpc::kate::query_rows(&sdk.client, rows, block_hash).await?;
dbg!(value);
/* Output
[
[
2178534751726990040338027377623275511556638494274780568875624948149315822336,
69809044805081050561201039752112594468796256047454289799440609083602104564736,
26941852917393734161602180963833199552029986735939578666038548832600818441216,
14351520191331507525755130937317610561547699892218140156652644610507664261120,
],
]
*/
Ok(())
}
Storage
use avail_rust::prelude::*;
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?;
println!("Storage finished correctly");
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 key = String::from("Reserved-1").as_bytes().to_vec();
let key = Param0 { 0: key };
let block_hash = sdk.client.best_block_hash().await?;
let storage = sdk.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 block_hash = sdk.client.best_block_hash().await?;
let storage = sdk.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 block_hash = sdk.client.best_block_hash().await?;
let storage = sdk.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 block_hash = sdk.client.best_block_hash().await?;
let storage = sdk.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 account_id = account::account_id_from_str("5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY")?; // Alice_Stash
let block_hash = sdk.client.best_block_hash().await?;
let storage = sdk.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 block_hash = sdk.client.best_block_hash().await?;
let storage = sdk.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 account = account::alice();
let account_id = account.public_key().to_account_id();
let block_hash = sdk.client.best_block_hash().await?;
let storage = sdk.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 block_hash = sdk.client.best_block_hash().await?;
let storage = sdk.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(())
}
Validator
use avail_rust::{
prelude::*,
transactions::staking::{Commission, RewardDestination},
};
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
let account = account::charlie();
// Bond min_validator_bond or 1 AVAIL token
let storage = sdk.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, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Generate Session Keys
let keys = rpc::author::rotate_keys(&sdk.client).await?;
// Set Keys
let tx = sdk.tx.session.set_keys(keys);
let res = tx.execute_and_watch_inclusion(&account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
// Validate
let commission = Commission::new(10)?;
let tx = sdk.tx.staking.validate(commission, false);
let res = tx.execute_and_watch_inclusion(&account, Options::new()).await?;
assert_eq!(res.is_successful(), Some(true));
Ok(())
}
Transaction State
use avail_rust::prelude::*;
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::local_endpoint()).await?;
// Transaction will be signed, and sent.
//
// There is no guarantee that the transaction was executed at all. It might have been
// dropped or discarded for various reasons. The caller is responsible for querying future
// blocks in order to determine the execution status of that transaction.
let tx = sdk.tx.data_availability.submit_data(vec![0, 1, 2]);
let tx_hash = tx.execute(&account::alice(), Options::new().app_id(1)).await?;
println!("Tx Hash: {:?}", tx_hash);
let result = loop {
let res = sdk.client.transaction_state(&tx_hash, false).await?;
if !res.is_empty() {
break res;
}
std::thread::sleep(std::time::Duration::from_secs(1));
};
assert_eq!(result.len(), 1);
println!(
"Block Hash: {:?}, Block Height: {}, Tx Hash: {:?}, Tx Index {}",
result[0].block_hash, result[0].block_height, result[0].tx_hash, result[0].tx_index
);
println!(
"Pallet Index: {:?}, Call Index: {}, Tx Success: {:?}, Is Finalized {}",
result[0].pallet_index, result[0].call_index, result[0].tx_success, result[0].is_finalized
);
println!("Transaction State finished correctly");
Ok(())
}
Indexer
use std::{
future::Future,
sync::{atomic::AtomicBool, Arc, Mutex},
time::Duration,
};
use avail_rust::prelude::*;
use tokio::task::JoinHandle;
type SharedLock<T> = Arc<Mutex<T>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Kind {
Manual,
Stream,
}
pub async fn run() -> Result<(), ClientError> {
let sdk = SDK::new(SDK::turing_endpoint()).await?;
let mut indexer = Indexer::new(sdk.clone());
indexer.run(Kind::Stream);
// Fetching blocks in procedural way
let mut sub = indexer.subscribe().await;
for _ in 0..3 {
let block = sub.fetch().await;
println!("Current: Block Height: {}, Block Hash: {:?}", block.height, block.hash)
}
// Fetching historical blocks
sub.block_height -= 100;
for _ in 0..3 {
let block = sub.fetch().await;
println!(
"Historical: Block Height: {}, Block Hash: {:?}",
block.height, block.hash
)
}
// Callback
let mut sub = indexer.callback(callback).await;
tokio::time::sleep(Duration::from_secs(25)).await;
sub.shutdown();
indexer.shutdown();
tokio::time::sleep(Duration::from_secs(3)).await;
Ok(())
}
async fn callback(block: IndexedBlock) {
println!("Callback: Block Height: {}, Block Hash: {:?}", block.height, block.hash)
}
#[derive(Clone)]
struct Indexer {
block: SharedLock<Option<IndexedBlock>>,
sdk: Arc<SDK>,
thread: SharedLock<Option<JoinHandle<()>>>,
}
impl Indexer {
pub fn new(sdk: SDK) -> Self {
Self {
block: Arc::new(Mutex::new(None)),
sdk: Arc::new(sdk),
thread: Arc::new(Mutex::new(None)),
}
}
pub fn run(&mut self, kind: Kind) {
if self.thread.lock().unwrap().is_some() {
return;
}
let block = self.block.clone();
let sdk = self.sdk.clone();
let t = tokio::spawn(async move {
println!("Kind: {:?}", kind);
match kind {
Kind::Manual => Self::task_man(block, sdk).await,
Kind::Stream => Self::task_sub(block, sdk).await,
};
});
self.thread = Arc::new(Mutex::new(Some(t)))
}
pub fn shutdown(&mut self) {
let lock = self.thread.lock().unwrap();
let Some(t) = lock.as_ref() else {
return;
};
t.abort();
}
pub async fn get_block(&self, block_height: u32) -> IndexedBlock {
loop {
let block = self.block.lock().unwrap().clone();
let Some(block) = block else {
tokio::time::sleep(Duration::from_secs(5)).await;
continue;
};
if block_height > block.height {
tokio::time::sleep(Duration::from_secs(5)).await;
continue;
}
if block_height == block.height {
return block;
}
let block_hash = self.sdk.client.block_hash(block_height).await.unwrap();
let block = Block::new(&self.sdk.client, block_hash.clone()).await.unwrap();
return IndexedBlock {
height: block_height,
hash: block_hash,
block,
};
}
}
pub async fn subscribe(&self) -> Subscription {
let block_height = loop {
let height = {
let block = self.block.lock().unwrap();
block.as_ref().map(|x| x.height)
};
if height.is_none() {
tokio::time::sleep(Duration::from_secs(5)).await;
continue;
}
break height.unwrap();
};
Subscription::new(block_height, self.clone())
}
pub async fn callback<F>(&self, cb: fn(IndexedBlock) -> F) -> Subscription
where
F: Future + std::marker::Send + 'static,
{
let sub = self.subscribe().await;
let mut sub2 = sub.clone();
tokio::spawn(async move {
loop {
let block = sub2.fetch().await;
cb(block).await;
}
});
sub
}
async fn task_man(shared_block: SharedLock<Option<IndexedBlock>>, sdk: Arc<SDK>) {
loop {
let new_hash = sdk.client.finalized_block_hash().await.unwrap();
let cur_hash = {
let block = shared_block.lock().unwrap();
block.as_ref().map(|x| x.hash)
};
if cur_hash.is_some_and(|x| x == new_hash) {
tokio::time::sleep(Duration::from_secs(15)).await;
continue;
}
let new_block = Block::new(&sdk.client, new_hash).await.unwrap();
let new_height = sdk.client.block_number(new_hash.clone()).await.unwrap();
let mut cur_block = shared_block.lock().unwrap();
*cur_block = Some(IndexedBlock {
height: new_height,
hash: new_hash,
block: new_block,
})
}
}
async fn task_sub(shared_block: SharedLock<Option<IndexedBlock>>, sdk: Arc<SDK>) {
let mut stream = sdk.client.blocks().subscribe_finalized().await.unwrap();
loop {
let block = stream.next().await.unwrap();
let block = match block {
Ok(b) => b,
Err(e) => {
if e.is_disconnected_will_reconnect() {
println!("The RPC connection was lost and we may have missed a few blocks");
continue;
}
panic!("Something is wrong");
},
};
let height = block.number();
let hash = block.hash();
let block = Block::from_block(block).await.unwrap();
let mut cur_block = shared_block.lock().unwrap();
*cur_block = Some(IndexedBlock { height, hash, block })
}
}
}
#[derive(Clone)]
struct Subscription {
pub indexer: Indexer,
pub block_height: u32,
pub shutdown: Arc<AtomicBool>,
}
impl Subscription {
pub fn new(block_height: u32, indexer: Indexer) -> Self {
Self {
indexer,
block_height,
shutdown: Arc::new(AtomicBool::new(false)),
}
}
pub async fn fetch(&mut self) -> IndexedBlock {
let block = self.indexer.get_block(self.block_height).await;
self.block_height += 1;
block
}
pub fn shutdown(&mut self) {
self.shutdown.store(false, std::sync::atomic::Ordering::Relaxed);
}
}
#[derive(Clone)]
struct IndexedBlock {
height: u32,
hash: H256,
block: Block,
}