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.",