Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/flow-rust-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ jobs:
uses: hiero-ledger/hiero-solo-action@10ec96a107b8d2f5cd26b3e7ab47e65407b5c462 # v0.11.0
with:
installMirrorNode: true
hieroVersion: v0.60.0-alpha.0
hieroVersion: v0.61.4

- name: Create env file
run: |
Expand Down
132 changes: 132 additions & 0 deletions examples/batch_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: Apache-2.0

use std::str::FromStr;

use hedera::{
AccountBalanceQuery, AccountCreateTransaction, AccountId, BatchTransaction, Client, Hbar, PrivateKey, TransferTransaction
};

#[tokio::main]
#[ignore] // Will currently not be working as the feature is disabled on testnet
async fn main() -> hedera::Result<()> {

Check warning on line 11 in examples/batch_transaction.rs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/batch_transaction.rs#L11

Method main has 60 lines of code (limit is 50)

Check warning on line 11 in examples/batch_transaction.rs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

examples/batch_transaction.rs#L11

Method main has a cyclomatic complexity of 20 (limit is 8)
// Create client for testnet (you can also use mainnet or previewnet)
let client = Client::for_testnet();

// Set operator (the account that pays for transactions)
let operator_key = PrivateKey::from_str_ed25519(
"302e020100300506032b657004220420a869f4c6191b9c8c99933e7f6b6611711737e4b1a1a5a4cb5370e719a1f6df98"
)?;
let operator_account = AccountId::from_str("0.0.1001")?;
client.set_operator(operator_account, operator_key);

println!("BatchTransaction Example");
println!("========================");

// Step 1: Create a batch key
// This key will be used to sign the batch transaction itself
let batch_key = PrivateKey::generate_ed25519();
println!("Generated batch key: {}", batch_key.public_key());

// Step 2: Create some accounts that will be involved in transfers
let alice_key = PrivateKey::generate_ed25519();
let alice = create_account(&client, alice_key.public_key(), Hbar::new(5)).await?;
println!("Created Alice account: {}", alice);

let bob_key = PrivateKey::generate_ed25519();
let bob = create_account(&client, bob_key.public_key(), Hbar::new(3)).await?;
println!("Created Bob account: {}", bob);

// Step 3: Create individual transactions and prepare them for batching
println!("\nPreparing batch transactions...");

// Create a transfer from Alice to the operator
let mut alice_transfer = TransferTransaction::new();
alice_transfer
.hbar_transfer(alice, Hbar::new(-1)) // Alice sends 1 HBAR
.hbar_transfer(operator_account, Hbar::new(1)); // Operator receives 1 HBAR

// Freeze the transaction and set batch key
alice_transfer.freeze_with(&client)?;
alice_transfer.set_batch_key(batch_key.public_key().into());
alice_transfer.sign(alice_key.clone());

// Create a transfer from Bob to the operator
let mut bob_transfer = TransferTransaction::new();
bob_transfer
.hbar_transfer(bob, Hbar::new(-2)) // Bob sends 2 HBAR
.hbar_transfer(operator_account, Hbar::new(2)); // Operator receives 2 HBAR

// Freeze the transaction and set batch key
bob_transfer.freeze_with(&client)?;
bob_transfer.set_batch_key(batch_key.public_key().into());
bob_transfer.sign(bob_key.clone());

// Step 4: Get balances before batch execution
println!("\nBalances before batch execution:");
print_balance(&client, "Alice", alice).await?;
print_balance(&client, "Bob", bob).await?;
print_balance(&client, "Operator", operator_account).await?;

// Step 5: Create and execute the batch transaction
println!("\nExecuting batch transaction...");

let mut batch = BatchTransaction::new();
batch.add_inner_transaction(alice_transfer.into())?;
batch.add_inner_transaction(bob_transfer.into())?;
batch.freeze_with(&client)?;
batch.sign(batch_key);

// Execute the batch transaction
let response = batch.execute(&client).await?;
let receipt = response.get_receipt(&client).await?;

println!("Batch transaction executed successfully!");
println!("Transaction ID: {}", response.transaction_id);
println!("Status: {:?}", receipt.status);

// Step 6: Get balances after batch execution
println!("\nBalances after batch execution:");
print_balance(&client, "Alice", alice).await?;
print_balance(&client, "Bob", bob).await?;
print_balance(&client, "Operator", operator_account).await?;

// Step 7: Get inner transaction IDs
println!("\nInner transaction IDs:");
for (i, tx_id) in batch.get_inner_transaction_ids().iter().enumerate() {
if let Some(id) = tx_id {
println!("Transaction {}: {}", i + 1, id);
}
}

println!("\nBatchTransaction example completed successfully!");

Ok(())
}

async fn create_account(
client: &Client,
public_key: hedera::PublicKey,
initial_balance: Hbar,
) -> hedera::Result<AccountId> {
let response = AccountCreateTransaction::new()
.set_key_without_alias(public_key)
.initial_balance(initial_balance)
.execute(client)
.await?;

let receipt = response.get_receipt(client).await?;
receipt.account_id.ok_or_else(|| {
hedera::Error::TimedOut(Box::new(hedera::Error::GrpcStatus(
tonic::Status::not_found("account_id not found in receipt"),
)))
})
}

async fn print_balance(client: &Client, name: &str, account_id: AccountId) -> hedera::Result<()> {
let balance = AccountBalanceQuery::new()
.account_id(account_id)
.execute(client)
.await?;
println!("{}: {} HBAR", name, balance.hbars);
Ok(())
}
Loading