Skip to content

Commit 3df940b

Browse files
authored
feat(HIP-551): Introduce BatchTransactions (#1078)
Signed-off-by: gsstoykov <[email protected]>
1 parent 38327a1 commit 3df940b

File tree

13 files changed

+1232
-6
lines changed

13 files changed

+1232
-6
lines changed

.github/workflows/flow-rust-ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ jobs:
114114
uses: hiero-ledger/hiero-solo-action@10ec96a107b8d2f5cd26b3e7ab47e65407b5c462 # v0.11.0
115115
with:
116116
installMirrorNode: true
117-
hieroVersion: v0.60.0-alpha.0
117+
hieroVersion: v0.61.4
118118

119119
- name: Create env file
120120
run: |

examples/batch_transaction.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
use std::str::FromStr;
4+
5+
use hedera::{
6+
AccountBalanceQuery, AccountCreateTransaction, AccountId, BatchTransaction, Client, Hbar, PrivateKey, TransferTransaction
7+
};
8+
9+
#[tokio::main]
10+
#[ignore] // Will currently not be working as the feature is disabled on testnet
11+
async fn main() -> hedera::Result<()> {
12+
// Create client for testnet (you can also use mainnet or previewnet)
13+
let client = Client::for_testnet();
14+
15+
// Set operator (the account that pays for transactions)
16+
let operator_key = PrivateKey::from_str_ed25519(
17+
"302e020100300506032b657004220420a869f4c6191b9c8c99933e7f6b6611711737e4b1a1a5a4cb5370e719a1f6df98"
18+
)?;
19+
let operator_account = AccountId::from_str("0.0.1001")?;
20+
client.set_operator(operator_account, operator_key);
21+
22+
println!("BatchTransaction Example");
23+
println!("========================");
24+
25+
// Step 1: Create a batch key
26+
// This key will be used to sign the batch transaction itself
27+
let batch_key = PrivateKey::generate_ed25519();
28+
println!("Generated batch key: {}", batch_key.public_key());
29+
30+
// Step 2: Create some accounts that will be involved in transfers
31+
let alice_key = PrivateKey::generate_ed25519();
32+
let alice = create_account(&client, alice_key.public_key(), Hbar::new(5)).await?;
33+
println!("Created Alice account: {}", alice);
34+
35+
let bob_key = PrivateKey::generate_ed25519();
36+
let bob = create_account(&client, bob_key.public_key(), Hbar::new(3)).await?;
37+
println!("Created Bob account: {}", bob);
38+
39+
// Step 3: Create individual transactions and prepare them for batching
40+
println!("\nPreparing batch transactions...");
41+
42+
// Create a transfer from Alice to the operator
43+
let mut alice_transfer = TransferTransaction::new();
44+
alice_transfer
45+
.hbar_transfer(alice, Hbar::new(-1)) // Alice sends 1 HBAR
46+
.hbar_transfer(operator_account, Hbar::new(1)); // Operator receives 1 HBAR
47+
48+
// Freeze the transaction and set batch key
49+
alice_transfer.freeze_with(&client)?;
50+
alice_transfer.set_batch_key(batch_key.public_key().into());
51+
alice_transfer.sign(alice_key.clone());
52+
53+
// Create a transfer from Bob to the operator
54+
let mut bob_transfer = TransferTransaction::new();
55+
bob_transfer
56+
.hbar_transfer(bob, Hbar::new(-2)) // Bob sends 2 HBAR
57+
.hbar_transfer(operator_account, Hbar::new(2)); // Operator receives 2 HBAR
58+
59+
// Freeze the transaction and set batch key
60+
bob_transfer.freeze_with(&client)?;
61+
bob_transfer.set_batch_key(batch_key.public_key().into());
62+
bob_transfer.sign(bob_key.clone());
63+
64+
// Step 4: Get balances before batch execution
65+
println!("\nBalances before batch execution:");
66+
print_balance(&client, "Alice", alice).await?;
67+
print_balance(&client, "Bob", bob).await?;
68+
print_balance(&client, "Operator", operator_account).await?;
69+
70+
// Step 5: Create and execute the batch transaction
71+
println!("\nExecuting batch transaction...");
72+
73+
let mut batch = BatchTransaction::new();
74+
batch.add_inner_transaction(alice_transfer.into())?;
75+
batch.add_inner_transaction(bob_transfer.into())?;
76+
batch.freeze_with(&client)?;
77+
batch.sign(batch_key);
78+
79+
// Execute the batch transaction
80+
let response = batch.execute(&client).await?;
81+
let receipt = response.get_receipt(&client).await?;
82+
83+
println!("Batch transaction executed successfully!");
84+
println!("Transaction ID: {}", response.transaction_id);
85+
println!("Status: {:?}", receipt.status);
86+
87+
// Step 6: Get balances after batch execution
88+
println!("\nBalances after batch execution:");
89+
print_balance(&client, "Alice", alice).await?;
90+
print_balance(&client, "Bob", bob).await?;
91+
print_balance(&client, "Operator", operator_account).await?;
92+
93+
// Step 7: Get inner transaction IDs
94+
println!("\nInner transaction IDs:");
95+
for (i, tx_id) in batch.get_inner_transaction_ids().iter().enumerate() {
96+
if let Some(id) = tx_id {
97+
println!("Transaction {}: {}", i + 1, id);
98+
}
99+
}
100+
101+
println!("\nBatchTransaction example completed successfully!");
102+
103+
Ok(())
104+
}
105+
106+
async fn create_account(
107+
client: &Client,
108+
public_key: hedera::PublicKey,
109+
initial_balance: Hbar,
110+
) -> hedera::Result<AccountId> {
111+
let response = AccountCreateTransaction::new()
112+
.set_key_without_alias(public_key)
113+
.initial_balance(initial_balance)
114+
.execute(client)
115+
.await?;
116+
117+
let receipt = response.get_receipt(client).await?;
118+
receipt.account_id.ok_or_else(|| {
119+
hedera::Error::TimedOut(Box::new(hedera::Error::GrpcStatus(
120+
tonic::Status::not_found("account_id not found in receipt"),
121+
)))
122+
})
123+
}
124+
125+
async fn print_balance(client: &Client, name: &str, account_id: AccountId) -> hedera::Result<()> {
126+
let balance = AccountBalanceQuery::new()
127+
.account_id(account_id)
128+
.execute(client)
129+
.await?;
130+
println!("{}: {} HBAR", name, balance.hbars);
131+
Ok(())
132+
}

0 commit comments

Comments
 (0)