use alloy::providers::ProviderBuilder;
use alloy_primitives::{aliases::U192, Address, U256};
use ostium_rust_sdk::{
contracts::TradingContract,
types::{OpenOrderType, Trade},
Network, OstiumClient, UnsignedTransactionParams,
};
use std::str::FromStr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
println!("🚀 Working Unsigned Transaction Example");
println!("======================================");
let _client = OstiumClient::new(Network::Testnet).await?;
println!("✅ Connected to Ostium testnet (no signer required)");
let trader_address = Address::from_str("0x742d35Cc6634C0532925a3b8D53A2e1c5Bc2D8db")?;
println!("👤 Using trader address: {}", trader_address);
println!("\n🔧 Building unsigned transactions using direct contract calls...");
let provider = ProviderBuilder::new()
.connect("https://sepolia-rollup.arbitrum.io/rpc")
.await?;
let contract_address = Address::from_str("0x2a9b9c988393f46a2537b0ff11e98c2c15a95afe")?;
let trading_contract = TradingContract::new(contract_address, provider);
let tx_params = UnsignedTransactionParams {
from: trader_address,
include_gas_estimates: true,
include_nonce: true,
};
println!("\n📈 Example 1: Building unsigned transaction for opening a trade");
let trade = Trade {
collateral: U256::from(1000_000000u64), open_price: 0, tp: 45000_000000000000000000u128, sl: 35000_000000000000000000u128, trader: trader_address,
leverage: 1000, pair_index: 0, index: 0, buy: true, };
match trading_contract
.open_trade_unsigned(
trade.clone(),
OpenOrderType::Market,
U256::from(100), tx_params.clone(),
)
.await
{
Ok(unsigned_tx) => {
println!("✅ Successfully built open trade unsigned transaction!");
print_transaction_details(&unsigned_tx, "Open Trade");
let frontend_payload = serde_json::to_string_pretty(&unsigned_tx)?;
println!("\n📤 JSON payload for frontend (open trade):");
println!("{}", frontend_payload);
}
Err(e) => {
println!("❌ Failed to build open trade transaction: {}", e);
}
}
println!("\n📉 Example 2: Building unsigned transaction for closing a trade");
match trading_contract
.close_trade_market_unsigned(
0, 0, 10000, tx_params.clone(),
)
.await
{
Ok(unsigned_tx) => {
println!("✅ Successfully built close trade unsigned transaction!");
print_transaction_details(&unsigned_tx, "Close Trade");
}
Err(e) => {
println!("❌ Failed to build close trade transaction: {}", e);
}
}
println!("\n📊 Example 3: Building unsigned transaction for updating take profit");
match trading_contract
.update_tp_unsigned(
0, 0, U192::from(50000_000000000000000000u128), tx_params.clone(),
)
.await
{
Ok(unsigned_tx) => {
println!("✅ Successfully built update TP unsigned transaction!");
print_transaction_details(&unsigned_tx, "Update Take Profit");
}
Err(e) => {
println!("❌ Failed to build update TP transaction: {}", e);
}
}
println!("\n🛑 Example 4: Building unsigned transaction for updating stop loss");
match trading_contract
.update_sl_unsigned(
0, 0, U192::from(30000_000000000000000000u128), tx_params.clone(),
)
.await
{
Ok(unsigned_tx) => {
println!("✅ Successfully built update SL unsigned transaction!");
print_transaction_details(&unsigned_tx, "Update Stop Loss");
}
Err(e) => {
println!("❌ Failed to build update SL transaction: {}", e);
}
}
println!("\n❌ Example 5: Building unsigned transaction for canceling an order");
match trading_contract
.cancel_open_limit_order_unsigned(
0, 0, tx_params.clone(),
)
.await
{
Ok(unsigned_tx) => {
println!("✅ Successfully built cancel order unsigned transaction!");
print_transaction_details(&unsigned_tx, "Cancel Order");
}
Err(e) => {
println!("❌ Failed to build cancel order transaction: {}", e);
}
}
println!("\n🔄 Example 6: Creating multiple unsigned transactions (batch)");
let mut batch_transactions = Vec::new();
if let Ok(update_tp_tx) = trading_contract
.update_tp_unsigned(
0,
0,
U192::from(55000_000000000000000000u128),
tx_params.clone(),
)
.await
{
batch_transactions.push(("Update TP", update_tp_tx));
}
if let Ok(update_sl_tx) = trading_contract
.update_sl_unsigned(
0,
0,
U192::from(25000_000000000000000000u128),
tx_params.clone(),
)
.await
{
batch_transactions.push(("Update SL", update_sl_tx));
}
if !batch_transactions.is_empty() {
println!(
"✅ Successfully created {} batch transactions!",
batch_transactions.len()
);
for (i, (name, tx)) in batch_transactions.iter().enumerate() {
println!(" {}. {}: {} bytes", i + 1, name, tx.data.len());
}
let transactions_only: Vec<_> = batch_transactions.iter().map(|(_, tx)| tx).collect();
let batch_json = serde_json::to_string_pretty(&transactions_only)?;
println!("\n📤 Batch transactions JSON:");
println!("{}", batch_json);
} else {
println!("❌ No batch transactions were successfully created");
}
println!("\n🔍 Example 7: Transaction data analysis");
if let Ok(simple_tx) = trading_contract
.close_trade_market_unsigned(0, 0, 5000, tx_params.clone())
.await
{
analyze_transaction_data(&simple_tx);
}
println!("\n✨ Summary");
println!("=========");
println!("• Unsigned transactions can be built without requiring private keys");
println!("• Gas estimates and nonces are automatically included when requested");
println!("• Transaction data is properly ABI-encoded for smart contract interaction");
println!("• Batch operations allow multiple transactions to be prepared together");
println!("• All transaction data is serializable to JSON for frontend integration");
println!();
println!("🔐 Security note: Private keys never leave the user's wallet/frontend");
println!("⚡ Performance: Backend pre-builds transactions, frontend just signs");
Ok(())
}
fn print_transaction_details(tx: &ostium_rust_sdk::UnsignedTransaction, operation: &str) {
println!("📋 {} Transaction Details:", operation);
println!(" To: {}", tx.to);
println!(" Data length: {} bytes", tx.data.len());
println!(" Value: {} ETH", tx.value);
println!(" Chain ID: {}", tx.chain_id);
if let Some(gas_limit) = tx.gas_limit {
println!(" Gas limit: {}", gas_limit);
}
if let Some(gas_price) = tx.gas_price {
println!(
" Gas price: {} gwei",
gas_price / U256::from(1_000_000_000u64)
);
}
if let Some(nonce) = tx.nonce {
println!(" Nonce: {}", nonce);
}
}
fn analyze_transaction_data(tx: &ostium_rust_sdk::UnsignedTransaction) {
println!("🔍 Transaction Data Analysis:");
println!(" Contract: {}", tx.to);
println!(" Data size: {} bytes", tx.data.len());
if tx.data.len() >= 4 {
let function_selector = &tx.data[0..4];
println!(" Function selector: 0x{}", hex::encode(function_selector));
let _selector_hex = hex::encode(function_selector);
let function_name = "Unknown function";
println!(" Likely function: {}", function_name);
}
if tx.data.len() > 4 {
let parameter_data = &tx.data[4..];
println!(" Parameter data: {} bytes", parameter_data.len());
}
let preview_len = std::cmp::min(32, tx.data.len());
let preview = &tx.data[0..preview_len];
println!(" Data preview: 0x{}", hex::encode(preview));
}