use ootle_rs::{
ToAccountAddress,
TransactionRequest,
address,
builtin_templates::{UnsignedTransactionBuilder, faucet::IFaucet},
const_nonzero_u64,
default_indexer_url,
displayable::Displayable,
key_provider::PrivateKeyProvider,
keys::HasViewOnlyKeySecret,
provider::{PendingTransaction, Provider, ProviderBuilder, WalletProvider},
stealth::{Output, StealthTransfer},
template_types::{
UtxoAddress,
constants::{TARI, TARI_TOKEN},
},
transaction::TransactionSigner,
wallet::OotleWallet,
};
use tari_ootle_common_types::engine_types::transaction_receipt::TransactionReceipt;
use tari_ootle_transaction::Transaction;
#[tokio::main]
async fn main() {
let recipient = address!( "otl_loc_1c370ayp9849gzmj9gwelyyd086ntrt84w5nkclu32tzyr2pvcfpre43z8z2xvupm6wltw9k5e8tzay3qqf9nfj9v5xuxwcpcxmg22vqlvz86l" );
let indexer_api_url = default_indexer_url(recipient.network());
let sender_secret = PrivateKeyProvider::random(recipient.network());
let sender_address = sender_secret.address().clone();
println!("Sender address: {sender_address}");
println!(
"Sender secrets: {} | {}",
sender_secret.credentials().account_secret().reveal(),
sender_secret.credentials().view_only_secret().reveal()
);
let account_component_addr = sender_address.to_account_address();
println!("Sender account address: {account_component_addr}");
let wallet = OotleWallet::from(sender_secret.clone());
let mut provider = ProviderBuilder::new()
.wallet(wallet)
.connect(indexer_api_url)
.await
.unwrap();
let network = provider.get_network().await.unwrap();
println!("Provider network ID: {network}");
assert_eq!(network, provider.network());
let latest_epoch = provider.get_epoch().await.unwrap();
println!("Latest epoch: {latest_epoch}");
let tari_token = TARI_TOKEN;
const INPUT_AMOUNT: u64 = 10 * TARI + 1000 - 500;
let (faucet_transfer, required_signers) = StealthTransfer::new(tari_token, &provider)
.spend_revealed_input(10 * TARI + 1000)
.to_revealed_output(500u64)
.to_stealth_output(
Output::new(sender_address.clone(), tari_token, const_nonzero_u64!(INPUT_AMOUNT))
)
.prepare()
.await
.unwrap();
let input_to_spend = faucet_transfer.stealth_outputs().to_vec();
let unsigned_tx = IFaucet::new(&provider)
.take_faucet_funds()
.into_stealth_transfer(faucet_transfer)
.and_pay_fee_from_revealed_output()
.prepare()
.await
.expect("Failed to prepare faucet transaction");
let authorizer = provider.wallet().stealth_authorizer(required_signers);
let transaction = TransactionRequest::default()
.with_transaction(unsigned_tx)
.build(&authorizer)
.await
.unwrap();
let pending_tx = provider.send_transaction(transaction).await.unwrap();
print_fancy_results("Faucet transfer", &pending_tx).await;
let (transfer, required_signers) = StealthTransfer::new(tari_token, &provider)
.spend_stealth_input(sender_address.clone(), input_to_spend[0].commitment())
.to_revealed_output(500u64)
.to_stealth_output(
Output::new(recipient, tari_token, const_nonzero_u64!(8 * TARI))
.with_memo_message("transfer from ootle-rs!")
)
.to_stealth_output(Output::new(sender_address, tari_token, const_nonzero_u64!(2*TARI)))
.prepare()
.await
.unwrap();
let unsigned_tx = Transaction::builder(provider.network())
.with_fee_instructions_builder(|builder| {
builder
.stealth_transfer(tari_token, transfer)
.put_last_instruction_output_on_workspace("fees")
.pay_fee_from_bucket("fees")
})
.add_input(tari_token)
.add_input(UtxoAddress::new(tari_token, input_to_spend[0].commitment().into()))
.build_unsigned();
let authorizer = provider.wallet().stealth_authorizer(required_signers);
let result = provider
.sign_and_send_dry_run_with(&authorizer, unsigned_tx.clone())
.await
.unwrap();
let _diff = result.expect_success();
println!("Dry run successful!");
println!(
"Estimated fees for transfer: {}",
result.finalize.fee_receipt.total_fees_charged()
);
let authorizations = authorizer.create_authorizations(&unsigned_tx).await.unwrap();
let transaction = TransactionRequest::default()
.with_transaction(unsigned_tx)
.with_authorizations(authorizations)
.build(&authorizer)
.await
.unwrap();
let pending_tx = provider.send_transaction(transaction).await.unwrap();
print_fancy_results("Stealth transfer", &pending_tx).await;
}
async fn print_fancy_results(label: &str, pending_tx: &PendingTransaction) -> TransactionReceipt {
println!("⌛️ {label} transaction pending... {}", pending_tx.tx_id());
let outcome = pending_tx.watch().await.unwrap();
println!("🏁 Transaction Finalized {}", pending_tx.tx_id());
println!("✅ Outcome: {:?}", outcome);
let receipt = pending_tx.get_receipt().await.unwrap();
println!("-------------------------------------------");
println!(" Transaction Receipt");
println!("-------------------------------------------");
println!("🔹 Epoch: {}", receipt.epoch);
println!("🔹 Transaction ID: {}", pending_tx.tx_id());
println!("🔹 Outcome: {:?}", receipt.outcome);
let fee_receipt = &receipt.fee_receipt;
println!("🔹 Fees Paid: {}", fee_receipt.total_fees_paid());
println!(
"🔹 Fees Overcharge: {} = {} (paid) - {} (charged) - {} (refunded)",
fee_receipt.total_fee_overcharge(),
fee_receipt.total_fees_paid(),
fee_receipt.total_fees_charged(),
fee_receipt.total_refunded()
);
if !receipt.logs.is_empty() {
println!("\n🪵 Logs:");
for log in receipt.logs() {
println!(" - {}", log);
}
}
if !receipt.events.is_empty() {
println!("\n🎉 Events:");
for event in receipt.events() {
println!(" - Substate ID: {}", event.substate_id().display());
println!(" Template Address: {}", event.template_address());
println!(" Topic: {}", event.topic());
println!(" Payload: {{{}}}", event.payload());
println!();
}
}
println!("-------------------------------------------");
receipt
}