Skip to content

Commit

Permalink
(ios) Fix several UI issues (#493)
Browse files Browse the repository at this point in the history
- Fixing compiler issues (latest lightning-kmp changes)

- Hide fee bumping button in irrelevant cases (funding 
and closing). Fixes #488.

- Don't allow users to send on-chain transactions with sat/vByte
below minimumFee
  • Loading branch information
robbiehanson authored Dec 19, 2023
1 parent 6633dd4 commit 83d14e7
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 28 deletions.
6 changes: 6 additions & 0 deletions phoenix-ios/phoenix-ios/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -8229,6 +8229,12 @@
}
}
}
},
"Feerate below minimum allowed by mempool: %@" : {

},
"Feerate below minimum allowed by mempool." : {

},
"Fees" : {
"comment" : "Label in SummaryInfoGrid",
Expand Down
50 changes: 34 additions & 16 deletions phoenix-ios/phoenix-ios/views/inspect/CpfpView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ struct MinerFeeCPFP {
}

enum CpfpError: Error {
case feeTooLow
case feeBelowMinimum
case feeNotIncreased
case noChannels
case errorThrown(message: String)
case executeError(problem: SpliceOutProblem)
Expand Down Expand Up @@ -180,7 +181,7 @@ struct CpfpView: View {
minerFeeFormula()
.padding(.bottom)

payButton()
footer()
}
.padding(.horizontal, 40)
}
Expand Down Expand Up @@ -387,7 +388,7 @@ struct CpfpView: View {
}

@ViewBuilder
func payButton() -> some View {
func footer() -> some View {

VStack(alignment: HorizontalAlignment.center, spacing: 10) {

Expand All @@ -412,7 +413,13 @@ struct CpfpView: View {

Group {
switch cpfpError {
case .feeTooLow:
case .feeBelowMinimum:
if let mrr = mempoolRecommendedResponse {
Text("Feerate below minimum allowed by mempool: \(satsPerByteString(mrr.minimumFee))")
} else {
Text("Feerate below minimum allowed by mempool.")
}
case .feeNotIncreased:
Text(
"""
This feerate is below what your transactions are already using. \
Expand Down Expand Up @@ -527,15 +534,19 @@ struct CpfpView: View {
}

let doubleValue = mempoolRecommendedResponse.feeForPriority(priority)

let stringValue = satsPerByteString(doubleValue)

return (doubleValue, stringValue)
}

func satsPerByteString(_ value: Double) -> String {

let nf = NumberFormatter()
nf.numberStyle = .decimal
nf.minimumFractionDigits = 0
nf.maximumFractionDigits = 1

let stringValue = nf.string(from: NSNumber(value: doubleValue)) ?? "?"

return (doubleValue, stringValue)
return nf.string(from: NSNumber(value: value)) ?? "?"
}

func satsPerByteString(_ priority: MinerFeePriority) -> String {
Expand Down Expand Up @@ -619,26 +630,29 @@ struct CpfpView: View {
func satsPerByteChanged() {
log.trace("satsPerByteChanged(): \(satsPerByte)")

minerFeeInfo = nil
guard
let satsPerByte_number = try? parsedSatsPerByte.get(),
let peer = Biz.business.peerManager.peerStateValue()
else {
minerFeeInfo = nil
return
}

if let mrr = mempoolRecommendedResponse, mrr.minimumFee > satsPerByte_number.doubleValue {
cpfpError = .feeBelowMinimum
return
}
cpfpError = nil

let originalSatsPerByte = satsPerByte

let satsPerByte_satoshi = Bitcoin_kmpSatoshi(sat: satsPerByte_number.int64Value)
let effectiveFeerate = Lightning_kmpFeeratePerByte(feerate: satsPerByte_satoshi)
let effectiveFeeratePerKw = Lightning_kmpFeeratePerKw(feeratePerByte: effectiveFeerate)

cpfpError = nil
minerFeeInfo = nil

Task { @MainActor in

var pair: KotlinPair<Lightning_kmpFeeratePerKw, Bitcoin_kmpSatoshi>? = nil
var pair: KotlinPair<Lightning_kmpFeeratePerKw, Lightning_kmpChannelCommand.CommitmentSpliceFees>? = nil
do {
pair = try await peer.estimateFeeForSpliceCpfp(
channelId: onChainPayment.channelId,
Expand All @@ -658,7 +672,8 @@ struct CpfpView: View {
let cpfpFeeratePerKw: Lightning_kmpFeeratePerKw = pair.first!
let cpfpFeerate = Lightning_kmpFeeratePerByte(feeratePerKw: cpfpFeeratePerKw)

let minerFee: Bitcoin_kmpSatoshi = pair.second!
let spliceFees: Lightning_kmpChannelCommand.CommitmentSpliceFees = pair.second!
let minerFee: Bitcoin_kmpSatoshi = spliceFees.miningFee

// From the docs (in lightning-kmp):
//
Expand All @@ -679,8 +694,8 @@ struct CpfpView: View {
minerFee: minerFee
)
} else {
log.error("Error: peer.estimateFeeForSpliceCpfp() => too low")
self.cpfpError = .feeTooLow
log.error("Error: peer.estimateFeeForSpliceCpfp() => fee not increased")
self.cpfpError = .feeNotIncreased
}

} else {
Expand All @@ -696,6 +711,9 @@ struct CpfpView: View {

// The UI will change, so we need to reset the geometry measurements
priorityBoxWidth = nil

// Might need to display an error (if minimumFee increased)
satsPerByteChanged()
}

func executePayment() {
Expand Down
11 changes: 10 additions & 1 deletion phoenix-ios/phoenix-ios/views/inspect/SummaryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ struct SummaryView: View {
}
} // </confirmationDialog>

if confirmations == 0 {
if confirmations == 0 && supportsBumpFee(onChainPayment) {
NavigationLink(destination: cpfpView(onChainPayment)) {
Label {
Text("Accelerate transaction")
Expand Down Expand Up @@ -651,6 +651,15 @@ struct SummaryView: View {
}
}

func supportsBumpFee(_ onChainPayment: Lightning_kmpOnChainOutgoingPayment) -> Bool {

switch onChainPayment {
case is Lightning_kmpSpliceOutgoingPayment : return true
case is Lightning_kmpSpliceCpfpOutgoingPayment : return true
default : return false
}
}

// --------------------------------------------------
// MARK: Tasks
// --------------------------------------------------
Expand Down
48 changes: 37 additions & 11 deletions phoenix-ios/phoenix-ios/views/send/MinerFeeSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct MinerFeeSheet: View {
@Binding var mempoolRecommendedResponse: MempoolRecommendedResponse?

@State var explicitlySelectedPriority: MinerFeePriority? = nil
@State var feeBelowMinimum: Bool = false

@Environment(\.colorScheme) var colorScheme: ColorScheme
@EnvironmentObject var smartModalState: SmartModalState
Expand Down Expand Up @@ -296,16 +297,28 @@ struct MinerFeeSheet: View {
@ViewBuilder
func footer() -> some View {

HStack(alignment: VerticalAlignment.center, spacing: 0) {
Spacer()
VStack(alignment: HorizontalAlignment.center, spacing: 10) {

Button {
reviewTransactionButtonTapped()
} label: {
Text("Review Transaction")
}
.font(.title3)
.disabled(minerFeeInfo == nil)
Spacer()

if feeBelowMinimum {
Group {
if let mrr = mempoolRecommendedResponse {
Text("Feerate below minimum allowed by mempool: \(satsPerByteString(mrr.minimumFee))")
} else {
Text("Feerate below minimum allowed by mempool.")
}
}
.font(.callout)
.foregroundColor(.appNegative)
.multilineTextAlignment(.center)
}
}
.padding()
.padding(.top)
Expand Down Expand Up @@ -348,15 +361,19 @@ struct MinerFeeSheet: View {
}

let doubleValue = mempoolRecommendedResponse.feeForPriority(priority)

let stringValue = satsPerByteString(doubleValue)

return (doubleValue, stringValue)
}

func satsPerByteString(_ value: Double) -> String {

let nf = NumberFormatter()
nf.numberStyle = .decimal
nf.minimumFractionDigits = 0
nf.maximumFractionDigits = 1

let stringValue = nf.string(from: NSNumber(value: doubleValue)) ?? "?"

return (doubleValue, stringValue)
return nf.string(from: NSNumber(value: value)) ?? "?"
}

func satsPerByteString(_ priority: MinerFeePriority) -> String {
Expand Down Expand Up @@ -440,23 +457,29 @@ struct MinerFeeSheet: View {
func satsPerByteChanged() {
log.trace("satsPerByteChanged(): \(satsPerByte)")

minerFeeInfo = nil
guard
let satsPerByte_number = try? parsedSatsPerByte.get(),
let peer = Biz.business.peerManager.peerStateValue(),
let scriptBytes = Parser.shared.addressToPublicKeyScript(chain: Biz.business.chain, address: btcAddress)
else {
minerFeeInfo = nil
return
}

if let mrr = mempoolRecommendedResponse, mrr.minimumFee > satsPerByte_number.doubleValue {
feeBelowMinimum = true
return
} else {
feeBelowMinimum = false
}

let originalSatsPerByte = satsPerByte
let scriptVector = Bitcoin_kmpByteVector(bytes: scriptBytes)

let satsPerByte_satoshi = Bitcoin_kmpSatoshi(sat: satsPerByte_number.int64Value)
let feePerByte = Lightning_kmpFeeratePerByte(feerate: satsPerByte_satoshi)
let feePerKw = Lightning_kmpFeeratePerKw(feeratePerByte: feePerByte)

minerFeeInfo = nil
Task { @MainActor in
do {
let pair = try await peer.estimateFeeForSpliceOut(
Expand All @@ -467,13 +490,13 @@ struct MinerFeeSheet: View {

if let pair = pair,
let updatedFeePerKw: Lightning_kmpFeeratePerKw = pair.first,
let fee: Bitcoin_kmpSatoshi = pair.second
let fees: Lightning_kmpChannelCommand.CommitmentSpliceFees = pair.second
{
if self.satsPerByte == originalSatsPerByte {
self.minerFeeInfo = MinerFeeInfo(
pubKeyScript: scriptVector,
feerate: updatedFeePerKw,
minerFee: fee
minerFee: fees.miningFee
)
}
} else {
Expand All @@ -492,6 +515,9 @@ struct MinerFeeSheet: View {

// The UI will change, so we need to reset the geometry measurements
priorityBoxWidth = nil

// Might need to display an error (if minimumFee increased)
satsPerByteChanged()
}

func closeButtonTapped() {
Expand Down

0 comments on commit 83d14e7

Please sign in to comment.