Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Bitkit/Utilities/SpendingConfirmTotal.swift
Original file line number Diff line number Diff line change
@@ -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 {
Comment thread
ovitrif marked this conversation as resolved.
return maxSendable.saturatingAdd(networkFeeSat)
}
return orderFeeSat.saturatingAdd(networkFeeSat)
}
}
7 changes: 6 additions & 1 deletion Bitkit/ViewModels/TransferViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion Bitkit/Views/Transfer/SpendingConfirm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
77 changes: 77 additions & 0 deletions BitkitTests/SpendingConfirmTotalTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
@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 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(
orderFeeSat: 50_000,
networkFeeSat: 250,
shouldUseSendAll: true,
maxSendable: nil
),
50_250
)
}
}
1 change: 1 addition & 0 deletions changelog.d/next/772.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
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.
Loading