diff --git a/CHANGELOG.md b/CHANGELOG.md
index a578d1fb857..92367e314fe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## Unreleased (develop)
- added: `AGENT_TEST_MODE` config setting and iOS debug-build `EdgeTestAnimations` switch that quiet the app for automated UI tests
+- changed: View XPub Address lists each xpub on its own labeled copy row, with a Copy All button
- changed: Stealth Swap works with Houdini switched off in the exchange settings, as Stealth Send already does
- fixed: Staking positions that loaded slowly or never when a public RPC node rate limited requests, with the stake plugins now sharing a vetted node list per chain and moving to another node when one fails
- fixed: Recognize promotions that only set a preferred exchange in the exchange settings, which now say the promotion is choosing it and offer to remove it, instead of showing a list whose taps were saved but silently overridden for the promotion's whole window
diff --git a/src/actions/WalletListMenuActions.tsx b/src/actions/WalletListMenuActions.tsx
index e6b924e9cc5..0c6c6be5077 100644
--- a/src/actions/WalletListMenuActions.tsx
+++ b/src/actions/WalletListMenuActions.tsx
@@ -4,6 +4,7 @@ import * as React from 'react'
import { Linking } from 'react-native'
import { sprintf } from 'sprintf-js'
+import { EdgeCard } from '../components/cards/EdgeCard'
import {
type ButtonInfo,
ButtonsModal
@@ -13,6 +14,7 @@ import {
EditWalletSettingsModal,
type WalletSettingsResult
} from '../components/modals/WalletSettingsModal'
+import { EdgeRow } from '../components/rows/EdgeRow'
import {
Airship,
showError,
@@ -212,8 +214,7 @@ export function walletListMenuAction(
})
}
}
- case 'viewPrivateViewKey':
- case 'viewXPub': {
+ case 'viewPrivateViewKey': {
return async (dispatch, getState) => {
const state = getState()
const { account } = state.core
@@ -230,30 +231,23 @@ export function walletListMenuAction(
}
const buttons = xpubExplorer != null ? { copy, link } : { copy }
- const title =
- switchString === 'viewPrivateViewKey'
- ? lstrings.fragment_wallets_view_private_view_key
- : lstrings.fragment_wallets_view_xpub
-
await Airship.show<'copy' | 'link' | undefined>(bridge => (
- {switchString === 'viewXPub' ? null : (
-
- )}
+
)).then(async result => {
switch (result) {
@@ -273,6 +267,63 @@ export function walletListMenuAction(
}
}
+ case 'viewXPub': {
+ return async (dispatch, getState) => {
+ const state = getState()
+ const { account } = state.core
+ const wallet = account.currencyWallets[walletId]
+ const { xpubExplorer } = wallet.currencyInfo
+
+ const displayKeys = await account.getDisplayPublicKeys(wallet.id)
+ const xpubs = Object.values(displayKeys)
+
+ const copy: ButtonInfo = {
+ label:
+ xpubs.length > 1
+ ? lstrings.fragment_wallets_copy_all_xpubs
+ : lstrings.fragment_request_copy_title
+ }
+ const link: ButtonInfo = {
+ label: lstrings.transaction_details_show_advanced_block_explorer
+ }
+ const buttons = xpubExplorer != null ? { copy, link } : { copy }
+
+ await Airship.show<'copy' | 'link' | undefined>(bridge => (
+
+
+ {Object.entries(displayKeys).map(([name, xpub]) => (
+
+ ))}
+
+
+ )).then(async result => {
+ switch (result) {
+ case 'copy':
+ Clipboard.setString(xpubs.join('\n'))
+ showToast(lstrings.fragment_wallets_pubkey_copied_title)
+ break
+ case 'link':
+ if (xpubExplorer != null && xpubs.length > 0) {
+ await Linking.openURL(sprintf(xpubExplorer, xpubs[0]))
+ }
+ break
+ case undefined:
+ break
+ }
+ })
+ }
+ }
+
case 'exportWalletTransactions': {
return async (dispatch, getState) => {
const state = getState()
@@ -408,3 +459,23 @@ export function walletListMenuAction(
return async () => {}
}
}
+
+/**
+ * Names a display public key by the address format it derives.
+ */
+function getXpubTitle(name: string): string {
+ switch (name) {
+ case 'bip84':
+ return lstrings.fragment_wallets_xpub_segwit
+ case 'bip49':
+ return lstrings.fragment_wallets_xpub_wrapped_segwit
+ case 'bip44':
+ return lstrings.fragment_wallets_xpub_legacy
+ case 'bip32':
+ return lstrings.fragment_wallets_xpub_legacy_bip32
+ case 'publicKey':
+ return lstrings.fragment_wallets_xpub_public_key
+ default:
+ return name
+ }
+}
diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts
index 8c11d1f6eeb..d571747d8d2 100644
--- a/src/locales/en_US.ts
+++ b/src/locales/en_US.ts
@@ -310,6 +310,12 @@ const strings = {
fragment_wallets_view_private_view_key: 'Private View Key',
fragment_wallets_view_private_view_key_warning_s: `The private view key allows the receiver to see the balance in your %1$s wallet. Do not share this key unless necessary, such as for tax purposes, accounting, or similar reasons.`,
fragment_wallets_view_xpub: 'View XPub Address',
+ fragment_wallets_copy_all_xpubs: 'Copy All',
+ fragment_wallets_xpub_segwit: 'SegWit',
+ fragment_wallets_xpub_wrapped_segwit: 'Wrapped SegWit',
+ fragment_wallets_xpub_legacy: 'Legacy',
+ fragment_wallets_xpub_legacy_bip32: 'Legacy (BIP32)',
+ fragment_wallets_xpub_public_key: 'Public Key',
fragment_wallets_sign_message: 'Sign Message',
sign_message_title: 'Sign Message',
sign_message_instructions:
diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json
index be56df55304..74d2cf8e5fa 100644
--- a/src/locales/strings/enUS.json
+++ b/src/locales/strings/enUS.json
@@ -205,6 +205,12 @@
"fragment_wallets_view_private_view_key": "Private View Key",
"fragment_wallets_view_private_view_key_warning_s": "The private view key allows the receiver to see the balance in your %1$s wallet. Do not share this key unless necessary, such as for tax purposes, accounting, or similar reasons.",
"fragment_wallets_view_xpub": "View XPub Address",
+ "fragment_wallets_copy_all_xpubs": "Copy All",
+ "fragment_wallets_xpub_segwit": "SegWit",
+ "fragment_wallets_xpub_wrapped_segwit": "Wrapped SegWit",
+ "fragment_wallets_xpub_legacy": "Legacy",
+ "fragment_wallets_xpub_legacy_bip32": "Legacy (BIP32)",
+ "fragment_wallets_xpub_public_key": "Public Key",
"fragment_wallets_sign_message": "Sign Message",
"sign_message_title": "Sign Message",
"sign_message_instructions": "Some exchanges ask you to prove you control this wallet by signing a message they provide. Paste the exact message below and sign it with your wallet address, then copy the signature back to the exchange.",