@@ -7,8 +7,9 @@ class WalletViewModel: ObservableObject {
77 @Published var walletExists : Bool ? = nil
88 @Published var isSyncingWallet = false // Syncing both LN and on chain
99 @AppStorage ( " totalBalanceSats " ) var totalBalanceSats : Int = 0 // Combined onchain and LN
10- @AppStorage ( " totalOnchainSats " ) var totalOnchainSats : Int = 0 // Combined onchain
10+ @AppStorage ( " totalOnchainSats " ) var totalOnchainSats : Int = 0 // The total balance of our on-chain wallet
1111 @AppStorage ( " totalLightningSats " ) var totalLightningSats : Int = 0 // Combined LN
12+ @AppStorage ( " spendableOnchainBalanceSats " ) var spendableOnchainBalanceSats : Int = 0 // The spendable balance of our on-chain wallet
1213
1314 // Receive flow
1415 @AppStorage ( " onchainAddress " ) var onchainAddress = " "
@@ -22,6 +23,7 @@ class WalletViewModel: ObservableObject {
2223 @Published var selectedSpeed : TransactionSpeed = . normal
2324 @Published var selectedUtxos : [ SpendableUtxo ] ?
2425 @Published var availableUtxos : [ SpendableUtxo ] = [ ]
26+ @Published var isMaxAmountSend : Bool = false
2527
2628 // LNURL withdraw flow
2729 @Published var lnurlWithdrawAmount : UInt64 ?
@@ -200,9 +202,10 @@ class WalletViewModel: ObservableObject {
200202 /// - Parameters:
201203 /// - address: The bitcoin address to send to
202204 /// - sats: The amount in satoshis to send
205+ /// - isMaxAmount: Whether this is a max amount send (uses sendAllToAddress)
203206 /// - Returns: The transaction ID (txid) of the sent transaction
204207 /// - Throws: An error if the transaction fails or if fee rates cannot be retrieved
205- func send( address: String , sats: UInt64 ) async throws -> Txid {
208+ func send( address: String , sats: UInt64 , isMaxAmount : Bool = false ) async throws -> Txid {
206209 guard let selectedFeeRateSatsPerVByte else {
207210 throw AppError ( message: " Fee rate not set " , debugMessage: " Please set a fee rate before selecting UTXOs. " )
208211 }
@@ -217,7 +220,8 @@ class WalletViewModel: ObservableObject {
217220 address: address,
218221 sats: sats,
219222 satsPerVbyte: selectedFeeRateSatsPerVByte,
220- utxosToSpend: selectedUtxos
223+ utxosToSpend: selectedUtxos,
224+ isMaxAmount: isMaxAmount
221225 )
222226
223227 Task {
@@ -330,6 +334,34 @@ class WalletViewModel: ObservableObject {
330334 )
331335 }
332336
337+ /// Calculates the maximum sendable amount for onchain transactions
338+ /// - Parameters:
339+ /// - address: The destination address
340+ /// - satsPerVByte: The fee rate in satoshis per virtual byte
341+ /// - Returns: The maximum amount that can be sent (balance minus fees)
342+ /// - Throws: Error if calculation fails
343+ func calculateMaxSendableAmount(
344+ address: String ,
345+ satsPerVByte: UInt32
346+ ) async throws -> UInt64 {
347+ let spendableBalance = UInt64 ( spendableOnchainBalanceSats)
348+
349+ availableUtxos = try await lightningService. listSpendableOutputs ( )
350+
351+ // Use LDK-Node's special handling - when we pass the spendable balance as amount,
352+ // it will automatically calculate the fee for sending all available funds
353+ // if the exact amount would result in insufficient funds due to fees
354+ let fee = try await lightningService. calculateTotalFee (
355+ address: address,
356+ amountSats: spendableBalance,
357+ satsPerVByte: satsPerVByte,
358+ utxosToSpend: availableUtxos
359+ )
360+
361+ // The max sendable amount is the spendable balance minus the fee
362+ return spendableBalance >= fee ? spendableBalance - fee : 0
363+ }
364+
333365 // NOTE: Let's keep this here for now until we are sure new version below is working
334366 // func send(
335367 // bolt11: String,
@@ -419,6 +451,7 @@ class WalletViewModel: ObservableObject {
419451 totalOnchainSats = Int ( balanceDetails. totalOnchainBalanceSats)
420452 totalLightningSats = Int ( balanceDetails. totalLightningBalanceSats)
421453 totalBalanceSats = Int ( balanceDetails. totalLightningBalanceSats + balanceDetails. totalOnchainBalanceSats)
454+ spendableOnchainBalanceSats = Int ( balanceDetails. spendableOnchainBalanceSats)
422455 }
423456 }
424457
@@ -527,6 +560,7 @@ class WalletViewModel: ObservableObject {
527560 selectedUtxos = nil
528561 availableUtxos = [ ]
529562 selectedSpeed = speed
563+ isMaxAmountSend = false
530564 }
531565
532566 func wipe( ) async throws {
0 commit comments