diff --git a/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt b/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt index 784db84..43dd182 100644 --- a/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt +++ b/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt @@ -1,3 +1,5 @@ +import java.util.Optional +import kotlin.ExperimentalUnsignedTypes import uniffi.bdk.* class LogProgress : BdkProgress { @@ -10,18 +12,37 @@ class NullProgress : BdkProgress { override fun update(progress: Float, message: String?) {} } -fun getConfirmedTransaction( - wallet: OnlineWalletInterface, - transactionId: String -): ConfirmedTransaction? { +fun getTransaction(wallet: OnlineWalletInterface, transactionId: String): Optional { wallet.sync(NullProgress(), null) return wallet.getTransactions() .stream() - .filter({ it.id.equals(transactionId) }) + .filter({ + when (it) { + is Transaction.Confirmed -> it.details.id.equals(transactionId) + is Transaction.Unconfirmed -> it.details.id.equals(transactionId) + } + }) .findFirst() - .orElse(null) } +@ExperimentalUnsignedTypes +val unconfirmedFirstThenByTimestampDescending = + Comparator { a, b -> + when { + (a is Transaction.Confirmed && b is Transaction.Confirmed) -> { + val comparison = b.confirmation.timestamp.compareTo(a.confirmation.timestamp) + when { + comparison == 0 -> b.details.id.compareTo(a.details.id) + else -> comparison + } + } + (a is Transaction.Confirmed && b is Transaction.Unconfirmed) -> 1 + (a is Transaction.Unconfirmed && b is Transaction.Confirmed) -> -1 + else -> 0 + } + } + +@ExperimentalUnsignedTypes fun main(args: Array) { println("Configuring an in-memory wallet on electrum..") val descriptor = "pkh(cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR)" @@ -41,23 +62,19 @@ fun main(args: Array) { println("New wallet balance: ${wallet.getBalance()}") println("Press Enter to return funds") readLine() - println( - "Creating a PSBT with recipient $recipient and amount $amount satoshis..." - ) + println("Creating a PSBT with recipient $recipient and amount $amount satoshis...") val transaction = PartiallySignedBitcoinTransaction(wallet, recipient, amount) println("Signing the transaction...") wallet.sign(transaction) println("Broadcasting the signed transaction...") val transactionId = wallet.broadcast(transaction) println("Broadcasted transaction with id $transactionId") - println("Confirming transaction...") - var confirmedTransaction = getConfirmedTransaction(wallet, transactionId) - while (confirmedTransaction == null) { - confirmedTransaction = getConfirmedTransaction(wallet, transactionId) - } - println("Confirmed transaction: $confirmedTransaction") - val transactions = wallet.getTransactions() - println("Listing all ${transactions.size} transactions...") - transactions.sortedByDescending { it.timestamp }.forEach { println(it) } + val take = 5 + println("Listing latest $take transactions...") + wallet + .getTransactions() + .sortedWith(unconfirmedFirstThenByTimestampDescending) + .take(take) + .forEach { println(it) } println("Final wallet balance: ${wallet.getBalance()}") } diff --git a/src/bdk.udl b/src/bdk.udl index 138950c..c52b9ac 100644 --- a/src/bdk.udl +++ b/src/bdk.udl @@ -63,15 +63,24 @@ interface DatabaseConfig { Sled(SledDbConfiguration config); }; -dictionary ConfirmedTransaction { +dictionary TransactionDetails { u64? fees; - u32 height; - u64 timestamp; u64 received; u64 sent; string id; }; +dictionary Confirmation { + u32 height; + u64 timestamp; +}; + +[Enum] +interface Transaction { + Unconfirmed(TransactionDetails details); + Confirmed(TransactionDetails details, Confirmation confirmation); +}; + interface OfflineWallet { [Throws=BdkError] constructor(string descriptor, Network network, DatabaseConfig database_config); @@ -83,7 +92,7 @@ interface OfflineWallet { [Throws=BdkError] void sign([ByRef] PartiallySignedBitcoinTransaction psbt); [Throws=BdkError] - sequence get_transactions(); + sequence get_transactions(); }; dictionary ElectrumConfig { @@ -123,7 +132,7 @@ interface OnlineWallet { [Throws=BdkError] void sign([ByRef] PartiallySignedBitcoinTransaction psbt); [Throws=BdkError] - sequence get_transactions(); + sequence get_transactions(); // OnlineWalletInterface Network get_network(); diff --git a/src/lib.rs b/src/lib.rs index 2972e14..02c22b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ use bdk::blockchain::{ use bdk::database::any::{AnyDatabase, SledDbConfiguration}; use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase}; use bdk::wallet::AddressIndex; -use bdk::{Error, SignOptions, Wallet}; +use bdk::{ConfirmationTime, Error, SignOptions, Wallet}; use std::convert::TryFrom; use std::str::FromStr; use std::sync::{Mutex, MutexGuard}; @@ -58,15 +58,24 @@ impl WalletHolder<()> for OfflineWallet { } #[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct ConfirmedTransaction { +pub struct TransactionDetails { pub fees: Option, - pub height: u32, - pub timestamp: u64, pub received: u64, pub sent: u64, pub id: String, } +type Confirmation = ConfirmationTime; +pub enum Transaction { + Unconfirmed { + details: TransactionDetails, + }, + Confirmed { + details: TransactionDetails, + confirmation: Confirmation, + }, +} + trait OfflineWalletOperations: WalletHolder { fn get_new_address(&self) -> String { self.get_wallet() @@ -92,18 +101,24 @@ trait OfflineWalletOperations: WalletHolder { } } - fn get_transactions(&self) -> Result, Error> { + fn get_transactions(&self) -> Result, Error> { let transactions = self.get_wallet().list_transactions(true)?; Ok(transactions .iter() - .filter(|x| x.confirmation_time.is_some()) - .map(|x| ConfirmedTransaction { - fees: x.fee, - height: x.confirmation_time.clone().map_or(0, |c| c.height), - timestamp: x.confirmation_time.clone().map_or(0, |c| c.timestamp), - id: x.txid.to_string(), - received: x.received, - sent: x.sent, + .map(|x| -> Transaction { + let details = TransactionDetails { + fees: x.fee, + id: x.txid.to_string(), + received: x.received, + sent: x.sent, + }; + match x.confirmation_time.clone() { + Some(confirmation) => Transaction::Confirmed { + details, + confirmation, + }, + None => Transaction::Unconfirmed { details }, + } }) .collect()) }