From c3c48c1f1278cf8a5e8820a70db609b7022ced6e Mon Sep 17 00:00:00 2001 From: Borja Castellano Date: Fri, 13 Feb 2026 15:12:02 -0600 Subject: [PATCH] send transaction implemented in swift example app --- .../Core/Wallet/CoreWalletManager.swift | 9 +++ .../SwiftDashSDK/KeyWallet/Transaction.swift | 16 ++--- .../SwiftDashSDK/KeyWallet/Wallet.swift | 2 +- .../KeyWallet/WalletManager.swift | 58 +++++++++++++++++++ .../Core/ViewModels/SendViewModel.swift | 16 ++++- .../Core/Views/CreateWalletView.swift | 2 +- 6 files changed, 88 insertions(+), 15 deletions(-) diff --git a/packages/swift-sdk/Sources/SwiftDashSDK/Core/Wallet/CoreWalletManager.swift b/packages/swift-sdk/Sources/SwiftDashSDK/Core/Wallet/CoreWalletManager.swift index 7638b7f439c..a51ee7dab6c 100644 --- a/packages/swift-sdk/Sources/SwiftDashSDK/Core/Wallet/CoreWalletManager.swift +++ b/packages/swift-sdk/Sources/SwiftDashSDK/Core/Wallet/CoreWalletManager.swift @@ -304,6 +304,15 @@ public class CoreWalletManager: ObservableObject { // MARK: - Account Management + /// Build a signed transaction + /// - Parameters: + /// - accountIndex: The account index to use + /// - outputs: The transaction outputs + /// - Returns: The signed transaction bytes + public func buildSignedTransaction(for wallet: HDWallet, accIndex: UInt32, outputs: [Transaction.Output]) throws -> (Data, UInt64) { + try sdkWalletManager.buildSignedTransaction(for: wallet, accIndex: accIndex, outputs: outputs) + } + /// Get transactions for a wallet /// - Parameters: /// - wallet: The wallet to get transactions for diff --git a/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/Transaction.swift b/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/Transaction.swift index be4aa9824c3..e7d8f7b3875 100644 --- a/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/Transaction.swift +++ b/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/Transaction.swift @@ -1,14 +1,6 @@ import Foundation import DashSDKFFI -/// Result of building and signing a transaction -public struct BuildAndSignResult: Sendable { - /// The signed transaction bytes - public let transactionData: Data - /// The fee paid in duffs - public let fee: UInt64 -} - /// Transaction utilities for wallet operations public class Transaction { @@ -23,9 +15,11 @@ public class Transaction { } func toFFI() -> FFITxOutput { - return address.withCString { addressCStr in - FFITxOutput(address: addressCStr, amount: amount) - } + // TODO: This memory is not being freed, FFI must free FFITxOutput + // or expose a method to do it + let cString = strdup(address) + + return FFITxOutput(address: cString, amount: amount) } } diff --git a/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/Wallet.swift b/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/Wallet.swift index 9be6cfd0c25..c8a9e79c78f 100644 --- a/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/Wallet.swift +++ b/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/Wallet.swift @@ -406,7 +406,7 @@ public class Wallet { return count } - + // MARK: - Key Derivation /// Get the extended public key for an account diff --git a/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/WalletManager.swift b/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/WalletManager.swift index ab34303e23b..a1407da6040 100644 --- a/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/WalletManager.swift +++ b/packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/WalletManager.swift @@ -387,6 +387,64 @@ public class WalletManager { return success } + /// Build a signed transaction + /// - Parameters: + /// - accIndex: The account index to use + /// - outputs: The transaction outputs + /// - Returns: The signed transaction bytes and the fee + public func buildSignedTransaction(for wallet: HDWallet, accIndex: UInt32, outputs: [Transaction.Output]) throws -> (Data, UInt64) { + guard !outputs.isEmpty else { + throw KeyWalletError.invalidInput("Transaction must have at least one output") + } + + var error = FFIError() + var txBytesPtr: UnsafeMutablePointer? + var txLen: size_t = 0 + + var fee: UInt64 = 0 + + guard let wallet = try self.getWallet(id: wallet.walletId) else { + throw KeyWalletError.walletError("Wallet not found in manager") + } + + let ffiOutputs = outputs.map { $0.toFFI() } + + let success = ffiOutputs.withUnsafeBufferPointer { outputsPtr in + wallet_build_and_sign_transaction( + self.handle, + wallet.ffiHandle, + accIndex, + outputsPtr.baseAddress, + outputs.count, + 1000, + &fee, + &txBytesPtr, + &txLen, + &error) + } + + defer { + if error.message != nil { + error_message_free(error.message) + } + for _ in ffiOutputs { + // TODO: Memory leak, FFI doesnt expose a way to free the address + } + if let ptr = txBytesPtr { + transaction_bytes_free(ptr) + } + } + + guard success, let ptr = txBytesPtr else { + throw KeyWalletError(ffiError: error) + } + + // Copy the transaction data before freeing + let txData = Data(bytes: ptr, count: txLen) + + return (txData, fee) + } + // MARK: - Block Height Management /// Get the current block height for a network diff --git a/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/ViewModels/SendViewModel.swift b/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/ViewModels/SendViewModel.swift index c2f0f583f7f..cd017dc9085 100644 --- a/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/ViewModels/SendViewModel.swift +++ b/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/ViewModels/SendViewModel.swift @@ -270,8 +270,20 @@ class SendViewModel: ObservableObject { successMessage = "Transfer to Platform complete" case .coreToCore: - // TODO: Implement standard Core → Core transaction - error = "Core to Core transfer not yet implemented" + let outputs = [ + Transaction.Output(address: recipientAddress, amount: amount) + ] + + // TODO: The model is using hardoced estimated fees + let (tx, _) = try walletService.walletManager + .buildSignedTransaction( + for: wallet, + accIndex: 0, + outputs: outputs + ) + + try walletService.broadcastTransaction(tx) + successMessage = "Transfer to Core complete" } // Refresh shielded balance diff --git a/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CreateWalletView.swift b/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CreateWalletView.swift index e1e7479ed42..f763ca8f06d 100644 --- a/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CreateWalletView.swift +++ b/packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CreateWalletView.swift @@ -285,7 +285,7 @@ struct CreateWalletView: View { Task { do { print("=== STARTING WALLET CREATION ===") - + let mnemonic = (showImportOption ? importMnemonic : mnemonic) print("PIN length: \(walletPin.count)") print("Import option enabled: \(showImportOption)")