Private Transaction Builder
Private Transaction Builder - Complete API Reference
This document provides a comprehensive guide to building private transactions using the Roru SDK's TransactionBuilder API.
Overview
The TransactionBuilder provides a fluent, type-safe API for constructing private transactions. It handles note selection, change calculation, fee estimation, and transaction structure automatically.
Basic Usage
Simple Transaction
Rust Example:
use roru_sdk::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
// Initialize client
let client = RoruClient::new(ClientConfig::default()).await?;
let wallet = client.create_wallet().await?;
// Generate recipient address
let recipient = wallet.generate_address()?;
// Build transaction
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000) // 1.0 tokens (6 decimals)
.build()?;
// Send transaction
let result = wallet.send_transaction(tx).await?;
println!("Transaction sent: {:?}", result.tx_hash);
Ok(())
}TypeScript Example:
import { RoruClient, TransactionBuilder } from '@roru/sdk';
async function main() {
const client = new RoruClient({
infraEndpoint: 'https://infra.roru.labs'
});
const wallet = await client.createWallet();
const recipient = wallet.generateAddress();
const tx = new TransactionBuilder()
.to(recipient)
.amount(1_000_000) // 1.0 tokens
.build();
const result = await wallet.sendTransaction(tx);
console.log('Transaction sent:', result.txHash);
}
main().catch(console.error);Python Example:
from roru_sdk import RoruClient, TransactionBuilder
async def main():
client = RoruClient()
wallet = await client.create_wallet()
recipient = wallet.generate_address()
tx = TransactionBuilder() \
.to(recipient) \
.amount(1_000_000) \
.build()
result = await wallet.send_transaction(tx)
print(f"Transaction sent: {result.tx_hash}")
asyncio.run(main())Advanced Usage
Custom Input Selection
Rust:
// Manually select input notes
let input_notes = vec![
wallet.get_note_by_index(0)?,
wallet.get_note_by_index(1)?,
];
let tx = TransactionBuilder::new()
.inputs(input_notes)
.to(recipient)
.amount(500_000)
.build()?;Multiple Outputs
Rust:
let recipient1 = wallet.generate_address()?;
let recipient2 = wallet.generate_address()?;
let tx = TransactionBuilder::new()
.to(recipient1)
.amount(500_000)
.add_output(recipient2, 300_000)
.build()?;With Memo
Rust:
let memo = b"Payment for services";
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.memo(memo)
.build()?;Custom Fee
Rust:
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.fee(10_000) // Custom fee amount
.build()?;Priority Level
Rust:
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.priority(Priority::High) // Fast settlement
.build()?;API Reference
TransactionBuilder Methods
new() -> TransactionBuilder
new() -> TransactionBuilderCreates a new transaction builder instance.
Returns: TransactionBuilder
Example:
let builder = TransactionBuilder::new();to(address: ShieldedAddress) -> Self
to(address: ShieldedAddress) -> SelfSets the primary recipient address.
Parameters:
address: The shielded address of the recipient
Returns: Self for method chaining
Example:
let tx = TransactionBuilder::new()
.to(recipient_address)
.amount(1_000_000)
.build()?;amount(value: u64) -> Self
amount(value: u64) -> SelfSets the transaction amount in smallest units.
Parameters:
value: Amount in smallest token unit (e.g., 1_000_000 = 1.0 tokens with 6 decimals)
Returns: Self for method chaining
Example:
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000) // 1.0 tokens
.build()?;inputs(notes: Vec<Note>) -> Self
inputs(notes: Vec<Note>) -> SelfManually specify input notes to use.
Parameters:
notes: Vector of notes to spend
Returns: Self for method chaining
Example:
let input_notes = wallet.get_notes()?;
let tx = TransactionBuilder::new()
.inputs(input_notes)
.to(recipient)
.amount(500_000)
.build()?;add_output(address: ShieldedAddress, amount: u64) -> Self
add_output(address: ShieldedAddress, amount: u64) -> SelfAdd an additional output to the transaction.
Parameters:
address: Recipient addressamount: Amount for this output
Returns: Self for method chaining
Example:
let tx = TransactionBuilder::new()
.to(recipient1)
.amount(500_000)
.add_output(recipient2, 300_000)
.build()?;memo(data: &[u8]) -> Self
memo(data: &[u8]) -> SelfAdd a memo field to the transaction.
Parameters:
data: Memo data (max 512 bytes)
Returns: Self for method chaining
Example:
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.memo(b"Payment ID: 12345")
.build()?;fee(amount: u64) -> Self
fee(amount: u64) -> SelfSet a custom fee amount.
Parameters:
amount: Fee amount in smallest units
Returns: Self for method chaining
Example:
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.fee(10_000)
.build()?;priority(level: Priority) -> Self
priority(level: Priority) -> SelfSet transaction priority level.
Parameters:
level:Priority::Low,Priority::Normal, orPriority::High
Returns: Self for method chaining
Example:
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.priority(Priority::High)
.build()?;asset(asset_id: AssetId) -> Self
asset(asset_id: AssetId) -> SelfSpecify the asset for this transaction.
Parameters:
asset_id: Asset identifier
Returns: Self for method chaining
Example:
let usdc = AssetId::new(ChainId::Ethereum, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.asset(usdc)
.build()?;expiry(timestamp: u64) -> Self
expiry(timestamp: u64) -> SelfSet transaction expiry timestamp.
Parameters:
timestamp: Unix timestamp when transaction expires
Returns: Self for method chaining
Example:
let expiry = current_timestamp() + 3600; // 1 hour from now
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.expiry(expiry)
.build()?;build() -> Result<Transaction>
build() -> Result<Transaction>Builds the final transaction.
Returns: Result<Transaction>
Errors:
InsufficientBalance: Not enough fundsInvalidAddress: Invalid recipient addressInvalidAmount: Invalid amount valueNoteSelectionFailed: Could not select appropriate notes
Example:
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.build()?;Transaction Structure
Transaction Type
pub struct Transaction {
pub inputs: Vec<Input>,
pub outputs: Vec<Output>,
pub proof: Option<Proof>, // Generated during send
pub nullifiers: Vec<Nullifier>,
pub public_data: PublicData,
pub signature: Option<Signature>, // Added during signing
pub metadata: TransactionMetadata,
}Input Structure
pub struct Input {
pub note: Note,
pub merkle_path: MerklePath,
pub nullifier: Nullifier,
}Output Structure
pub struct Output {
pub commitment: Commitment,
pub encrypted_note: EncryptedNote,
pub recipient: ShieldedAddress,
}Error Handling
Common Errors
InsufficientBalance:
match tx_builder.build() {
Ok(tx) => { /* success */ }
Err(RoruError::InsufficientBalance) => {
println!("Not enough funds. Current balance: {}", wallet.get_balance()?);
}
Err(e) => { /* other error */ }
}InvalidAddress:
match tx_builder.to(invalid_address).build() {
Ok(tx) => { /* success */ }
Err(RoruError::InvalidAddress(msg)) => {
println!("Invalid address: {}", msg);
}
Err(e) => { /* other error */ }
}Best Practices
1. Always Check Balance First
let balance = wallet.get_balance().await?;
if balance.shielded < amount {
return Err(Error::InsufficientBalance);
}2. Handle Change Properly
The builder automatically handles change, but you can verify:
let tx = builder.build()?;
if let Some(change) = tx.get_change() {
println!("Change amount: {}", change);
}3. Use Appropriate Priority
// For time-sensitive transactions
let tx = builder
.priority(Priority::High)
.build()?;
// For regular transactions
let tx = builder
.priority(Priority::Normal)
.build()?;4. Validate Before Building
// Validate recipient address
if !wallet.is_valid_address(&recipient) {
return Err(Error::InvalidAddress);
}
// Validate amount
if amount == 0 || amount > MAX_AMOUNT {
return Err(Error::InvalidAmount);
}Complete Example
Rust - Full Transaction Flow:
use roru_sdk::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
// Initialize
let config = ClientConfig {
infra_endpoint: "https://infra.roru.labs".to_string(),
api_key: Some(env::var("RORU_API_KEY")?),
..Default::default()
};
let client = RoruClient::new(config).await?;
let wallet = client.load_wallet(&master_key).await?;
// Sync state
wallet.sync_state().await?;
// Check balance
let balance = wallet.get_balance().await?;
println!("Current balance: {}", balance.shielded);
// Generate recipient address
let recipient = ShieldedAddress::from_string(
"roru1abc123..."
)?;
// Build transaction
let tx = TransactionBuilder::new()
.to(recipient)
.amount(1_000_000)
.memo(b"Payment for services")
.priority(Priority::Normal)
.build()?;
// Estimate fees
let fee_estimate = wallet.estimate_fees(&tx).await?;
println!("Estimated fee: {}", fee_estimate.total);
// Send transaction
let result = wallet.send_transaction(tx).await?;
// Wait for confirmation
let confirmation = wallet.wait_for_confirmation(
result.tx_hash,
Duration::from_secs(60)
).await?;
println!("Transaction confirmed in block: {}", confirmation.block_number);
Ok(())
}Conclusion
The TransactionBuilder provides:
Type Safety: Compile-time validation
Flexibility: Multiple configuration options
Automatic Handling: Note selection, change, fees
Error Handling: Clear error messages
Developer Experience: Fluent, intuitive API
Use the builder for all private transaction creation in your applications.
Last updated
