Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
75dc541
feat: support all address types
ben-kaufman Feb 5, 2026
36c600e
Fix legacy type issues
ben-kaufman Feb 11, 2026
32a3ba6
Update ldk-node
ben-kaufman Feb 11, 2026
4cab166
fix balance check on disable
ben-kaufman Feb 11, 2026
82cf9ad
fix timeout issue
ben-kaufman Feb 11, 2026
13b8ab4
fix
ben-kaufman Feb 11, 2026
9ac4142
fix multiple address support for backup and migration
ben-kaufman Feb 11, 2026
bff806b
remove funds sweep for different address types
ben-kaufman Feb 11, 2026
8ca8c88
fixes
ben-kaufman Feb 11, 2026
b85158f
update ldk-node
ben-kaufman Feb 11, 2026
4181b50
update ldk-node
ben-kaufman Feb 11, 2026
6502cb3
update ldk node
ben-kaufman Feb 12, 2026
cd23f7d
fix wipe and update comments
ben-kaufman Feb 12, 2026
184d32d
fixes
ben-kaufman Feb 12, 2026
6f448f0
extract texts
ben-kaufman Feb 12, 2026
dcb9dc6
remove empty wallets from monitor list after restore
ben-kaufman Feb 12, 2026
b0e40c3
add tests
ben-kaufman Feb 12, 2026
125600e
Merge branch 'master' into feat/multiple-addresses-types
ben-kaufman Feb 16, 2026
39b5e5f
fix rebase string updates
ben-kaufman Feb 17, 2026
7ced729
Update AddressViewer.swift
ben-kaufman Feb 17, 2026
37dd99e
review fixes
ben-kaufman Feb 18, 2026
ae742df
Merge branch 'master' into feat/multiple-addresses-types
ben-kaufman Feb 18, 2026
dc1ab2a
fix AddressScriptType ref
ben-kaufman Feb 18, 2026
07eb373
fix: change allAddressTypes from computed var to stored let
github-actions[bot] Feb 18, 2026
faf9eb7
fixes
ben-kaufman Feb 18, 2026
8e59b05
Merge branch 'master' into feat/multiple-addresses-types
ben-kaufman Feb 18, 2026
8956313
Add tests
ben-kaufman Feb 19, 2026
e541654
add retry logic to find payment
ben-kaufman Feb 19, 2026
f5b2c3d
Update unit-tests.yml
ben-kaufman Feb 19, 2026
99dff14
Fix explore screen title
ben-kaufman Feb 19, 2026
7c05c7d
Fix activity not created on send: create immediately from txid, incre…
ben-kaufman Feb 20, 2026
d1ac6b5
Replace node restart with dynamic address type APIs (#447)
ben-kaufman Feb 20, 2026
3de48ec
Merge branch 'master' into feat/multiple-addresses-types
ben-kaufman Feb 20, 2026
2f5ef26
toast test ids
piotr-iohk Feb 20, 2026
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
2 changes: 0 additions & 2 deletions .github/workflows/e2e_migration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ jobs:
- { name: migration_1-restore, setup_type: standard }
- { name: migration_2-migration, setup_type: standard }
- { name: migration_3-with-passphrase, setup_type: passphrase }
- { name: migration_4-with-sweep, setup_type: sweep }
with:
e2e_branch: ${{ needs.e2e-branch.outputs.branch }}
rn_version: ${{ matrix.rn_version }}
Expand All @@ -149,7 +148,6 @@ jobs:
- { name: migration_1-restore, setup_type: standard, grep: "@migration_1" }
- { name: migration_2-migration, setup_type: standard, grep: "@migration_2" }
- { name: migration_3-with-passphrase, setup_type: passphrase, grep: "@migration_3" }
- { name: migration_4-with-sweep, setup_type: sweep, grep: "@migration_4" }

name: e2e-tests - ${{ matrix.rn_version }} - ${{ matrix.scenario.name }}

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ jobs:
-only-testing:BitkitTests/UtxoSelectionTests \
-only-testing:BitkitTests/BlocktankTests \
-only-testing:BitkitTests/PaymentFlowTests \
-only-testing:BitkitTests/AddressTypeIntegrationTests \
| xcbeautify --report junit
}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
-skip-testing:BitkitTests/UtxoSelectionTests \
-skip-testing:BitkitTests/BlocktankTests \
-skip-testing:BitkitTests/PaymentFlowTests \
-skip-testing:BitkitTests/AddressTypeIntegrationTests \
| xcbeautify --report junit
echo "✅ Unit tests completed at $(date)"
Expand Down
8 changes: 5 additions & 3 deletions Bitkit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
membershipExceptions = (
Constants/Env.swift,
Extensions/HexBytes.swift,
"Extensions/LDKNode+AddressType.swift",
Extensions/PaymentDetails.swift,
Models/BlocktankNotificationType.swift,
Models/LnPeer.swift,
Expand Down Expand Up @@ -109,6 +110,7 @@
membershipExceptions = (
Constants/Env.swift,
Extensions/HexBytes.swift,
"Extensions/LDKNode+AddressType.swift",
Extensions/PaymentDetails.swift,
Models/BlocktankNotificationType.swift,
Models/LnPeer.swift,
Expand Down Expand Up @@ -893,7 +895,7 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/synonymdev/vss-rust-client-ffi";
requirement = {
branch = "master";
branch = master;
kind = branch;
};
};
Expand Down Expand Up @@ -925,8 +927,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/synonymdev/ldk-node";
requirement = {
branch = main;
kind = branch;
kind = revision;
revision = 2ccd7aba932d245850d90e7906559025b5be2f43;
};
};
96DEA0382DE8BBA1009932BF /* XCRemoteSwiftPackageReference "bitkit-core" */ = {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions Bitkit/AppScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ struct AppScene: View {
if UserDefaults.standard.bool(forKey: "pinOnLaunch") && settings.pinEnabled {
isPinVerified = false
}
SweepViewModel.checkAndPromptForSweepableFunds(sheets: sheets)

if migrations.needsPostMigrationSync {
app.toast(
type: .warning,
Expand Down
148 changes: 148 additions & 0 deletions Bitkit/Extensions/LDKNode+AddressType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import LDKNode

extension LDKNode.AddressType {
// MARK: - All cases (ordered)

static let allAddressTypes: [LDKNode.AddressType] = [.legacy, .nestedSegwit, .nativeSegwit, .taproot]

/// All address types with `selected` first, remaining in standard order.
static func prioritized(selected: LDKNode.AddressType) -> [LDKNode.AddressType] {
var types = [selected]
for type in allAddressTypes where type != selected {
types.append(type)
}
return types
}

// MARK: - Storage string (UserDefaults / BitkitCore APIs)

/// String value used in UserDefaults and BitkitCore APIs.
var stringValue: String {
switch self {
case .legacy: return "legacy"
case .nestedSegwit: return "nestedSegwit"
case .nativeSegwit: return "nativeSegwit"
case .taproot: return "taproot"
}
}

/// Parses storage string; returns nil for invalid or unknown values.
static func from(string: String) -> LDKNode.AddressType? {
switch string {
case "legacy": return .legacy
case "nestedSegwit": return .nestedSegwit
case "nativeSegwit": return .nativeSegwit
case "taproot": return .taproot
default: return nil
}
}

/// Parses storage string; returns `.nativeSegwit` for nil or invalid (backward compatibility).
static func fromStorage(_ string: String?) -> LDKNode.AddressType {
guard let s = string, let type = from(string: s) else { return .nativeSegwit }
return type
}

/// Parses a comma-separated string of address types; filters invalid values.
static func parseCommaSeparated(_ string: String) -> [LDKNode.AddressType] {
string.split(separator: ",")
.map { String($0).trimmingCharacters(in: .whitespaces) }
.compactMap { from(string: $0) }
}

// MARK: - Derivation path

/// BIP derivation path using current network (Env.network) for coin type.
var derivationPath: String {
let coinType = Env.network == .bitcoin ? "0" : "1"
return derivationPath(coinType: coinType)
}

/// BIP derivation path for the given coin type ("0" mainnet, "1" testnet).
func derivationPath(coinType: String) -> String {
switch self {
case .legacy: return "m/44'/\(coinType)'/0'/0" // BIP 44
case .nestedSegwit: return "m/49'/\(coinType)'/0'/0" // BIP 49
case .nativeSegwit: return "m/84'/\(coinType)'/0'/0" // BIP 84
case .taproot: return "m/86'/\(coinType)'/0'/0" // BIP 86
}
}

// MARK: - Localized display

var localizedTitle: String {
switch self {
case .legacy: return "Legacy"
case .nestedSegwit: return "Nested Segwit"
case .nativeSegwit: return "Native Segwit"
case .taproot: return "Taproot"
}
}

/// Short label for compact UI (e.g. "Native").
var shortLabel: String {
switch self {
case .legacy: return "Legacy"
case .nestedSegwit: return "Nested"
case .nativeSegwit: return "Native"
case .taproot: return "Taproot"
}
}
Comment on lines +82 to +90
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer to only use the full name for the different address types. Never heard them being abbreviated like this. Doesn't seem to break the UI in the address viewer as far as I can see.


var localizedDescription: String {
switch self {
case .legacy: return "Pay-to-public-key-hash (1x...)"
case .nestedSegwit: return "Pay-to-Script-Hash (3x...)"
case .nativeSegwit: return "Pay-to-witness-public-key-hash (bc1x...)"
case .taproot: return "Pay-to-Taproot (bc1px...)"
}
}

var example: String {
switch self {
case .legacy: return "(1x...)"
case .nestedSegwit: return "(3x...)"
case .nativeSegwit: return "(bc1x...)"
case .taproot: return "(bc1px...)"
}
}

var shortExample: String {
switch self {
case .legacy: return "1x..."
case .nestedSegwit: return "3x..."
case .nativeSegwit: return "bc1q..."
case .taproot: return "bc1p..."
}
}

/// Accessibility / UI test identifier.
var testId: String {
switch self {
case .legacy: return "p2pkh"
case .nestedSegwit: return "p2sh-p2wpkh"
case .nativeSegwit: return "p2wpkh"
case .taproot: return "p2tr"
}
}

// MARK: - Address format validation

/// Returns true if the address has the expected prefix for this address type on the given network.
/// Defensive check only; not a full script/checksum validation.
func matchesAddressFormat(_ address: String, network: LDKNode.Network) -> Bool {
let trimmed = address.trimmingCharacters(in: .whitespaces)
guard !trimmed.isEmpty else { return false }
let isMainnet = network == .bitcoin
switch self {
case .legacy:
return isMainnet ? trimmed.hasPrefix("1") : trimmed.hasPrefix("m") || trimmed.hasPrefix("n")
case .nestedSegwit:
return isMainnet ? trimmed.hasPrefix("3") : trimmed.hasPrefix("2")
case .nativeSegwit:
return isMainnet ? trimmed.hasPrefix("bc1q") : trimmed.hasPrefix("tb1q") || trimmed.hasPrefix("bcrt1q")
case .taproot:
return isMainnet ? trimmed.hasPrefix("bc1p") : trimmed.hasPrefix("tb1p") || trimmed.hasPrefix("bcrt1p")
}
}
}
16 changes: 1 addition & 15 deletions Bitkit/MainNavView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ struct MainNavView: View {
@EnvironmentObject private var wallet: WalletViewModel
@Environment(\.scenePhase) var scenePhase

@StateObject private var sweepViewModel = SweepViewModel()

@State private var showClipboardAlert = false
@State private var clipboardUri: String?

Expand Down Expand Up @@ -164,14 +162,6 @@ struct MainNavView: View {
) {
config in ForceTransferSheet(config: config)
}
.sheet(
item: $sheets.sweepPromptSheetItem,
onDismiss: {
sheets.hideSheet()
}
) {
config in SweepPromptSheet(config: config)
}
.accentColor(.white)
.overlay {
TabBar()
Expand Down Expand Up @@ -403,18 +393,14 @@ struct MainNavView: View {

// Advanced settings
case .coinSelection: CoinSelectionSettingsView()
case .addressTypePreference: AddressTypePreferenceView()
case .connections: LightningConnectionsView()
case let .connectionDetail(channelId): LightningConnectionDetailView(channelId: channelId)
case let .closeConnection(channel: channel): CloseConnectionConfirmation(channel: channel)
case .node: NodeStateView()
case .electrumSettings: ElectrumSettingsScreen()
case .rgsSettings: RgsSettingsScreen()
case .addressViewer: AddressViewer()
case .sweep: SweepSettingsView().environmentObject(sweepViewModel)
case .sweepConfirm: SweepConfirmView().environmentObject(sweepViewModel)
case .sweepFeeRate: SweepFeeRateView().environmentObject(sweepViewModel)
case .sweepFeeCustom: SweepFeeCustomView().environmentObject(sweepViewModel)
case let .sweepSuccess(txid): SweepSuccessView(txid: txid).environmentObject(sweepViewModel)

// Dev settings
case .blocktankRegtest: BlocktankRegtestView()
Expand Down
2 changes: 2 additions & 0 deletions Bitkit/Models/SettingsBackupConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ enum SettingsBackupConfig {
"defaultTransactionSpeed": .string(optional: true),
"coinSelectionMethod": .string(optional: true),
"coinSelectionAlgorithm": .string(optional: true),
"selectedAddressType": .string(optional: true),
"addressTypesToMonitor": .string(optional: true),
"enableQuickpay": .bool,
"showWidgets": .bool,
"showWidgetTitles": .bool,
Expand Down
2 changes: 0 additions & 2 deletions Bitkit/Resources/Localization/cs.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -710,8 +710,6 @@
"settings__adv__section_other" = "Další";
"settings__adv__address_type" = "Typ bitcoinové adresy";
"settings__adv__monitored_address_types" = "Sledované typy adres";
"settings__adv__monitored_address_types_update_title" = "Aktualizace sledovaných typů adres";
"settings__adv__monitored_address_types_update_description" = "Změny se plně projeví po restartu aplikace.";
"settings__adv__gap_limit" = "Limit mezery v adrese";
"settings__adv__coin_selection" = "Výběr mince";
"settings__adv__cs_method" = "Metoda výběru mince";
Expand Down
2 changes: 0 additions & 2 deletions Bitkit/Resources/Localization/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -708,8 +708,6 @@
"settings__adv__section_other" = "Andere";
"settings__adv__address_type" = "Bitcoin Adressen-Typ";
"settings__adv__monitored_address_types" = "Überwachte Adress-Typen";
"settings__adv__monitored_address_types_update_title" = "Überwachte Adress-Typen aktualisiert";
"settings__adv__monitored_address_types_update_description" = "Änderungen werden nach dem Neustart der App vollständig wirksam.";
"settings__adv__gap_limit" = "Address Gap Limit";
"settings__adv__coin_selection" = "Coin-Auswahl";
"settings__adv__cs_method" = "Coin-Auswahlmethode";
Expand Down
15 changes: 13 additions & 2 deletions Bitkit/Resources/Localization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -738,8 +738,19 @@
"settings__adv__section_other" = "Other";
"settings__adv__address_type" = "Bitcoin Address Type";
"settings__adv__monitored_address_types" = "Monitored Address Types";
"settings__adv__monitored_address_types_update_title" = "Monitored Address Types Updated";
"settings__adv__monitored_address_types_update_description" = "Changes will take full effect after app restarts.";
"settings__adv__addr_type_failed_title" = "Failed";
"settings__adv__addr_type_change_failed_desc" = "Could not change address type. Please try again.";
"settings__adv__addr_type_applying" = "Applying changes…";
"settings__adv__addr_type_changed_title" = "Address Type Changed";
"settings__adv__addr_type_changed_desc" = "Now using {type} addresses.";
"settings__adv__addr_type_monitored_updated_title" = "Settings Updated";
"settings__adv__addr_type_monitored_updated_desc" = "Address monitoring settings applied.";
"settings__adv__addr_type_cannot_disable_title" = "Cannot Disable";
"settings__adv__addr_type_cannot_disable_native_desc" = "At least one Native SegWit or Taproot wallet is required for Lightning channels.";
"settings__adv__addr_type_cannot_disable_balance_desc" = "{type} addresses have balance.";
"settings__adv__addr_type_monitored_failed_desc" = "Could not update monitoring settings. Please try again.";
"settings__adv__addr_type_currently_selected" = "Currently selected";
"settings__adv__addr_type_monitored_note" = "Enable monitoring to track funds received at different address types. The app will watch these addresses for incoming transactions. Disabling monitoring for a type with balance may hide your funds.";
"settings__adv__gap_limit" = "Address Gap Limit";
"settings__adv__coin_selection" = "Coin Selection";
"settings__adv__cs_method" = "Coin Selection Method";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,6 @@
"settings__adv__section_other" = "Otros";
"settings__adv__address_type" = "Tipo de dirección Bitcoin";
"settings__adv__monitored_address_types" = "Tipos de Direcciones en monitoreo";
"settings__adv__monitored_address_types_update_title" = "Tipos de dirección monitoreados actualizados";
"settings__adv__monitored_address_types_update_description" = "Los cambios surtirán pleno efecto tras reiniciar la aplicación.";
"settings__adv__gap_limit" = "Límite de la brecha de direcciones";
"settings__adv__coin_selection" = "Selección de monedas";
"settings__adv__cs_method" = "Método de selección de monedas";
Expand Down
2 changes: 0 additions & 2 deletions Bitkit/Resources/Localization/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -721,8 +721,6 @@
"settings__adv__section_other" = "Autre";
"settings__adv__address_type" = "Type d\'adresse Bitcoin";
"settings__adv__monitored_address_types" = "Types d\'adresses suivies";
"settings__adv__monitored_address_types_update_title" = "Mise à jour des types d\'adresses suivies";
"settings__adv__monitored_address_types_update_description" = "Les modifications prendront effet après le redémarrage de l\'application.";
"settings__adv__gap_limit" = "Limite de l\'écart d\'adresse";
"settings__adv__coin_selection" = "Sélection des UTXOs";
"settings__adv__cs_method" = "Méthode de sélection des UTXOs";
Expand Down
2 changes: 0 additions & 2 deletions Bitkit/Resources/Localization/it.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -699,8 +699,6 @@
"settings__adv__section_other" = "Altro";
"settings__adv__address_type" = "Tipologia Indirizzo Bitcoin";
"settings__adv__monitored_address_types" = "Tipi di indirizzi monitorati";
"settings__adv__monitored_address_types_update_title" = "Tipi di indirizzi monitorati aggiornati";
"settings__adv__monitored_address_types_update_description" = "Le modifiche avranno pieno effetto dopo il riavvio dell\'app.";
"settings__adv__gap_limit" = "Limite del gap di indirizzi";
"settings__adv__coin_selection" = "Coin Selection";
"settings__adv__cs_method" = "Metodo di Coin Selection";
Expand Down
2 changes: 0 additions & 2 deletions Bitkit/Resources/Localization/nl.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -718,8 +718,6 @@
"settings__adv__section_other" = "Overige";
"settings__adv__address_type" = "Bitcoin adres type";
"settings__adv__monitored_address_types" = "Bewaakte adrestypes";
"settings__adv__monitored_address_types_update_title" = "Bewaakte adrestypes bijgewerkt";
"settings__adv__monitored_address_types_update_description" = "Wijzigingen worden volledig van kracht nadat de app opnieuw is opgestart.";
"settings__adv__gap_limit" = "Adres Gap Limit";
"settings__adv__coin_selection" = "Coin selectie";
"settings__adv__cs_method" = "Coin Selectie Methode";
Expand Down
2 changes: 0 additions & 2 deletions Bitkit/Resources/Localization/pl.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -722,8 +722,6 @@
"settings__adv__section_other" = "Inne";
"settings__adv__address_type" = "Typ adresu Bitcoin";
"settings__adv__monitored_address_types" = "Monitorowane typy adresów";
"settings__adv__monitored_address_types_update_title" = "Zaktualizowano monitorowane typy adresów";
"settings__adv__monitored_address_types_update_description" = "Zmiany zostaną zastosowane po ponownym uruchomieniu aplikacji.";
"settings__adv__gap_limit" = "Limit odstępu adresów";
"settings__adv__coin_selection" = "Wybór monet";
"settings__adv__cs_method" = "Metoda wyboru monet";
Expand Down
2 changes: 0 additions & 2 deletions Bitkit/Resources/Localization/pt-BR.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -722,8 +722,6 @@
"settings__adv__section_other" = "Outros";
"settings__adv__address_type" = "Tipo de endereço Bitcoin";
"settings__adv__monitored_address_types" = "Tipos de Endereços Monitorados";
"settings__adv__monitored_address_types_update_title" = "Tipos de Endereços Monitorados Atualizados";
"settings__adv__monitored_address_types_update_description" = "As alterações terão após a reinicialização do aplicativo.";
"settings__adv__gap_limit" = "Limite de Endereços";
"settings__adv__coin_selection" = "Controle de Moedas";
"settings__adv__cs_method" = "Método de Controle de Moedas";
Expand Down
2 changes: 0 additions & 2 deletions Bitkit/Resources/Localization/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -722,8 +722,6 @@
"settings__adv__section_other" = "Другое";
"settings__adv__address_type" = "Тип Биткойн-Адреса";
"settings__adv__monitored_address_types" = "Отслеживаемые Типы Адресов";
"settings__adv__monitored_address_types_update_title" = "Отслеживаемые Типы Адресов Обновлены";
"settings__adv__monitored_address_types_update_description" = "Изменения вступят в силу после перезапуска приложения.";
"settings__adv__gap_limit" = "Gap Limit Адресов";
"settings__adv__coin_selection" = "Выбор Монет";
"settings__adv__cs_method" = "Метод Выбора Монет";
Expand Down
Loading
Loading