From 77b1bfaf2c3089c6941674e211a3cba20f442aa7 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 22 Sep 2026 20:54:34 +0200 Subject: [PATCH 1/3] fix: show the amount that leaves on spending confirm From 4f679264d40c76b720352bd4ed76b396b88187a6 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Tue, 22 Sep 2026 21:20:25 +0200 Subject: [PATCH 2/3] fix: show the amount that leaves on spending confirm --- Bitkit/Utilities/SpendingConfirmTotal.swift | 18 +++++++ Bitkit/Views/Transfer/SpendingConfirm.swift | 7 ++- BitkitTests/SpendingConfirmTotalTests.swift | 53 +++++++++++++++++++++ changelog.d/next/772.fixed.md | 1 + 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 Bitkit/Utilities/SpendingConfirmTotal.swift create mode 100644 BitkitTests/SpendingConfirmTotalTests.swift create mode 100644 changelog.d/next/772.fixed.md diff --git a/Bitkit/Utilities/SpendingConfirmTotal.swift b/Bitkit/Utilities/SpendingConfirmTotal.swift new file mode 100644 index 000000000..7360d9b89 --- /dev/null +++ b/Bitkit/Utilities/SpendingConfirmTotal.swift @@ -0,0 +1,18 @@ +import Foundation + +/// Confirm total for the spending transfer screen: the amount of bitcoin that leaves the wallet. +enum SpendingConfirmTotal { + /// When send-all funds the order, the sweep spends `maxSendable + networkFee` (the spendable balance). + /// Otherwise the total is the order fee plus the miner fee. + static func leavingAmount( + orderFeeSat: UInt64, + networkFeeSat: UInt64, + shouldUseSendAll: Bool, + maxSendable: UInt64? + ) -> UInt64 { + if shouldUseSendAll, let maxSendable { + return maxSendable + networkFeeSat + } + return orderFeeSat + networkFeeSat + } +} diff --git a/Bitkit/Views/Transfer/SpendingConfirm.swift b/Bitkit/Views/Transfer/SpendingConfirm.swift index 77cd8ce93..2a86b6af9 100644 --- a/Bitkit/Views/Transfer/SpendingConfirm.swift +++ b/Bitkit/Views/Transfer/SpendingConfirm.swift @@ -26,7 +26,12 @@ struct SpendingConfirm: View { } var total: UInt64 { - transfer.uiState.feeSat + transactionFee + SpendingConfirmTotal.leavingAmount( + orderFeeSat: transfer.uiState.feeSat, + networkFeeSat: transactionFee, + shouldUseSendAll: shouldUseSendAll, + maxSendable: maxSendableAmount + ) } var body: some View { diff --git a/BitkitTests/SpendingConfirmTotalTests.swift b/BitkitTests/SpendingConfirmTotalTests.swift new file mode 100644 index 000000000..f682beba6 --- /dev/null +++ b/BitkitTests/SpendingConfirmTotalTests.swift @@ -0,0 +1,53 @@ +@testable import Bitkit +import XCTest + +final class SpendingConfirmTotalTests: XCTestCase { + func testNormalPath_UsesOrderFeePlusNetworkFee() { + XCTAssertEqual( + SpendingConfirmTotal.leavingAmount( + orderFeeSat: 50_000, + networkFeeSat: 250, + shouldUseSendAll: false, + maxSendable: nil + ), + 50_250 + ) + } + + func testNormalPath_IgnoresStaleMaxSendable() { + XCTAssertEqual( + SpendingConfirmTotal.leavingAmount( + orderFeeSat: 50_000, + networkFeeSat: 250, + shouldUseSendAll: false, + maxSendable: 99_750 + ), + 50_250 + ) + } + + func testSendAllPath_UsesMaxSendablePlusNetworkFee() { + // balance 100_000, send-all fee 250 → maxSendable 99_750; amount leaving = 100_000 + XCTAssertEqual( + SpendingConfirmTotal.leavingAmount( + orderFeeSat: 50_000, + networkFeeSat: 250, + shouldUseSendAll: true, + maxSendable: 99_750 + ), + 100_000 + ) + } + + func testSendAllPath_WithoutMaxSendable_FallsBackToOrderPlusFee() { + XCTAssertEqual( + SpendingConfirmTotal.leavingAmount( + orderFeeSat: 50_000, + networkFeeSat: 250, + shouldUseSendAll: true, + maxSendable: nil + ), + 50_250 + ) + } +} diff --git a/changelog.d/next/772.fixed.md b/changelog.d/next/772.fixed.md new file mode 100644 index 000000000..2ff3b15a0 --- /dev/null +++ b/changelog.d/next/772.fixed.md @@ -0,0 +1 @@ +Spending confirm now shows the amount of bitcoin that leaves the wallet when a transfer uses send-all funding. From b7c5ba8706773dab2f37569bab2371202b2777c0 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 23 Sep 2026 21:03:08 +0200 Subject: [PATCH 3/3] fix: record the send-all amount that leaves --- Bitkit/Utilities/SpendingConfirmTotal.swift | 4 ++-- Bitkit/ViewModels/TransferViewModel.swift | 7 +++++- BitkitTests/SpendingConfirmTotalTests.swift | 24 +++++++++++++++++++++ changelog.d/next/772.fixed.md | 2 +- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Bitkit/Utilities/SpendingConfirmTotal.swift b/Bitkit/Utilities/SpendingConfirmTotal.swift index 7360d9b89..9e2f809b7 100644 --- a/Bitkit/Utilities/SpendingConfirmTotal.swift +++ b/Bitkit/Utilities/SpendingConfirmTotal.swift @@ -11,8 +11,8 @@ enum SpendingConfirmTotal { maxSendable: UInt64? ) -> UInt64 { if shouldUseSendAll, let maxSendable { - return maxSendable + networkFeeSat + return maxSendable.saturatingAdd(networkFeeSat) } - return orderFeeSat + networkFeeSat + return orderFeeSat.saturatingAdd(networkFeeSat) } } diff --git a/Bitkit/ViewModels/TransferViewModel.swift b/Bitkit/ViewModels/TransferViewModel.swift index 9179d6659..af5caa89d 100644 --- a/Bitkit/ViewModels/TransferViewModel.swift +++ b/Bitkit/ViewModels/TransferViewModel.swift @@ -399,7 +399,12 @@ class TransferViewModel: ObservableObject { isMaxAmount: isMaxAmount ) - let txTotalSats = order.feeSat + txFee + let txTotalSats = SpendingConfirmTotal.leavingAmount( + orderFeeSat: order.feeSat, + networkFeeSat: txFee, + shouldUseSendAll: isMaxAmount, + maxSendable: maxSendableAmount + ) // Pre-activity metadata lets the LDK activity sync recognize this send as a transfer. let currentTime = UInt64(Date().timeIntervalSince1970) diff --git a/BitkitTests/SpendingConfirmTotalTests.swift b/BitkitTests/SpendingConfirmTotalTests.swift index f682beba6..f50165874 100644 --- a/BitkitTests/SpendingConfirmTotalTests.swift +++ b/BitkitTests/SpendingConfirmTotalTests.swift @@ -39,6 +39,30 @@ final class SpendingConfirmTotalTests: XCTestCase { ) } + func testNormalPath_SaturatesOnOverflow() { + XCTAssertEqual( + SpendingConfirmTotal.leavingAmount( + orderFeeSat: UInt64.max, + networkFeeSat: 1, + shouldUseSendAll: false, + maxSendable: nil + ), + UInt64.max + ) + } + + func testSendAllPath_SaturatesOnOverflow() { + XCTAssertEqual( + SpendingConfirmTotal.leavingAmount( + orderFeeSat: 1, + networkFeeSat: 1, + shouldUseSendAll: true, + maxSendable: UInt64.max + ), + UInt64.max + ) + } + func testSendAllPath_WithoutMaxSendable_FallsBackToOrderPlusFee() { XCTAssertEqual( SpendingConfirmTotal.leavingAmount( diff --git a/changelog.d/next/772.fixed.md b/changelog.d/next/772.fixed.md index 2ff3b15a0..5e7da60bd 100644 --- a/changelog.d/next/772.fixed.md +++ b/changelog.d/next/772.fixed.md @@ -1 +1 @@ -Spending confirm now shows the amount of bitcoin that leaves the wallet when a transfer uses send-all funding. +Spending confirm now shows the amount of bitcoin that leaves the wallet when a transfer uses send-all funding, and the transfer record uses that same amount.