diff --git a/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt b/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt index a9e64c6..3ecc066 100644 --- a/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt +++ b/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt @@ -1,40 +1,60 @@ import uniffi.bdk.* -class LogProgress: BdkProgress { - override fun update(progress: Float, message: String? ) { - println("progress: $progress, message: $message") +class LogProgress : BdkProgress { + override fun update(progress: Float, message: String?) { + println("Syncing..") } } +class NullProgress : BdkProgress { + override fun update(progress: Float, message: String?) {} +} + +fun getConfirmedTransaction( + wallet: OnlineWalletInterface, + transactionId: String +): ConfirmedTransaction? { + wallet.sync(NullProgress(), null) + return wallet.getTransactions() + .stream() + .filter({ it.id.equals(transactionId) }) + .findFirst() + .orElse(null) +} + fun main(args: Array) { println("Configuring an in-memory wallet on electrum..") - val descriptor = - "pkh(cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR)"; - val amount = 1000uL; - val recipient = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt"; + val descriptor = "pkh(cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR)" + val amount = 1000uL + val recipient = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt" val db = DatabaseConfig.Memory("") - val client = BlockchainConfig.Electrum(ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u)) + val client = + BlockchainConfig.Electrum( + ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u) + ) val wallet = OnlineWallet(descriptor, Network.TESTNET, db, client) - val address = wallet.getNewAddress() - println("Please send $amount satoshis to address: $address") - readLine() - println("Syncing...") wallet.sync(LogProgress(), null) - val balance = wallet.getBalance() - println("New wallet balance: $balance") + println("Initial wallet balance: ${wallet.getBalance()}") + println("Please send $amount satoshis to address: ${wallet.getNewAddress()}") + readLine() + wallet.sync(LogProgress(), null) + println("New wallet balance: ${wallet.getBalance()}") println("Press Enter to return funds") readLine() - println("Creating a partially signed bitcoin transaction 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("Refunded $amount satoshis to $recipient via transaction id $transactionId") - println("Syncing...") - wallet.sync(LogProgress(), null) - val final_balance = wallet.getBalance() - println("New wallet balance: $final_balance") - println("Press Enter to exit") - readLine() + println("Confirming transaction...") + var confirmedTransaction = getConfirmedTransaction(wallet, transactionId) + while (confirmedTransaction == null) { + confirmedTransaction = getConfirmedTransaction(wallet, transactionId) + } + println("Confirmed transaction: $confirmedTransaction") + println("Final wallet balance: ${wallet.getBalance()}") } diff --git a/bindings/bdk-kotlin/jvm/src/main/kotlin/uniffi/bdk/bdk.kt b/bindings/bdk-kotlin/jvm/src/main/kotlin/uniffi/bdk/bdk.kt index 15731af..2bce10b 100644 --- a/bindings/bdk-kotlin/jvm/src/main/kotlin/uniffi/bdk/bdk.kt +++ b/bindings/bdk-kotlin/jvm/src/main/kotlin/uniffi/bdk/bdk.kt @@ -44,15 +44,15 @@ open class RustBuffer : Structure() { companion object { internal fun alloc(size: Int = 0) = rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_70ef_rustbuffer_alloc(size, status) + _UniFFILib.INSTANCE.ffi_bdk_1945_rustbuffer_alloc(size, status) } internal fun free(buf: RustBuffer.ByValue) = rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_70ef_rustbuffer_free(buf, status) + _UniFFILib.INSTANCE.ffi_bdk_1945_rustbuffer_free(buf, status) } internal fun reserve(buf: RustBuffer.ByValue, additional: Int) = rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_70ef_rustbuffer_reserve(buf, additional, status) + _UniFFILib.INSTANCE.ffi_bdk_1945_rustbuffer_reserve(buf, additional, status) } } @@ -398,6 +398,12 @@ internal fun String.write(buf: RustBufferBuilder) { + + + + + + @@ -483,6 +489,45 @@ internal fun writeOptionalu32(v: UInt?, buf: RustBufferBuilder) { +// Helper functions for pasing values of type ULong? +@ExperimentalUnsignedTypes +internal fun liftOptionalu64(rbuf: RustBuffer.ByValue): ULong? { + return liftFromRustBuffer(rbuf) { buf -> + readOptionalu64(buf) + } +} + +@ExperimentalUnsignedTypes +internal fun readOptionalu64(buf: ByteBuffer): ULong? { + if (buf.get().toInt() == 0) { + return null + } + return ULong.read(buf) +} + +@ExperimentalUnsignedTypes +internal fun lowerOptionalu64(v: ULong?): RustBuffer.ByValue { + return lowerIntoRustBuffer(v) { v, buf -> + writeOptionalu64(v, buf) + } +} + +@ExperimentalUnsignedTypes +internal fun writeOptionalu64(v: ULong?, buf: RustBufferBuilder) { + if (v == null) { + buf.putByte(0) + } else { + buf.putByte(1) + v.write(buf) + } +} + + + + + + + // Helper functions for pasing values of type String? internal fun liftOptionalstring(rbuf: RustBuffer.ByValue): String? { @@ -519,6 +564,44 @@ internal fun writeOptionalstring(v: String?, buf: RustBufferBuilder) { + + + +// Helper functions for pasing values of type List + +@ExperimentalUnsignedTypes +internal fun liftSequenceTypeConfirmedTransaction(rbuf: RustBuffer.ByValue): List { + return liftFromRustBuffer(rbuf) { buf -> + readSequenceTypeConfirmedTransaction(buf) + } +} + +@ExperimentalUnsignedTypes +internal fun readSequenceTypeConfirmedTransaction(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + ConfirmedTransaction.read(buf) + } +} + +@ExperimentalUnsignedTypes +internal fun lowerSequenceTypeConfirmedTransaction(v: List): RustBuffer.ByValue { + return lowerIntoRustBuffer(v) { v, buf -> + writeSequenceTypeConfirmedTransaction(v, buf) + } +} + +@ExperimentalUnsignedTypes +internal fun writeSequenceTypeConfirmedTransaction(v: List, buf: RustBufferBuilder) { + buf.putInt(v.size) + v.forEach { + it.write(buf) + } +} + + + + @Synchronized fun findLibraryName(componentName: String): String { val libOverride = System.getProperty("uniffi.component.${componentName}.libraryOverride") @@ -548,83 +631,91 @@ internal interface _UniFFILib : Library { } } - fun ffi_bdk_70ef_OfflineWallet_object_free(ptr: Pointer, + fun ffi_bdk_1945_OfflineWallet_object_free(ptr: Pointer, uniffi_out_err: RustCallStatus ): Unit - fun bdk_70ef_OfflineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue, + fun bdk_1945_OfflineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue, uniffi_out_err: RustCallStatus ): Pointer - fun bdk_70ef_OfflineWallet_get_new_address(ptr: Pointer, + fun bdk_1945_OfflineWallet_get_new_address(ptr: Pointer, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun bdk_70ef_OfflineWallet_get_balance(ptr: Pointer, + fun bdk_1945_OfflineWallet_get_balance(ptr: Pointer, uniffi_out_err: RustCallStatus ): Long - fun bdk_70ef_OfflineWallet_sign(ptr: Pointer,psbt: Pointer, + fun bdk_1945_OfflineWallet_sign(ptr: Pointer,psbt: Pointer, uniffi_out_err: RustCallStatus ): Unit - fun ffi_bdk_70ef_OnlineWallet_object_free(ptr: Pointer, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_70ef_OnlineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,blockchain_config: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Pointer - - fun bdk_70ef_OnlineWallet_get_new_address(ptr: Pointer, + fun bdk_1945_OfflineWallet_get_transactions(ptr: Pointer, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun bdk_70ef_OnlineWallet_get_balance(ptr: Pointer, + fun ffi_bdk_1945_OnlineWallet_object_free(ptr: Pointer, + uniffi_out_err: RustCallStatus + ): Unit + + fun bdk_1945_OnlineWallet_new(descriptor: RustBuffer.ByValue,network: RustBuffer.ByValue,database_config: RustBuffer.ByValue,blockchain_config: RustBuffer.ByValue, + uniffi_out_err: RustCallStatus + ): Pointer + + fun bdk_1945_OnlineWallet_get_new_address(ptr: Pointer, + uniffi_out_err: RustCallStatus + ): RustBuffer.ByValue + + fun bdk_1945_OnlineWallet_get_balance(ptr: Pointer, uniffi_out_err: RustCallStatus ): Long - fun bdk_70ef_OnlineWallet_sign(ptr: Pointer,psbt: Pointer, + fun bdk_1945_OnlineWallet_sign(ptr: Pointer,psbt: Pointer, uniffi_out_err: RustCallStatus ): Unit - fun bdk_70ef_OnlineWallet_get_network(ptr: Pointer, + fun bdk_1945_OnlineWallet_get_transactions(ptr: Pointer, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun bdk_70ef_OnlineWallet_sync(ptr: Pointer,progress_update: Long,max_address_param: RustBuffer.ByValue, - uniffi_out_err: RustCallStatus - ): Unit - - fun bdk_70ef_OnlineWallet_broadcast(ptr: Pointer,psbt: Pointer, + fun bdk_1945_OnlineWallet_get_network(ptr: Pointer, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun ffi_bdk_70ef_PartiallySignedBitcoinTransaction_object_free(ptr: Pointer, + fun bdk_1945_OnlineWallet_sync(ptr: Pointer,progress_update: Long,max_address_param: RustBuffer.ByValue, uniffi_out_err: RustCallStatus ): Unit - fun bdk_70ef_PartiallySignedBitcoinTransaction_new(wallet: Pointer,recipient: RustBuffer.ByValue,amount: Long, + fun bdk_1945_OnlineWallet_broadcast(ptr: Pointer,psbt: Pointer, + uniffi_out_err: RustCallStatus + ): RustBuffer.ByValue + + fun ffi_bdk_1945_PartiallySignedBitcoinTransaction_object_free(ptr: Pointer, + uniffi_out_err: RustCallStatus + ): Unit + + fun bdk_1945_PartiallySignedBitcoinTransaction_new(wallet: Pointer,recipient: RustBuffer.ByValue,amount: Long, uniffi_out_err: RustCallStatus ): Pointer - fun ffi_bdk_70ef_BdkProgress_init_callback(callback_stub: ForeignCallback, + fun ffi_bdk_1945_BdkProgress_init_callback(callback_stub: ForeignCallback, uniffi_out_err: RustCallStatus ): Unit - fun ffi_bdk_70ef_rustbuffer_alloc(size: Int, + fun ffi_bdk_1945_rustbuffer_alloc(size: Int, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun ffi_bdk_70ef_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue, + fun ffi_bdk_1945_rustbuffer_from_bytes(bytes: ForeignBytes.ByValue, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue - fun ffi_bdk_70ef_rustbuffer_free(buf: RustBuffer.ByValue, + fun ffi_bdk_1945_rustbuffer_free(buf: RustBuffer.ByValue, uniffi_out_err: RustCallStatus ): Unit - fun ffi_bdk_70ef_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int, + fun ffi_bdk_1945_rustbuffer_reserve(buf: RustBuffer.ByValue,additional: Int, uniffi_out_err: RustCallStatus ): RustBuffer.ByValue @@ -1223,6 +1314,55 @@ data class SledDbConfiguration ( +} + +@ExperimentalUnsignedTypes +data class ConfirmedTransaction ( + var fees: ULong?, + var height: UInt, + var timestamp: ULong, + var received: ULong, + var sent: ULong, + var id: String +) { + companion object { + internal fun lift(rbuf: RustBuffer.ByValue): ConfirmedTransaction { + return liftFromRustBuffer(rbuf) { buf -> ConfirmedTransaction.read(buf) } + } + + internal fun read(buf: ByteBuffer): ConfirmedTransaction { + return ConfirmedTransaction( + readOptionalu64(buf), + UInt.read(buf), + ULong.read(buf), + ULong.read(buf), + ULong.read(buf), + String.read(buf) + ) + } + } + + internal fun lower(): RustBuffer.ByValue { + return lowerIntoRustBuffer(this, {v, buf -> v.write(buf)}) + } + + internal fun write(buf: RustBufferBuilder) { + writeOptionalu64(this.fees, buf) + + this.height.write(buf) + + this.timestamp.write(buf) + + this.received.write(buf) + + this.sent.write(buf) + + this.id.write(buf) + + } + + + } @ExperimentalUnsignedTypes @@ -1326,6 +1466,7 @@ public interface OfflineWalletInterface { fun getNewAddress(): String fun getBalance(): ULong fun sign(psbt: PartiallySignedBitcoinTransaction ) + fun getTransactions(): List } @@ -1336,7 +1477,7 @@ class OfflineWallet( constructor(descriptor: String, network: Network, databaseConfig: DatabaseConfig ) : this( rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_OfflineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower() ,status) + _UniFFILib.INSTANCE.bdk_1945_OfflineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower() ,status) }) /** @@ -1349,7 +1490,7 @@ class OfflineWallet( */ override protected fun freeRustArcPtr() { rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_70ef_OfflineWallet_object_free(this.pointer, status) + _UniFFILib.INSTANCE.ffi_bdk_1945_OfflineWallet_object_free(this.pointer, status) } } @@ -1364,7 +1505,7 @@ class OfflineWallet( override fun getNewAddress(): String = callWithPointer { rustCall() { status -> - _UniFFILib.INSTANCE.bdk_70ef_OfflineWallet_get_new_address(it, status) + _UniFFILib.INSTANCE.bdk_1945_OfflineWallet_get_new_address(it, status) } }.let { String.lift(it) @@ -1373,7 +1514,7 @@ class OfflineWallet( override fun getBalance(): ULong = callWithPointer { rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_OfflineWallet_get_balance(it, status) + _UniFFILib.INSTANCE.bdk_1945_OfflineWallet_get_balance(it, status) } }.let { ULong.lift(it) @@ -1382,10 +1523,19 @@ class OfflineWallet( override fun sign(psbt: PartiallySignedBitcoinTransaction ) = callWithPointer { rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_OfflineWallet_sign(it, psbt.lower() , status) + _UniFFILib.INSTANCE.bdk_1945_OfflineWallet_sign(it, psbt.lower() , status) } } + override fun getTransactions(): List = + callWithPointer { + rustCallWithError(BdkException) { status -> + _UniFFILib.INSTANCE.bdk_1945_OfflineWallet_get_transactions(it, status) +} + }.let { + liftSequenceTypeConfirmedTransaction(it) + } + companion object { @@ -1408,6 +1558,7 @@ public interface OnlineWalletInterface { fun getNewAddress(): String fun getBalance(): ULong fun sign(psbt: PartiallySignedBitcoinTransaction ) + fun getTransactions(): List fun getNetwork(): Network fun sync(progressUpdate: BdkProgress, maxAddressParam: UInt? ) fun broadcast(psbt: PartiallySignedBitcoinTransaction ): String @@ -1421,7 +1572,7 @@ class OnlineWallet( constructor(descriptor: String, network: Network, databaseConfig: DatabaseConfig, blockchainConfig: BlockchainConfig ) : this( rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_OnlineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower(), blockchainConfig.lower() ,status) + _UniFFILib.INSTANCE.bdk_1945_OnlineWallet_new(descriptor.lower(), network.lower(), databaseConfig.lower(), blockchainConfig.lower() ,status) }) /** @@ -1434,7 +1585,7 @@ class OnlineWallet( */ override protected fun freeRustArcPtr() { rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_70ef_OnlineWallet_object_free(this.pointer, status) + _UniFFILib.INSTANCE.ffi_bdk_1945_OnlineWallet_object_free(this.pointer, status) } } @@ -1449,7 +1600,7 @@ class OnlineWallet( override fun getNewAddress(): String = callWithPointer { rustCall() { status -> - _UniFFILib.INSTANCE.bdk_70ef_OnlineWallet_get_new_address(it, status) + _UniFFILib.INSTANCE.bdk_1945_OnlineWallet_get_new_address(it, status) } }.let { String.lift(it) @@ -1458,7 +1609,7 @@ class OnlineWallet( override fun getBalance(): ULong = callWithPointer { rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_OnlineWallet_get_balance(it, status) + _UniFFILib.INSTANCE.bdk_1945_OnlineWallet_get_balance(it, status) } }.let { ULong.lift(it) @@ -1467,14 +1618,23 @@ class OnlineWallet( override fun sign(psbt: PartiallySignedBitcoinTransaction ) = callWithPointer { rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_OnlineWallet_sign(it, psbt.lower() , status) + _UniFFILib.INSTANCE.bdk_1945_OnlineWallet_sign(it, psbt.lower() , status) } } + override fun getTransactions(): List = + callWithPointer { + rustCallWithError(BdkException) { status -> + _UniFFILib.INSTANCE.bdk_1945_OnlineWallet_get_transactions(it, status) +} + }.let { + liftSequenceTypeConfirmedTransaction(it) + } + override fun getNetwork(): Network = callWithPointer { rustCall() { status -> - _UniFFILib.INSTANCE.bdk_70ef_OnlineWallet_get_network(it, status) + _UniFFILib.INSTANCE.bdk_1945_OnlineWallet_get_network(it, status) } }.let { Network.lift(it) @@ -1483,14 +1643,14 @@ class OnlineWallet( override fun sync(progressUpdate: BdkProgress, maxAddressParam: UInt? ) = callWithPointer { rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_OnlineWallet_sync(it, CallbackInterfaceBdkProgressInternals.lower(progressUpdate), lowerOptionalu32(maxAddressParam) , status) + _UniFFILib.INSTANCE.bdk_1945_OnlineWallet_sync(it, CallbackInterfaceBdkProgressInternals.lower(progressUpdate), lowerOptionalu32(maxAddressParam) , status) } } override fun broadcast(psbt: PartiallySignedBitcoinTransaction ): String = callWithPointer { rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_OnlineWallet_broadcast(it, psbt.lower() , status) + _UniFFILib.INSTANCE.bdk_1945_OnlineWallet_broadcast(it, psbt.lower() , status) } }.let { String.lift(it) @@ -1525,7 +1685,7 @@ class PartiallySignedBitcoinTransaction( constructor(wallet: OnlineWallet, recipient: String, amount: ULong ) : this( rustCallWithError(BdkException) { status -> - _UniFFILib.INSTANCE.bdk_70ef_PartiallySignedBitcoinTransaction_new(wallet.lower(), recipient.lower(), amount.lower() ,status) + _UniFFILib.INSTANCE.bdk_1945_PartiallySignedBitcoinTransaction_new(wallet.lower(), recipient.lower(), amount.lower() ,status) }) /** @@ -1538,7 +1698,7 @@ class PartiallySignedBitcoinTransaction( */ override protected fun freeRustArcPtr() { rustCall() { status -> - _UniFFILib.INSTANCE.ffi_bdk_70ef_PartiallySignedBitcoinTransaction_object_free(this.pointer, status) + _UniFFILib.INSTANCE.ffi_bdk_1945_PartiallySignedBitcoinTransaction_object_free(this.pointer, status) } } @@ -1616,7 +1776,7 @@ internal object CallbackInterfaceBdkProgressInternals: CallbackInternals - lib.ffi_bdk_70ef_BdkProgress_init_callback(this.foreignCallback, status) + lib.ffi_bdk_1945_BdkProgress_init_callback(this.foreignCallback, status) } } } diff --git a/src/bdk.udl b/src/bdk.udl index 9f386de..138950c 100644 --- a/src/bdk.udl +++ b/src/bdk.udl @@ -63,6 +63,15 @@ interface DatabaseConfig { Sled(SledDbConfiguration config); }; +dictionary ConfirmedTransaction { + u64? fees; + u32 height; + u64 timestamp; + u64 received; + u64 sent; + string id; +}; + interface OfflineWallet { [Throws=BdkError] constructor(string descriptor, Network network, DatabaseConfig database_config); @@ -73,6 +82,8 @@ interface OfflineWallet { u64 get_balance(); [Throws=BdkError] void sign([ByRef] PartiallySignedBitcoinTransaction psbt); + [Throws=BdkError] + sequence get_transactions(); }; dictionary ElectrumConfig { @@ -111,6 +122,8 @@ interface OnlineWallet { u64 get_balance(); [Throws=BdkError] void sign([ByRef] PartiallySignedBitcoinTransaction psbt); + [Throws=BdkError] + sequence get_transactions(); // OnlineWalletInterface Network get_network(); diff --git a/src/lib.rs b/src/lib.rs index d9d375f..2972e14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,16 @@ impl WalletHolder<()> for OfflineWallet { } } +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct ConfirmedTransaction { + pub fees: Option, + pub height: u32, + pub timestamp: u64, + pub received: u64, + pub sent: u64, + pub id: String, +} + trait OfflineWalletOperations: WalletHolder { fn get_new_address(&self) -> String { self.get_wallet() @@ -81,6 +91,22 @@ trait OfflineWalletOperations: WalletHolder { ))), } } + + 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, + }) + .collect()) + } } impl OfflineWallet {