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
4 changes: 4 additions & 0 deletions phoenix-ios/phoenix-ios.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@
DC9B8EE225D72CC200E13818 /* ForceCloseChannelsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9B8EE125D72CC200E13818 /* ForceCloseChannelsView.swift */; };
DC9CF83D2D2C6D37003F3B0F /* ScrollView_18.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9CF83C2D2C6D31003F3B0F /* ScrollView_18.swift */; };
DC9CF83F2D2DC3F3003F3B0F /* GeometryGroup_17.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9CF83E2D2DC3ED003F3B0F /* GeometryGroup_17.swift */; };
DC9CF8412D2ECF08003F3B0F /* IncomingBalancePopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9CF8402D2ECEF7003F3B0F /* IncomingBalancePopover.swift */; };
DC9E7EC32A12955300A5F1D0 /* LiquidityHTML.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9E7EC22A12955300A5F1D0 /* LiquidityHTML.swift */; };
DC9E7EC62A1295B100A5F1D0 /* liquidity.html in Resources */ = {isa = PBXBuildFile; fileRef = DC9E7EC82A1295B100A5F1D0 /* liquidity.html */; };
DCA02B9D2BD065BF0080520F /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = DCA02B9C2BD065BF0080520F /* PrivacyInfo.xcprivacy */; };
Expand Down Expand Up @@ -640,6 +641,7 @@
DC9B8EE125D72CC200E13818 /* ForceCloseChannelsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForceCloseChannelsView.swift; sourceTree = "<group>"; };
DC9CF83C2D2C6D31003F3B0F /* ScrollView_18.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollView_18.swift; sourceTree = "<group>"; };
DC9CF83E2D2DC3ED003F3B0F /* GeometryGroup_17.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometryGroup_17.swift; sourceTree = "<group>"; };
DC9CF8402D2ECEF7003F3B0F /* IncomingBalancePopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingBalancePopover.swift; sourceTree = "<group>"; };
DC9E7EC22A12955300A5F1D0 /* LiquidityHTML.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiquidityHTML.swift; sourceTree = "<group>"; };
DC9E7EC72A1295B100A5F1D0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = Base; path = Base.lproj/liquidity.html; sourceTree = "<group>"; };
DCA02B9C2BD065BF0080520F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1522,6 +1524,7 @@
DCDD9ECF286377B7001800A3 /* MainView_Big.swift */,
DC33C5622A7C15D40053D785 /* MainView_BigPrimary.swift */,
53BEFE171C182513A5762686 /* HomeView.swift */,
DC9CF8402D2ECEF7003F3B0F /* IncomingBalancePopover.swift */,
DCDD9ED528637FD7001800A3 /* AppStatusButton.swift */,
DCDD9ED328637EBB001800A3 /* ToolsButton.swift */,
DC4864D929D4E52C00ACD539 /* BgRefreshDisabledPopover.swift */,
Expand Down Expand Up @@ -2047,6 +2050,7 @@
DCDD9ECE28637474001800A3 /* Orientation.swift in Sources */,
DCCFE6B02B64326F002FFF11 /* OSLogHandler.swift in Sources */,
DCDD9ECB28637242001800A3 /* MainView.swift in Sources */,
DC9CF8412D2ECF08003F3B0F /* IncomingBalancePopover.swift in Sources */,
DCDD9ED2286377C5001800A3 /* MainView_Small.swift in Sources */,
C8D7AFF5BC5754DBBEEB2688 /* ElectrumConfigurationView.swift in Sources */,
53BEFBECABE13063AB28A4D6 /* publishers.swift in Sources */,
Expand Down
40 changes: 40 additions & 0 deletions phoenix-ios/phoenix-ios/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -7432,6 +7432,9 @@
}
}
}
},
"Attention! Some funds will expire soon and won't be eligible for a swap anymore." : {

},
"Authenticate" : {
"comment" : "lnurl-auth: login button title",
Expand Down Expand Up @@ -9663,6 +9666,9 @@
}
}
}
},
"Cannot be swapped anymore, after 4 months waiting. These funds must be spent manually." : {

},
"Cannot create commit tx" : {
"localizations" : {
Expand Down Expand Up @@ -12101,6 +12107,9 @@
}
}
}
},
"Confirming: " : {

},
"Connected" : {
"comment" : "Connection state",
Expand Down Expand Up @@ -17929,6 +17938,9 @@
}
}
}
},
"Expired: " : {

},
"Explore" : {
"localizations" : {
Expand Down Expand Up @@ -19391,6 +19403,9 @@
}
}
}
},
"Final wallet: " : {

},
"Fix It" : {
"localizations" : {
Expand Down Expand Up @@ -41199,6 +41214,9 @@
}
}
}
},
"These funds come from closed Lightning channels. They must be spent manually." : {

},
"These funds were not swapped in time. Tap to spend." : {
"localizations" : {
Expand Down Expand Up @@ -45523,6 +45541,9 @@
}
}
}
},
"Waiting for confirmation first before they can be swapped to Lightning." : {

},
"waiting for confirmations" : {
"comment" : "explanation for pending transaction",
Expand Down Expand Up @@ -45885,6 +45906,9 @@
}
}
}
},
"Waiting for swap: " : {

},
"Waiting to start…" : {
"localizations" : {
Expand Down Expand Up @@ -46807,6 +46831,19 @@
}
}
}
},
"Will automatically be swapped to Lightning if the fee is **less than %@** (%@) of the amount." : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Will automatically be swapped to Lightning if the fee is **less than %1$@** (%2$@) of the amount."
}
}
}
},
"Will automatically be swapped to Lightning if the fee is **less than %@**." : {

},
"will be sent to:" : {
"localizations" : {
Expand Down Expand Up @@ -46968,6 +47005,9 @@
}
}
}
},
"Will remain on-chain because automated channels management is disabled." : {

},
"With configured proportional fee, we recommend a base fee of at least %@." : {
"extractionState" : "manual",
Expand Down
11 changes: 11 additions & 0 deletions phoenix-ios/phoenix-ios/kotlin/KotlinExtensions+Bitcoin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ import PhoenixShared
import CryptoKit


extension Bitcoin_kmpSatoshi {

func toMilliSatoshi() -> Lightning_kmpMilliSatoshi {
return Lightning_kmpMilliSatoshi(sat: self)
}

func toMsat() -> Int64 {
return self.toLong() * Utils.Millisatoshis_Per_Satoshi
}
}

extension Bitcoin_kmpTxId {

func toHex() -> String {
Expand Down
22 changes: 3 additions & 19 deletions phoenix-ios/phoenix-ios/kotlin/KotlinExtensions+Lightning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,8 @@ extension Lightning_kmpWalletState.WalletWithConfirmations {
}

var totalBalance: Bitcoin_kmpSatoshi {
let allTx = unconfirmed + weaklyConfirmed + deeplyConfirmed
let balance = allTx.map { $0.amount.toLong() }.sum()
return Bitcoin_kmpSatoshi(sat: balance)
}

/// The `deeplyConfirmed` property contains UTXO's that are also represented in
/// `lockedUntilRefund` & `readyForRefund`. This property is a subset of
/// `deeplyConfirmed` that excludes those 2 categories.
///
var readyForSwap: [Lightning_kmpWalletState.Utxo] {
let timedOut = Set(self.lockedUntilRefund + self.readyForRefund)
return deeplyConfirmed.filter {
!timedOut.contains($0)
}
}

var readyForSwapBalance: Bitcoin_kmpSatoshi {
let balance = readyForSwap.map { $0.amount.toLong() }.sum()
// all: unconfirmed + weaklyConfirmed + deeplyConfirmed + lockedUntilRefund + readyForRefund
let balance = all.map { $0.amount.toLong() }.sum()
return Bitcoin_kmpSatoshi(sat: balance)
}

Expand All @@ -103,7 +87,7 @@ extension Lightning_kmpWalletState.WalletWithConfirmations {
func expirationWarningInDays() -> Int? {

let maxConfirmations = swapInParams.maxConfirmations
let remainingConfirmationsList = readyForSwap.map {
let remainingConfirmationsList = deeplyConfirmed.map {
maxConfirmations - confirmations(utxo: $0)
}

Expand Down
29 changes: 15 additions & 14 deletions phoenix-ios/phoenix-ios/utils/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ enum MsatsPolicy {

class Utils {

public static let Millisatoshis_Per_Satoshi = 1_000.0
public static let Millisatoshis_Per_Bit = 100_000.0
public static let Millisatoshis_Per_Millibitcoin = 100_000_000.0
public static let Millisatoshis_Per_Bitcoin = 100_000_000_000.0
public static let Millisatoshis_Per_Satoshi : Int64 = 1_000
public static let Millisatoshis_Per_Bit : Int64 = 100_000
public static let Millisatoshis_Per_Millibitcoin : Int64 = 100_000_000
public static let Millisatoshis_Per_Bitcoin : Int64 = 100_000_000_000

// --------------------------------------------------
// MARK: Conversion
Expand Down Expand Up @@ -43,7 +43,7 @@ class Utils {
/// Converts from satoshi to millisatoshi
///
static func toMsat(sat: Int64) -> Int64 {
return sat * Int64(Millisatoshis_Per_Satoshi)
return sat * Millisatoshis_Per_Satoshi
}

/// Converts to millisatoshi, the preferred unit for performing conversions.
Expand All @@ -52,10 +52,10 @@ class Utils {

var msat: Double
switch bitcoinUnit {
case .sat : msat = amount * Millisatoshis_Per_Satoshi
case .bit : msat = amount * Millisatoshis_Per_Bit
case .mbtc : msat = amount * Millisatoshis_Per_Millibitcoin
default/*.bitcoin*/: msat = amount * Millisatoshis_Per_Bitcoin
case .sat : msat = amount * Double(Millisatoshis_Per_Satoshi)
case .bit : msat = amount * Double(Millisatoshis_Per_Bit)
case .mbtc : msat = amount * Double(Millisatoshis_Per_Millibitcoin)
default/*.bitcoin*/: msat = amount * Double(Millisatoshis_Per_Bitcoin)
}

if let result = Int64(exactly: msat.rounded(.toNearestOrAwayFromZero)) {
Expand All @@ -80,10 +80,11 @@ class Utils {
static func convertBitcoin(msat: Int64, to bitcoinUnit: BitcoinUnit) -> Double {

switch bitcoinUnit {
case .sat : return Double(msat) / Millisatoshis_Per_Satoshi
case .bit : return Double(msat) / Millisatoshis_Per_Bit
case .mbtc : return Double(msat) / Millisatoshis_Per_Millibitcoin
default/*.bitcoin*/: return Double(msat) / Millisatoshis_Per_Bitcoin
case .sat : return Double(msat) / Double(Millisatoshis_Per_Satoshi)
case .bit : return Double(msat) / Double(Millisatoshis_Per_Bit)
case .mbtc : return Double(msat) / Double(Millisatoshis_Per_Millibitcoin)
case .btc : return Double(msat) / Double(Millisatoshis_Per_Bitcoin)
@unknown default : fatalError("Unknown BitcoinUnit")
}
}

Expand All @@ -94,7 +95,7 @@ class Utils {
//
// exchangeRate.price => value of 1.0 BTC in fiat

let btc = Double(msat) / Millisatoshis_Per_Bitcoin
let btc = Double(msat) / Double(Millisatoshis_Per_Bitcoin)
let fiat = btc * exchangeRate.price

return fiat
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ struct ConfigurationList: View {
case .liquiditySettings : newNavLinkTag = .ChannelManagement ; delay *= 1
case .forceCloseChannels : newNavLinkTag = .ForceCloseChannels ; delay *= 1
case .swapInWallet : newNavLinkTag = .WalletInfo ; delay *= 2
case .finalWallet : newNavLinkTag = .WalletInfo ; delay *= 2
}

if let newNavLinkTag {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ struct SwapInWalletDetails: View {

let absoluteMax: Int64 = liquidityPolicy.effectiveMaxFeeSats

let swapInBalance: Int64 = swapInWallet.totalBalance.sat
let swapInBalance: Int64 = swapInWallet.unconfirmedBalance.sat + swapInWallet.anyConfirmedBalance.sat
if swapInBalance > 0 {

let maxPercent: Double = Double(liquidityPolicy.effectiveMaxFeeBasisPoints) / Double(10_000)
Expand Down Expand Up @@ -475,7 +475,7 @@ struct SwapInWalletDetails: View {

func confirmedBalance() -> (FormattedAmount, FormattedAmount) {

let sats = swapInWallet.readyForSwapBalance
let sats = swapInWallet.deeplyConfirmedBalance
return formattedBalances(sats)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ struct WalletInfoView: View {
case .liquiditySettings : break
case .forceCloseChannels : break
case .swapInWallet : newNavLinkTag = NavLinkTag.SwapInWalletDetails
case .finalWallet : newNavLinkTag = NavLinkTag.FinalWalletDetails
}

if let newNavLinkTag {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ struct PaymentOptionsList: View {
case .liquiditySettings : break
case .forceCloseChannels : break
case .swapInWallet : break
case .finalWallet : break
}

if let newNavLinkTag {
Expand Down
43 changes: 23 additions & 20 deletions phoenix-ios/phoenix-ios/views/environment/DeepLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ enum DeepLink: String, Equatable {
case liquiditySettings
case forceCloseChannels
case swapInWallet
case finalWallet
}

class DeepLinkManager: ObservableObject {
Expand All @@ -41,26 +42,28 @@ class DeepLinkManager: ObservableObject {
}
}

// In iOS 16, Apple deprecated NavigationLink(destination:tag:selection:label:).
//
// The sugggested replacement is to use NavigationLink(value:label:) paired
// with navigationDestination(for:destination:).
// However that solution was only half-baked when Apple released it, and it's riddled with bugs:
// https://github.com/ACINQ/phoenix/pull/333
//
// The deprecated solution still works for now, but has issues.
// One of which is that manual navigation is fragile.
// Manually setting the `navLinkTag` will often highlight the NavigationLink,
// but won't successfully trigger a navigation.
// If you keep trying (by causing a view refresh) it will work properly.
//
// (This is related to manual navigation via a DeepLink)

for idx in 1...50 {
DispatchQueue.main.asyncAfter(deadline: .now() + (Double(idx) * 0.100)) {
self.iOS16Workaround = UUID()
}
}
if #unavailable(iOS 17.0) {
// In iOS 16, Apple deprecated NavigationLink(destination:tag:selection:label:).
//
// The sugggested replacement is to use NavigationLink(value:label:) paired
// with navigationDestination(for:destination:).
// However that solution was only half-baked when Apple released it, and it's riddled with bugs:
// https://github.com/ACINQ/phoenix/pull/333
//
// The deprecated solution still works for now, but has issues.
// One of which is that manual navigation is fragile.
// Manually setting the `navLinkTag` will often highlight the NavigationLink,
// but won't successfully trigger a navigation.
// If you keep trying (by causing a view refresh) it will work properly.
//
// (This is related to manual navigation via a DeepLink)

for idx in 1...50 {
DispatchQueue.main.asyncAfter(deadline: .now() + (Double(idx) * 0.100)) {
self.iOS16Workaround = UUID()
}
}
}
}

func unbroadcast(_ value: DeepLink) {
Expand Down
Loading