mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 01:40:12 +01:00
Merge pull request #7293 from BlueWallet/import-offline
feat: offline import
This commit is contained in:
commit
219130a5e8
@ -1113,7 +1113,7 @@ export const testConnection = async function (host: string, tcpPort?: number, ss
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const forceDisconnect = (): void => {
|
export const forceDisconnect = (): void => {
|
||||||
mainClient.close();
|
mainClient?.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setBatchingDisabled = () => {
|
export const setBatchingDisabled = () => {
|
||||||
|
@ -53,6 +53,7 @@ const startImport = (
|
|||||||
importTextOrig: string,
|
importTextOrig: string,
|
||||||
askPassphrase: boolean = false,
|
askPassphrase: boolean = false,
|
||||||
searchAccounts: boolean = false,
|
searchAccounts: boolean = false,
|
||||||
|
offline: boolean = false,
|
||||||
onProgress: (name: string) => void,
|
onProgress: (name: string) => void,
|
||||||
onWallet: (wallet: TWallet) => void,
|
onWallet: (wallet: TWallet) => void,
|
||||||
onPassword: (title: string, text: string) => Promise<string>,
|
onPassword: (title: string, text: string) => Promise<string>,
|
||||||
@ -67,6 +68,18 @@ const startImport = (
|
|||||||
promiseReject = reject;
|
promiseReject = reject;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
// in offline mode all wallets are considered used
|
||||||
|
const wasUsed = async (wallet: TWallet): Promise<boolean> => {
|
||||||
|
if (offline) return true;
|
||||||
|
return wallet.wasEverUsed();
|
||||||
|
};
|
||||||
|
const fetch = async (wallet: TWallet, balance: boolean = false, transactions: boolean = false) => {
|
||||||
|
if (offline) return;
|
||||||
|
if (balance) await wallet.fetchBalance();
|
||||||
|
if (transactions) await wallet.fetchTransactions();
|
||||||
|
};
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
const reportProgress = (name: string) => {
|
const reportProgress = (name: string) => {
|
||||||
onProgress(name);
|
onProgress(name);
|
||||||
@ -165,7 +178,7 @@ const startImport = (
|
|||||||
const ms = new MultisigHDWallet();
|
const ms = new MultisigHDWallet();
|
||||||
ms.setSecret(text);
|
ms.setSecret(text);
|
||||||
if (ms.getN() > 0 && ms.getM() > 0) {
|
if (ms.getN() > 0 && ms.getM() > 0) {
|
||||||
await ms.fetchBalance();
|
await fetch(ms, true, false);
|
||||||
yield { wallet: ms };
|
yield { wallet: ms };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,11 +192,13 @@ const startImport = (
|
|||||||
lnd.setSecret(split[0]);
|
lnd.setSecret(split[0]);
|
||||||
}
|
}
|
||||||
await lnd.init();
|
await lnd.init();
|
||||||
await lnd.authorize();
|
if (!offline) {
|
||||||
await lnd.fetchTransactions();
|
await lnd.authorize();
|
||||||
await lnd.fetchUserInvoices();
|
await lnd.fetchTransactions();
|
||||||
await lnd.fetchPendingTransactions();
|
await lnd.fetchUserInvoices();
|
||||||
await lnd.fetchBalance();
|
await lnd.fetchPendingTransactions();
|
||||||
|
await lnd.fetchBalance();
|
||||||
|
}
|
||||||
yield { wallet: lnd };
|
yield { wallet: lnd };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +243,7 @@ const startImport = (
|
|||||||
}
|
}
|
||||||
wallet.setDerivationPath(path);
|
wallet.setDerivationPath(path);
|
||||||
yield { progress: `bip39 ${i.script_type} ${path}` };
|
yield { progress: `bip39 ${i.script_type} ${path}` };
|
||||||
if (await wallet.wasEverUsed()) {
|
if (await wasUsed(wallet)) {
|
||||||
yield { wallet };
|
yield { wallet };
|
||||||
walletFound = true;
|
walletFound = true;
|
||||||
} else {
|
} else {
|
||||||
@ -247,11 +262,12 @@ const startImport = (
|
|||||||
m0Legacy.setDerivationPath("m/0'");
|
m0Legacy.setDerivationPath("m/0'");
|
||||||
yield { progress: "bip39 p2pkh m/0'" };
|
yield { progress: "bip39 p2pkh m/0'" };
|
||||||
// BRD doesn't support passphrase and only works with 12 words seeds
|
// BRD doesn't support passphrase and only works with 12 words seeds
|
||||||
if (!password && text.split(' ').length === 12) {
|
// do not try to guess BRD wallet in offline mode
|
||||||
|
if (!password && text.split(' ').length === 12 && !offline) {
|
||||||
const brd = new HDLegacyBreadwalletWallet();
|
const brd = new HDLegacyBreadwalletWallet();
|
||||||
brd.setSecret(text);
|
brd.setSecret(text);
|
||||||
|
|
||||||
if (await m0Legacy.wasEverUsed()) {
|
if (await wasUsed(m0Legacy)) {
|
||||||
await m0Legacy.fetchBalance();
|
await m0Legacy.fetchBalance();
|
||||||
await m0Legacy.fetchTransactions();
|
await m0Legacy.fetchTransactions();
|
||||||
yield { progress: 'BRD' };
|
yield { progress: 'BRD' };
|
||||||
@ -265,7 +281,7 @@ const startImport = (
|
|||||||
walletFound = true;
|
walletFound = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (await m0Legacy.wasEverUsed()) {
|
if (await wasUsed(m0Legacy)) {
|
||||||
yield { wallet: m0Legacy };
|
yield { wallet: m0Legacy };
|
||||||
walletFound = true;
|
walletFound = true;
|
||||||
}
|
}
|
||||||
@ -275,7 +291,6 @@ const startImport = (
|
|||||||
if (!walletFound) {
|
if (!walletFound) {
|
||||||
yield { wallet: hd2 };
|
yield { wallet: hd2 };
|
||||||
}
|
}
|
||||||
// return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
yield { progress: 'wif' };
|
yield { progress: 'wif' };
|
||||||
@ -288,17 +303,17 @@ const startImport = (
|
|||||||
yield { progress: 'wif p2wpkh' };
|
yield { progress: 'wif p2wpkh' };
|
||||||
const segwitBech32Wallet = new SegwitBech32Wallet();
|
const segwitBech32Wallet = new SegwitBech32Wallet();
|
||||||
segwitBech32Wallet.setSecret(text);
|
segwitBech32Wallet.setSecret(text);
|
||||||
if (await segwitBech32Wallet.wasEverUsed()) {
|
if (await wasUsed(segwitBech32Wallet)) {
|
||||||
// yep, its single-address bech32 wallet
|
// yep, its single-address bech32 wallet
|
||||||
await segwitBech32Wallet.fetchBalance();
|
await fetch(segwitBech32Wallet, true);
|
||||||
walletFound = true;
|
walletFound = true;
|
||||||
yield { wallet: segwitBech32Wallet };
|
yield { wallet: segwitBech32Wallet };
|
||||||
}
|
}
|
||||||
|
|
||||||
yield { progress: 'wif p2wpkh-p2sh' };
|
yield { progress: 'wif p2wpkh-p2sh' };
|
||||||
if (await segwitWallet.wasEverUsed()) {
|
if (await wasUsed(segwitWallet)) {
|
||||||
// yep, its single-address p2wpkh wallet
|
// yep, its single-address p2wpkh wallet
|
||||||
await segwitWallet.fetchBalance();
|
await fetch(segwitWallet, true);
|
||||||
walletFound = true;
|
walletFound = true;
|
||||||
yield { wallet: segwitWallet };
|
yield { wallet: segwitWallet };
|
||||||
}
|
}
|
||||||
@ -307,9 +322,9 @@ const startImport = (
|
|||||||
yield { progress: 'wif p2pkh' };
|
yield { progress: 'wif p2pkh' };
|
||||||
const legacyWallet = new LegacyWallet();
|
const legacyWallet = new LegacyWallet();
|
||||||
legacyWallet.setSecret(text);
|
legacyWallet.setSecret(text);
|
||||||
if (await legacyWallet.wasEverUsed()) {
|
if (await wasUsed(legacyWallet)) {
|
||||||
// yep, its single-address legacy wallet
|
// yep, its single-address legacy wallet
|
||||||
await legacyWallet.fetchBalance();
|
await fetch(legacyWallet, true);
|
||||||
walletFound = true;
|
walletFound = true;
|
||||||
yield { wallet: legacyWallet };
|
yield { wallet: legacyWallet };
|
||||||
}
|
}
|
||||||
@ -327,8 +342,7 @@ const startImport = (
|
|||||||
const legacyWallet = new LegacyWallet();
|
const legacyWallet = new LegacyWallet();
|
||||||
legacyWallet.setSecret(text);
|
legacyWallet.setSecret(text);
|
||||||
if (legacyWallet.getAddress()) {
|
if (legacyWallet.getAddress()) {
|
||||||
await legacyWallet.fetchBalance();
|
await fetch(legacyWallet, true, true);
|
||||||
await legacyWallet.fetchTransactions();
|
|
||||||
yield { wallet: legacyWallet };
|
yield { wallet: legacyWallet };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +351,7 @@ const startImport = (
|
|||||||
const watchOnly = new WatchOnlyWallet();
|
const watchOnly = new WatchOnlyWallet();
|
||||||
watchOnly.setSecret(text);
|
watchOnly.setSecret(text);
|
||||||
if (watchOnly.valid()) {
|
if (watchOnly.valid()) {
|
||||||
await watchOnly.fetchBalance();
|
await fetch(watchOnly, true);
|
||||||
yield { wallet: watchOnly };
|
yield { wallet: watchOnly };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +398,7 @@ const startImport = (
|
|||||||
if (password) {
|
if (password) {
|
||||||
s1.setPassphrase(password);
|
s1.setPassphrase(password);
|
||||||
}
|
}
|
||||||
if (await s1.wasEverUsed()) {
|
if (await wasUsed(s1)) {
|
||||||
yield { wallet: s1 };
|
yield { wallet: s1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +408,7 @@ const startImport = (
|
|||||||
s2.setPassphrase(password);
|
s2.setPassphrase(password);
|
||||||
}
|
}
|
||||||
s2.setSecret(text);
|
s2.setSecret(text);
|
||||||
if (await s2.wasEverUsed()) {
|
if (await wasUsed(s2)) {
|
||||||
yield { wallet: s2 };
|
yield { wallet: s2 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +447,7 @@ const startImport = (
|
|||||||
if (next.value?.progress) reportProgress(next.value.progress);
|
if (next.value?.progress) reportProgress(next.value.progress);
|
||||||
if (next.value?.wallet) reportWallet(next.value.wallet);
|
if (next.value?.wallet) reportWallet(next.value.wallet);
|
||||||
if (next.done) break; // break if generator has been finished
|
if (next.done) break; // break if generator has been finished
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1)); // try not to block the thread
|
||||||
}
|
}
|
||||||
reportFinish();
|
reportFinish();
|
||||||
})().catch(e => {
|
})().catch(e => {
|
||||||
|
@ -448,6 +448,7 @@
|
|||||||
"import_discovery_subtitle": "Choose a discovered wallet",
|
"import_discovery_subtitle": "Choose a discovered wallet",
|
||||||
"import_discovery_derivation": "Use custom derivation path",
|
"import_discovery_derivation": "Use custom derivation path",
|
||||||
"import_discovery_no_wallets": "No wallets were found.",
|
"import_discovery_no_wallets": "No wallets were found.",
|
||||||
|
"import_discovery_offline": "BlueWallet is currently in offline mode. In this mode, it can't verify the existence of the wallet, so you'll need to select the correct one manually",
|
||||||
"import_derivation_found": "Found",
|
"import_derivation_found": "Found",
|
||||||
"import_derivation_found_not": "Not found",
|
"import_derivation_found_not": "Not found",
|
||||||
"import_derivation_loading": "Loading...",
|
"import_derivation_loading": "Loading...",
|
||||||
|
@ -20,7 +20,11 @@ import {
|
|||||||
|
|
||||||
export type AddWalletStackParamList = {
|
export type AddWalletStackParamList = {
|
||||||
AddWallet: undefined;
|
AddWallet: undefined;
|
||||||
ImportWallet: undefined;
|
ImportWallet?: {
|
||||||
|
label?: string;
|
||||||
|
triggerImport?: boolean;
|
||||||
|
scannedData?: string;
|
||||||
|
};
|
||||||
ImportWalletDiscovery: {
|
ImportWalletDiscovery: {
|
||||||
importText: string;
|
importText: string;
|
||||||
askPassphrase: boolean;
|
askPassphrase: boolean;
|
||||||
|
@ -7,7 +7,7 @@ const WalletsAdd = lazy(() => import('../screen/wallets/Add'));
|
|||||||
const ImportCustomDerivationPath = lazy(() => import('../screen/wallets/ImportCustomDerivationPath'));
|
const ImportCustomDerivationPath = lazy(() => import('../screen/wallets/ImportCustomDerivationPath'));
|
||||||
const ImportWalletDiscovery = lazy(() => import('../screen/wallets/ImportWalletDiscovery'));
|
const ImportWalletDiscovery = lazy(() => import('../screen/wallets/ImportWalletDiscovery'));
|
||||||
const ImportSpeed = lazy(() => import('../screen/wallets/ImportSpeed'));
|
const ImportSpeed = lazy(() => import('../screen/wallets/ImportSpeed'));
|
||||||
const ImportWallet = lazy(() => import('../screen/wallets/import'));
|
const ImportWallet = lazy(() => import('../screen/wallets/ImportWallet'));
|
||||||
const PleaseBackup = lazy(() => import('../screen/wallets/PleaseBackup'));
|
const PleaseBackup = lazy(() => import('../screen/wallets/PleaseBackup'));
|
||||||
const PleaseBackupLNDHub = lazy(() => import('../screen/wallets/pleaseBackupLNDHub'));
|
const PleaseBackupLNDHub = lazy(() => import('../screen/wallets/pleaseBackupLNDHub'));
|
||||||
const ProvideEntropy = lazy(() => import('../screen/wallets/ProvideEntropy'));
|
const ProvideEntropy = lazy(() => import('../screen/wallets/ProvideEntropy'));
|
||||||
|
@ -14,6 +14,7 @@ import WalletToImport from '../../components/WalletToImport';
|
|||||||
import { useStorage } from '../../hooks/context/useStorage';
|
import { useStorage } from '../../hooks/context/useStorage';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
|
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
|
||||||
|
import { useSettings } from '../../hooks/context/useSettings';
|
||||||
|
|
||||||
type RouteProps = RouteProp<AddWalletStackParamList, 'ImportCustomDerivationPath'>;
|
type RouteProps = RouteProp<AddWalletStackParamList, 'ImportCustomDerivationPath'>;
|
||||||
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'ImportCustomDerivationPath'>;
|
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'ImportCustomDerivationPath'>;
|
||||||
@ -44,6 +45,7 @@ const ImportCustomDerivationPath: React.FC = () => {
|
|||||||
const [used, setUsed] = useState<TUsedByPath>({});
|
const [used, setUsed] = useState<TUsedByPath>({});
|
||||||
const [selected, setSelected] = useState<string>('');
|
const [selected, setSelected] = useState<string>('');
|
||||||
const importing = useRef(false);
|
const importing = useRef(false);
|
||||||
|
const { isElectrumDisabled } = useSettings();
|
||||||
|
|
||||||
const debouncedSavePath = useRef(
|
const debouncedSavePath = useRef(
|
||||||
debounce(async newPath => {
|
debounce(async newPath => {
|
||||||
@ -65,6 +67,14 @@ const ImportCustomDerivationPath: React.FC = () => {
|
|||||||
}
|
}
|
||||||
setWallets(ws => ({ ...ws, [newPath]: newWallets }));
|
setWallets(ws => ({ ...ws, [newPath]: newWallets }));
|
||||||
|
|
||||||
|
if (isElectrumDisabled) {
|
||||||
|
// do not check if electrum is disabled
|
||||||
|
Object.values(newWallets).forEach(w => {
|
||||||
|
setUsed(u => ({ ...u, [newPath]: { ...u[newPath], [w.type]: STATUS.WALLET_UNKNOWN } }));
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// discover was they ever used
|
// discover was they ever used
|
||||||
const promises = Object.values(newWallets).map(w => {
|
const promises = Object.values(newWallets).map(w => {
|
||||||
return w.wasEverUsed().then(v => {
|
return w.wasEverUsed().then(v => {
|
||||||
@ -81,6 +91,7 @@ const ImportCustomDerivationPath: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, 500),
|
}, 500),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (path in wallets) return;
|
if (path in wallets) return;
|
||||||
debouncedSavePath.current(path);
|
debouncedSavePath.current(path);
|
||||||
|
@ -1,36 +1,41 @@
|
|||||||
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useRoute } from '@react-navigation/native';
|
import { RouteProp, useRoute } from '@react-navigation/native';
|
||||||
import { Keyboard, Platform, StyleSheet, TouchableWithoutFeedback, View, ScrollView } from 'react-native';
|
import Clipboard from '@react-native-clipboard/clipboard';
|
||||||
|
import { Keyboard, Platform, ScrollView, StyleSheet, TouchableWithoutFeedback, View } from 'react-native';
|
||||||
|
import { disallowScreenshot } from 'react-native-screen-capture';
|
||||||
import { BlueButtonLink, BlueFormLabel, BlueFormMultiInput, BlueSpacing20 } from '../../BlueComponents';
|
import { BlueButtonLink, BlueFormLabel, BlueFormMultiInput, BlueSpacing20 } from '../../BlueComponents';
|
||||||
import Button from '../../components/Button';
|
import Button from '../../components/Button';
|
||||||
import { useTheme } from '../../components/themes';
|
|
||||||
import { scanQrHelper } from '../../helpers/scan-qr';
|
|
||||||
import { disallowScreenshot } from 'react-native-screen-capture';
|
|
||||||
import loc from '../../loc';
|
|
||||||
import {
|
import {
|
||||||
DoneAndDismissKeyboardInputAccessory,
|
DoneAndDismissKeyboardInputAccessory,
|
||||||
DoneAndDismissKeyboardInputAccessoryViewID,
|
DoneAndDismissKeyboardInputAccessoryViewID,
|
||||||
} from '../../components/DoneAndDismissKeyboardInputAccessory';
|
} from '../../components/DoneAndDismissKeyboardInputAccessory';
|
||||||
import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
|
||||||
import { useKeyboard } from '../../hooks/useKeyboard';
|
|
||||||
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
|
||||||
import Clipboard from '@react-native-clipboard/clipboard';
|
|
||||||
import HeaderMenuButton from '../../components/HeaderMenuButton';
|
import HeaderMenuButton from '../../components/HeaderMenuButton';
|
||||||
|
import { useTheme } from '../../components/themes';
|
||||||
|
import { scanQrHelper } from '../../helpers/scan-qr';
|
||||||
import { useSettings } from '../../hooks/context/useSettings';
|
import { useSettings } from '../../hooks/context/useSettings';
|
||||||
|
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
|
||||||
|
import { useKeyboard } from '../../hooks/useKeyboard';
|
||||||
|
import loc from '../../loc';
|
||||||
|
import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
|
||||||
|
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
|
||||||
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||||
|
|
||||||
const WalletsImport = () => {
|
type RouteProps = RouteProp<AddWalletStackParamList, 'ImportWallet'>;
|
||||||
const navigation = useExtendedNavigation();
|
type NavigationProps = NativeStackNavigationProp<AddWalletStackParamList, 'ImportWallet'>;
|
||||||
|
|
||||||
|
const ImportWallet = () => {
|
||||||
|
const navigation = useExtendedNavigation<NavigationProps>();
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const route = useRoute();
|
const route = useRoute<RouteProps>();
|
||||||
const label = route?.params?.label ?? '';
|
const label = route?.params?.label ?? '';
|
||||||
const triggerImport = route?.params?.triggerImport ?? false;
|
const triggerImport = route?.params?.triggerImport ?? false;
|
||||||
const scannedData = route?.params?.scannedData ?? '';
|
const scannedData = route?.params?.scannedData ?? '';
|
||||||
const [importText, setImportText] = useState(label);
|
const [importText, setImportText] = useState<string>(label);
|
||||||
const [isToolbarVisibleForAndroid, setIsToolbarVisibleForAndroid] = useState(false);
|
const [isToolbarVisibleForAndroid, setIsToolbarVisibleForAndroid] = useState<boolean>(false);
|
||||||
const [, setSpeedBackdoor] = useState(0);
|
const [, setSpeedBackdoor] = useState<number>(0);
|
||||||
const [searchAccountsMenuState, setSearchAccountsMenuState] = useState(false);
|
const [searchAccountsMenuState, setSearchAccountsMenuState] = useState<boolean>(false);
|
||||||
const [askPassphraseMenuState, setAskPassphraseMenuState] = useState(false);
|
const [askPassphraseMenuState, setAskPassphraseMenuState] = useState<boolean>(false);
|
||||||
const [clearClipboardMenuState, setClearClipboardMenuState] = useState(true);
|
const [clearClipboardMenuState, setClearClipboardMenuState] = useState<boolean>(true);
|
||||||
const { isPrivacyBlurEnabled } = useSettings();
|
const { isPrivacyBlurEnabled } = useSettings();
|
||||||
// Styles
|
// Styles
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
@ -46,11 +51,11 @@ const WalletsImport = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onBlur = () => {
|
const onBlur = useCallback(() => {
|
||||||
const valueWithSingleWhitespace = importText.replace(/^\s+|\s+$|\s+(?=\s)/g, '');
|
const valueWithSingleWhitespace = importText.replace(/^\s+|\s+$|\s+(?=\s)/g, '');
|
||||||
setImportText(valueWithSingleWhitespace);
|
setImportText(valueWithSingleWhitespace);
|
||||||
return valueWithSingleWhitespace;
|
return valueWithSingleWhitespace;
|
||||||
};
|
}, [importText]);
|
||||||
|
|
||||||
useKeyboard({
|
useKeyboard({
|
||||||
onKeyboardDidShow: () => {
|
onKeyboardDidShow: () => {
|
||||||
@ -61,62 +66,50 @@ const WalletsImport = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
const importMnemonic = useCallback(
|
||||||
disallowScreenshot(isPrivacyBlurEnabled);
|
async (text: string) => {
|
||||||
return () => {
|
if (clearClipboardMenuState) {
|
||||||
disallowScreenshot(false);
|
try {
|
||||||
};
|
if (await Clipboard.hasString()) {
|
||||||
}, [isPrivacyBlurEnabled]);
|
Clipboard.setString('');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to clear clipboard:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navigation.navigate('ImportWalletDiscovery', {
|
||||||
|
importText: text,
|
||||||
|
askPassphrase: askPassphraseMenuState,
|
||||||
|
searchAccounts: searchAccountsMenuState,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[askPassphraseMenuState, clearClipboardMenuState, navigation, searchAccountsMenuState],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const handleImport = useCallback(() => {
|
||||||
if (triggerImport) importButtonPressed();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [triggerImport]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (scannedData) {
|
|
||||||
onBarScanned(scannedData);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [scannedData]);
|
|
||||||
|
|
||||||
const importButtonPressed = () => {
|
|
||||||
const textToImport = onBlur();
|
const textToImport = onBlur();
|
||||||
if (textToImport.trim().length === 0) {
|
if (textToImport.trim().length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
importMnemonic(textToImport);
|
importMnemonic(textToImport);
|
||||||
};
|
}, [importMnemonic, onBlur]);
|
||||||
|
|
||||||
const importMnemonic = async text => {
|
const onBarScanned = useCallback(
|
||||||
if (clearClipboardMenuState) {
|
(value: string | { data: any }) => {
|
||||||
try {
|
// no objects here, only strings
|
||||||
if (await Clipboard.hasString()) {
|
const newValue: string = typeof value !== 'string' ? value.data + '' : value;
|
||||||
Clipboard.setString('');
|
setImportText(newValue);
|
||||||
}
|
setTimeout(() => importMnemonic(newValue), 500);
|
||||||
} catch (error) {
|
},
|
||||||
console.error('Failed to clear clipboard:', error);
|
[importMnemonic],
|
||||||
}
|
);
|
||||||
}
|
|
||||||
navigation.navigate('ImportWalletDiscovery', {
|
|
||||||
importText: text,
|
|
||||||
askPassphrase: askPassphraseMenuState,
|
|
||||||
searchAccounts: searchAccountsMenuState,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onBarScanned = value => {
|
const importScan = useCallback(async () => {
|
||||||
if (value && value.data) value = value.data + ''; // no objects here, only strings
|
const data = await scanQrHelper(route.name, true);
|
||||||
setImportText(value);
|
|
||||||
setTimeout(() => importMnemonic(value), 500);
|
|
||||||
};
|
|
||||||
|
|
||||||
const importScan = async () => {
|
|
||||||
const data = await scanQrHelper(navigation, true);
|
|
||||||
if (data) {
|
if (data) {
|
||||||
onBarScanned(data);
|
onBarScanned(data);
|
||||||
}
|
}
|
||||||
};
|
}, [route.name, onBarScanned]);
|
||||||
|
|
||||||
const speedBackdoorTap = () => {
|
const speedBackdoorTap = () => {
|
||||||
setSpeedBackdoor(v => {
|
setSpeedBackdoor(v => {
|
||||||
@ -128,7 +121,7 @@ const WalletsImport = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const toolTipOnPressMenuItem = useCallback(
|
const toolTipOnPressMenuItem = useCallback(
|
||||||
menuItem => {
|
(menuItem: string) => {
|
||||||
Keyboard.dismiss();
|
Keyboard.dismiss();
|
||||||
if (menuItem === CommonToolTipActions.Passphrase.id) {
|
if (menuItem === CommonToolTipActions.Passphrase.id) {
|
||||||
setAskPassphraseMenuState(!askPassphraseMenuState);
|
setAskPassphraseMenuState(!askPassphraseMenuState);
|
||||||
@ -143,13 +136,11 @@ const WalletsImport = () => {
|
|||||||
|
|
||||||
// ToolTipMenu actions for advanced options
|
// ToolTipMenu actions for advanced options
|
||||||
const toolTipActions = useMemo(() => {
|
const toolTipActions = useMemo(() => {
|
||||||
const askPassphraseAction = CommonToolTipActions.Passphrase;
|
return [
|
||||||
askPassphraseAction.menuState = askPassphraseMenuState;
|
{ ...CommonToolTipActions.Passphrase, menuState: askPassphraseMenuState },
|
||||||
const searchAccountsAction = CommonToolTipActions.SearchAccount;
|
{ ...CommonToolTipActions.SearchAccount, menuState: searchAccountsMenuState },
|
||||||
searchAccountsAction.menuState = searchAccountsMenuState;
|
{ ...CommonToolTipActions.ClearClipboard, menuState: clearClipboardMenuState },
|
||||||
const clearClipboardAction = CommonToolTipActions.ClearClipboard;
|
];
|
||||||
clearClipboardAction.menuState = clearClipboardMenuState;
|
|
||||||
return [askPassphraseAction, searchAccountsAction, clearClipboardAction];
|
|
||||||
}, [askPassphraseMenuState, clearClipboardMenuState, searchAccountsMenuState]);
|
}, [askPassphraseMenuState, clearClipboardMenuState, searchAccountsMenuState]);
|
||||||
|
|
||||||
const HeaderRight = useMemo(
|
const HeaderRight = useMemo(
|
||||||
@ -157,6 +148,23 @@ const WalletsImport = () => {
|
|||||||
[toolTipOnPressMenuItem, toolTipActions],
|
[toolTipOnPressMenuItem, toolTipActions],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
disallowScreenshot(isPrivacyBlurEnabled);
|
||||||
|
return () => {
|
||||||
|
disallowScreenshot(false);
|
||||||
|
};
|
||||||
|
}, [isPrivacyBlurEnabled]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (triggerImport) handleImport();
|
||||||
|
}, [triggerImport, handleImport]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (scannedData) {
|
||||||
|
onBarScanned(scannedData);
|
||||||
|
}
|
||||||
|
}, [scannedData, onBarScanned]);
|
||||||
|
|
||||||
// Adding the ToolTipMenu to the header
|
// Adding the ToolTipMenu to the header
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
@ -169,12 +177,7 @@ const WalletsImport = () => {
|
|||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
<View style={styles.center}>
|
<View style={styles.center}>
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button disabled={importText.trim().length === 0} title={loc.wallets.import_do_import} testID="DoImport" onPress={handleImport} />
|
||||||
disabled={importText.trim().length === 0}
|
|
||||||
title={loc.wallets.import_do_import}
|
|
||||||
testID="DoImport"
|
|
||||||
onPress={importButtonPressed}
|
|
||||||
/>
|
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={importScan} testID="ScanImport" />
|
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={importScan} testID="ScanImport" />
|
||||||
</>
|
</>
|
||||||
@ -234,4 +237,4 @@ const WalletsImport = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WalletsImport;
|
export default ImportWallet;
|
@ -19,6 +19,7 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
|||||||
import { THDWalletForWatchOnly, TWallet } from '../../class/wallets/types';
|
import { THDWalletForWatchOnly, TWallet } from '../../class/wallets/types';
|
||||||
import { navigate } from '../../NavigationService';
|
import { navigate } from '../../NavigationService';
|
||||||
import { keepAwake, disallowScreenshot } from 'react-native-screen-capture';
|
import { keepAwake, disallowScreenshot } from 'react-native-screen-capture';
|
||||||
|
import { useSettings } from '../../hooks/context/useSettings';
|
||||||
|
|
||||||
type RouteProps = RouteProp<AddWalletStackParamList, 'ImportWalletDiscovery'>;
|
type RouteProps = RouteProp<AddWalletStackParamList, 'ImportWalletDiscovery'>;
|
||||||
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'ImportWalletDiscovery'>;
|
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'ImportWalletDiscovery'>;
|
||||||
@ -34,6 +35,7 @@ const ImportWalletDiscovery: React.FC = () => {
|
|||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const route = useRoute<RouteProps>();
|
const route = useRoute<RouteProps>();
|
||||||
const { importText, askPassphrase, searchAccounts } = route.params;
|
const { importText, askPassphrase, searchAccounts } = route.params;
|
||||||
|
const { isElectrumDisabled } = useSettings();
|
||||||
const task = useRef<TImport | null>(null);
|
const task = useRef<TImport | null>(null);
|
||||||
const { addAndSaveWallet } = useStorage();
|
const { addAndSaveWallet } = useStorage();
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
@ -67,6 +69,11 @@ const ImportWalletDiscovery: React.FC = () => {
|
|||||||
[addAndSaveWallet],
|
[addAndSaveWallet],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
if (wallets.length === 0) return;
|
||||||
|
saveWallet(wallets[selected].wallet);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onProgress = (data: string) => setProgress(data);
|
const onProgress = (data: string) => setProgress(data);
|
||||||
|
|
||||||
@ -105,7 +112,7 @@ const ImportWalletDiscovery: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
keepAwake(true);
|
keepAwake(true);
|
||||||
task.current = startImport(importText, askPassphrase, searchAccounts, onProgress, onWallet, onPassword);
|
task.current = startImport(importText, askPassphrase, searchAccounts, isElectrumDisabled, onProgress, onWallet, onPassword);
|
||||||
|
|
||||||
task.current.promise
|
task.current.promise
|
||||||
.then(({ cancelled, wallets: w }) => {
|
.then(({ cancelled, wallets: w }) => {
|
||||||
@ -117,6 +124,7 @@ const ImportWalletDiscovery: React.FC = () => {
|
|||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.warn('import error', e);
|
console.warn('import error', e);
|
||||||
|
console.warn('err.stack', e.stack);
|
||||||
presentAlert({ title: 'Import error', message: e.message });
|
presentAlert({ title: 'Import error', message: e.message });
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@ -129,8 +137,9 @@ const ImportWalletDiscovery: React.FC = () => {
|
|||||||
keepAwake(false);
|
keepAwake(false);
|
||||||
task.current?.stop();
|
task.current?.stop();
|
||||||
};
|
};
|
||||||
|
// ignoring "navigation" here, because it is constantly mutating
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, [askPassphrase, importText, isElectrumDisabled, saveWallet, searchAccounts]);
|
||||||
|
|
||||||
const handleCustomDerivation = () => {
|
const handleCustomDerivation = () => {
|
||||||
task.current?.stop();
|
task.current?.stop();
|
||||||
@ -157,16 +166,21 @@ const ImportWalletDiscovery: React.FC = () => {
|
|||||||
const ListHeaderComponent = useMemo(
|
const ListHeaderComponent = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<>
|
<>
|
||||||
{wallets && wallets.length > 0 ? (
|
{wallets.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<BlueSpacing20 />
|
{isElectrumDisabled && (
|
||||||
|
<>
|
||||||
|
<BlueFormLabel>{loc.wallets.import_discovery_offline}</BlueFormLabel>
|
||||||
|
<BlueSpacing20 />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<BlueFormLabel>{loc.wallets.import_discovery_subtitle}</BlueFormLabel>
|
<BlueFormLabel>{loc.wallets.import_discovery_subtitle}</BlueFormLabel>
|
||||||
<BlueSpacing10 />
|
<BlueSpacing10 />
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[wallets],
|
[wallets, isElectrumDisabled],
|
||||||
);
|
);
|
||||||
|
|
||||||
const ListEmptyComponent = useMemo(
|
const ListEmptyComponent = useMemo(
|
||||||
@ -213,14 +227,7 @@ const ImportWalletDiscovery: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
<BlueSpacing10 />
|
<BlueSpacing10 />
|
||||||
<View style={styles.buttonContainer}>
|
<View style={styles.buttonContainer}>
|
||||||
<Button
|
<Button disabled={wallets?.length === 0} title={loc.wallets.import_do_import} onPress={handleSave} />
|
||||||
disabled={wallets?.length === 0}
|
|
||||||
title={loc.wallets.import_do_import}
|
|
||||||
onPress={() => {
|
|
||||||
if (wallets.length === 0) return;
|
|
||||||
saveWallet(wallets[selected].wallet);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</SafeArea>
|
</SafeArea>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||||
import {
|
import {
|
||||||
@ -17,7 +18,7 @@ import {
|
|||||||
WatchOnlyWallet,
|
WatchOnlyWallet,
|
||||||
} from '../../class';
|
} from '../../class';
|
||||||
import startImport from '../../class/wallet-import';
|
import startImport from '../../class/wallet-import';
|
||||||
const fs = require('fs');
|
import { TWallet } from '../../class/wallets/types';
|
||||||
|
|
||||||
jest.setTimeout(90 * 1000);
|
jest.setTimeout(90 * 1000);
|
||||||
|
|
||||||
@ -32,31 +33,37 @@ beforeAll(async () => {
|
|||||||
await BlueElectrum.connectMain();
|
await BlueElectrum.connectMain();
|
||||||
});
|
});
|
||||||
|
|
||||||
const createStore = password => {
|
type THistoryItem = { action: 'progress'; data: string } | { action: 'wallet'; data: TWallet } | { action: 'password'; data: string };
|
||||||
const state = { wallets: [] };
|
type TState = { wallets: TWallet[]; progress?: string; password?: string };
|
||||||
const history = [];
|
type TOnProgress = (name: string) => void;
|
||||||
|
type TOnWallet = (wallet: TWallet) => void;
|
||||||
|
type TOnPassword = (title: string, text: string) => Promise<string>;
|
||||||
|
|
||||||
const onProgress = data => {
|
const createStore = (password?: string) => {
|
||||||
|
const state: TState = { wallets: [] };
|
||||||
|
const history: THistoryItem[] = [];
|
||||||
|
|
||||||
|
const onProgress: TOnProgress = data => {
|
||||||
history.push({ action: 'progress', data });
|
history.push({ action: 'progress', data });
|
||||||
state.progress = data;
|
state.progress = data;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWallet = data => {
|
const onWallet: TOnWallet = data => {
|
||||||
history.push({ action: 'wallet', data });
|
history.push({ action: 'wallet', data });
|
||||||
state.wallets.push(data);
|
state.wallets.push(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPassword = () => {
|
const onPassword: TOnPassword = async () => {
|
||||||
history.push({ action: 'password', data: password });
|
history.push({ action: 'password', data: password! });
|
||||||
state.password = password;
|
state.password = password;
|
||||||
return password;
|
return password!;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
history,
|
history,
|
||||||
callbacks: [onProgress, onWallet, onPassword],
|
callbacks: [onProgress, onWallet, onPassword],
|
||||||
};
|
} as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('import procedure', () => {
|
describe('import procedure', () => {
|
||||||
@ -69,8 +76,9 @@ describe('import procedure', () => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
|
// @ts-ignore: oopsie
|
||||||
store.callbacks[2] = onPassword;
|
store.callbacks[2] = onPassword;
|
||||||
const { promise } = startImport('6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN', false, false, ...store.callbacks);
|
const { promise } = startImport('6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN', false, false, false, ...store.callbacks);
|
||||||
const imprt = await promise;
|
const imprt = await promise;
|
||||||
assert.strictEqual(store.state.wallets.length, 0);
|
assert.strictEqual(store.state.wallets.length, 0);
|
||||||
assert.strictEqual(imprt.cancelled, true);
|
assert.strictEqual(imprt.cancelled, true);
|
||||||
@ -78,7 +86,7 @@ describe('import procedure', () => {
|
|||||||
|
|
||||||
it('can be stopped', async () => {
|
it('can be stopped', async () => {
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
const { promise, stop } = startImport('KztVRmc2EJJBHi599mCdXrxMTsNsGy3NUjc3Fb3FFDSMYyMDRjnv', false, false, ...store.callbacks);
|
const { promise, stop } = startImport('KztVRmc2EJJBHi599mCdXrxMTsNsGy3NUjc3Fb3FFDSMYyMDRjnv', false, false, false, ...store.callbacks);
|
||||||
stop();
|
stop();
|
||||||
await assert.doesNotReject(async () => await promise);
|
await assert.doesNotReject(async () => await promise);
|
||||||
const imprt = await promise;
|
const imprt = await promise;
|
||||||
@ -91,18 +99,33 @@ describe('import procedure', () => {
|
|||||||
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets.length > 3, true);
|
assert.strictEqual(store.state.wallets.length > 3, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can import multiple wallets in offline mode', async () => {
|
||||||
|
const store = createStore();
|
||||||
|
const { promise } = startImport(
|
||||||
|
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
...store.callbacks,
|
||||||
|
);
|
||||||
|
await promise;
|
||||||
|
assert.strictEqual(store.state.wallets.length > 100, true);
|
||||||
|
});
|
||||||
|
|
||||||
it('can import BIP84', async () => {
|
it('can import BIP84', async () => {
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
const { promise } = startImport(
|
const { promise } = startImport(
|
||||||
'always direct find escape liar turn differ shy tool gap elder galaxy lawn wild movie fog moon spread casual inner box diagram outdoor tell',
|
'always direct find escape liar turn differ shy tool gap elder galaxy lawn wild movie fog moon spread casual inner box diagram outdoor tell',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -116,6 +139,7 @@ describe('import procedure', () => {
|
|||||||
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -125,7 +149,7 @@ describe('import procedure', () => {
|
|||||||
|
|
||||||
it('can import Legacy', async () => {
|
it('can import Legacy', async () => {
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
const { promise } = startImport('KztVRmc2EJJBHi599mCdXrxMTsNsGy3NUjc3Fb3FFDSMYyMDRjnv', false, false, ...store.callbacks);
|
const { promise } = startImport('KztVRmc2EJJBHi599mCdXrxMTsNsGy3NUjc3Fb3FFDSMYyMDRjnv', false, false, false, ...store.callbacks);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets[0].type, LegacyWallet.type);
|
assert.strictEqual(store.state.wallets[0].type, LegacyWallet.type);
|
||||||
assert.strictEqual(store.state.wallets[0].getAddress(), '1AhcdMCzby4VXgqrexuMfh7eiSprRFtN78');
|
assert.strictEqual(store.state.wallets[0].getAddress(), '1AhcdMCzby4VXgqrexuMfh7eiSprRFtN78');
|
||||||
@ -133,7 +157,7 @@ describe('import procedure', () => {
|
|||||||
|
|
||||||
it('can import P2SH Segwit', async () => {
|
it('can import P2SH Segwit', async () => {
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
const { promise } = startImport('L3NxFnYoBGjJ5PhxrxV6jorvjnc8cerYJx71vXU6ta8BXQxHVZya', false, false, ...store.callbacks);
|
const { promise } = startImport('L3NxFnYoBGjJ5PhxrxV6jorvjnc8cerYJx71vXU6ta8BXQxHVZya', false, false, false, ...store.callbacks);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets[0].type, SegwitP2SHWallet.type);
|
assert.strictEqual(store.state.wallets[0].type, SegwitP2SHWallet.type);
|
||||||
assert.strictEqual(store.state.wallets[0].getAddress(), '3KM9VfdsDf9uT7uwZagoKgVn8z35m9CtSM');
|
assert.strictEqual(store.state.wallets[0].getAddress(), '3KM9VfdsDf9uT7uwZagoKgVn8z35m9CtSM');
|
||||||
@ -143,7 +167,7 @@ describe('import procedure', () => {
|
|||||||
|
|
||||||
it('can import Bech32 Segwit', async () => {
|
it('can import Bech32 Segwit', async () => {
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
const { promise } = startImport('L1T6FfKpKHi8JE6eBKrsXkenw34d5FfFzJUZ6dLs2utxkSvsDfxZ', false, false, ...store.callbacks);
|
const { promise } = startImport('L1T6FfKpKHi8JE6eBKrsXkenw34d5FfFzJUZ6dLs2utxkSvsDfxZ', false, false, false, ...store.callbacks);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets[0].type, SegwitBech32Wallet.type);
|
assert.strictEqual(store.state.wallets[0].type, SegwitBech32Wallet.type);
|
||||||
assert.strictEqual(store.state.wallets[0].getAddress(), 'bc1q763rf54hzuncmf8dtlz558uqe4f247mq39rjvr');
|
assert.strictEqual(store.state.wallets[0].getAddress(), 'bc1q763rf54hzuncmf8dtlz558uqe4f247mq39rjvr');
|
||||||
@ -153,7 +177,7 @@ describe('import procedure', () => {
|
|||||||
|
|
||||||
it('can import Legacy/P2SH/Bech32 from an empty wallet', async () => {
|
it('can import Legacy/P2SH/Bech32 from an empty wallet', async () => {
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
const { promise } = startImport('L36mabzoQyMZoHHsBFVNB7PUBXgXTynwY6yR7kYZ82EkS7oejVp2', false, false, ...store.callbacks);
|
const { promise } = startImport('L36mabzoQyMZoHHsBFVNB7PUBXgXTynwY6yR7kYZ82EkS7oejVp2', false, false, false, ...store.callbacks);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets[0].type, SegwitBech32Wallet.type);
|
assert.strictEqual(store.state.wallets[0].type, SegwitBech32Wallet.type);
|
||||||
assert.strictEqual(store.state.wallets[0].getAddress(), 'bc1q8dkdgpaq9sd2xwptsjhe7krwp0k595w0hdtkfr');
|
assert.strictEqual(store.state.wallets[0].getAddress(), 'bc1q8dkdgpaq9sd2xwptsjhe7krwp0k595w0hdtkfr');
|
||||||
@ -169,6 +193,7 @@ describe('import procedure', () => {
|
|||||||
'sting museum endless duty nice riot because swallow brother depth weapon merge woman wish hold finish venture gauge stomach bomb device bracket agent parent',
|
'sting museum endless duty nice riot because swallow brother depth weapon merge woman wish hold finish venture gauge stomach bomb device bracket agent parent',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -182,6 +207,7 @@ describe('import procedure', () => {
|
|||||||
'abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abeille',
|
'abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abaisser abeille',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -195,6 +221,7 @@ describe('import procedure', () => {
|
|||||||
'believe torch sport lizard absurd retreat scale layer song pen clump combine window staff dream filter latin bicycle vapor anchor put clean gain slush',
|
'believe torch sport lizard absurd retreat scale layer song pen clump combine window staff dream filter latin bicycle vapor anchor put clean gain slush',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -208,6 +235,7 @@ describe('import procedure', () => {
|
|||||||
'eight derive blast guide smoke piece coral burden lottery flower tomato flame',
|
'eight derive blast guide smoke piece coral burden lottery flower tomato flame',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -221,6 +249,7 @@ describe('import procedure', () => {
|
|||||||
'receive happy wash prosper update pet neck acid try profit proud hungry',
|
'receive happy wash prosper update pet neck acid try profit proud hungry',
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -234,6 +263,7 @@ describe('import procedure', () => {
|
|||||||
'become salmon motor battle sweet merit romance ecology age squirrel oblige awesome',
|
'become salmon motor battle sweet merit romance ecology age squirrel oblige awesome',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -248,6 +278,7 @@ describe('import procedure', () => {
|
|||||||
'noble mimic pipe merry knife screen enter dune crop bonus slice card',
|
'noble mimic pipe merry knife screen enter dune crop bonus slice card',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -262,6 +293,7 @@ describe('import procedure', () => {
|
|||||||
'bitter grass shiver impose acquire brush forget axis eager alone wine silver',
|
'bitter grass shiver impose acquire brush forget axis eager alone wine silver',
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -275,6 +307,7 @@ describe('import procedure', () => {
|
|||||||
'abstract rhythm weird food attract treat mosquito sight royal actor surround ride strike remove guilt catch filter summer mushroom protect poverty cruel chaos pattern',
|
'abstract rhythm weird food attract treat mosquito sight royal actor surround ride strike remove guilt catch filter summer mushroom protect poverty cruel chaos pattern',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -287,6 +320,7 @@ describe('import procedure', () => {
|
|||||||
'able mix price funny host express lawsuit congress antique float pig exchange vapor drip wide cup style apple tumble verb fix blush tongue market',
|
'able mix price funny host express lawsuit congress antique float pig exchange vapor drip wide cup style apple tumble verb fix blush tongue market',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -297,14 +331,14 @@ describe('import procedure', () => {
|
|||||||
const store = createStore();
|
const store = createStore();
|
||||||
const tempWallet = new HDSegwitBech32Wallet();
|
const tempWallet = new HDSegwitBech32Wallet();
|
||||||
await tempWallet.generate();
|
await tempWallet.generate();
|
||||||
const { promise } = startImport(tempWallet.getSecret(), false, false, ...store.callbacks);
|
const { promise } = startImport(tempWallet.getSecret(), false, false, false, ...store.callbacks);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets[0].type, HDSegwitBech32Wallet.type);
|
assert.strictEqual(store.state.wallets[0].type, HDSegwitBech32Wallet.type);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can import Legacy with uncompressed pubkey', async () => {
|
it('can import Legacy with uncompressed pubkey', async () => {
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
const { promise } = startImport('5KE6tf9vhYkzYSbgEL6M7xvkY69GMFHF3WxzYaCFMvwMxn3QgRS', false, false, ...store.callbacks);
|
const { promise } = startImport('5KE6tf9vhYkzYSbgEL6M7xvkY69GMFHF3WxzYaCFMvwMxn3QgRS', false, false, false, ...store.callbacks);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets[0].getSecret(), '5KE6tf9vhYkzYSbgEL6M7xvkY69GMFHF3WxzYaCFMvwMxn3QgRS');
|
assert.strictEqual(store.state.wallets[0].getSecret(), '5KE6tf9vhYkzYSbgEL6M7xvkY69GMFHF3WxzYaCFMvwMxn3QgRS');
|
||||||
assert.strictEqual(store.state.wallets[0].type, LegacyWallet.type);
|
assert.strictEqual(store.state.wallets[0].type, LegacyWallet.type);
|
||||||
@ -313,7 +347,7 @@ describe('import procedure', () => {
|
|||||||
|
|
||||||
it('can import BIP38 encrypted backup', async () => {
|
it('can import BIP38 encrypted backup', async () => {
|
||||||
const store = createStore('qwerty');
|
const store = createStore('qwerty');
|
||||||
const { promise } = startImport('6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN', false, false, ...store.callbacks);
|
const { promise } = startImport('6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN', false, false, false, ...store.callbacks);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets[0].getSecret(), 'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc');
|
assert.strictEqual(store.state.wallets[0].getSecret(), 'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc');
|
||||||
assert.strictEqual(store.state.wallets[0].type, SegwitBech32Wallet.type);
|
assert.strictEqual(store.state.wallets[0].type, SegwitBech32Wallet.type);
|
||||||
@ -328,17 +362,17 @@ describe('import procedure', () => {
|
|||||||
|
|
||||||
it('can import watch-only address', async () => {
|
it('can import watch-only address', async () => {
|
||||||
const store1 = createStore();
|
const store1 = createStore();
|
||||||
const { promise: promise1 } = startImport('1AhcdMCzby4VXgqrexuMfh7eiSprRFtN78', false, false, ...store1.callbacks);
|
const { promise: promise1 } = startImport('1AhcdMCzby4VXgqrexuMfh7eiSprRFtN78', false, false, false, ...store1.callbacks);
|
||||||
await promise1;
|
await promise1;
|
||||||
assert.strictEqual(store1.state.wallets[0].type, WatchOnlyWallet.type);
|
assert.strictEqual(store1.state.wallets[0].type, WatchOnlyWallet.type);
|
||||||
|
|
||||||
const store2 = createStore();
|
const store2 = createStore();
|
||||||
const { promise: promise2 } = startImport('3EoqYYp7hQSHn5nHqRtWzkgqmK3caQ2SUu', false, false, ...store2.callbacks);
|
const { promise: promise2 } = startImport('3EoqYYp7hQSHn5nHqRtWzkgqmK3caQ2SUu', false, false, false, ...store2.callbacks);
|
||||||
await promise2;
|
await promise2;
|
||||||
assert.strictEqual(store2.state.wallets[0].type, WatchOnlyWallet.type);
|
assert.strictEqual(store2.state.wallets[0].type, WatchOnlyWallet.type);
|
||||||
|
|
||||||
const store3 = createStore();
|
const store3 = createStore();
|
||||||
const { promise: promise3 } = startImport('bc1q8j4lk4qlhun0n7h5ahfslfldc8zhlxgynfpdj2', false, false, ...store3.callbacks);
|
const { promise: promise3 } = startImport('bc1q8j4lk4qlhun0n7h5ahfslfldc8zhlxgynfpdj2', false, false, false, ...store3.callbacks);
|
||||||
await promise3;
|
await promise3;
|
||||||
assert.strictEqual(store3.state.wallets[0].type, WatchOnlyWallet.type);
|
assert.strictEqual(store3.state.wallets[0].type, WatchOnlyWallet.type);
|
||||||
|
|
||||||
@ -347,6 +381,7 @@ describe('import procedure', () => {
|
|||||||
'zpub6r7jhKKm7BAVx3b3nSnuadY1WnshZYkhK8gKFoRLwK9rF3Mzv28BrGcCGA3ugGtawi1WLb2vyjQAX9ZTDGU5gNk2bLdTc3iEXr6tzR1ipNP',
|
'zpub6r7jhKKm7BAVx3b3nSnuadY1WnshZYkhK8gKFoRLwK9rF3Mzv28BrGcCGA3ugGtawi1WLb2vyjQAX9ZTDGU5gNk2bLdTc3iEXr6tzR1ipNP',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store4.callbacks,
|
...store4.callbacks,
|
||||||
);
|
);
|
||||||
await promise4;
|
await promise4;
|
||||||
@ -364,6 +399,7 @@ describe('import procedure', () => {
|
|||||||
'crystal lungs academic agency class payment actress avoid rebound ordinary exchange petition tendency mild mobile spine robin fancy shelter increase',
|
'crystal lungs academic agency class payment actress avoid rebound ordinary exchange petition tendency mild mobile spine robin fancy shelter increase',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -381,6 +417,7 @@ describe('import procedure', () => {
|
|||||||
'crystal lungs academic agency class payment actress avoid rebound ordinary exchange petition tendency mild mobile spine robin fancy shelter increase',
|
'crystal lungs academic agency class payment actress avoid rebound ordinary exchange petition tendency mild mobile spine robin fancy shelter increase',
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -394,6 +431,7 @@ describe('import procedure', () => {
|
|||||||
'{"ExtPubKey":"zpub6riZchHnrWzhhZ3Z4dhCJmesGyafMmZBRC9txhnidR313XJbcv4KiDubderKHhL7rMsqacYd82FQ38e4whgs8Dg7CpsxX3dSGWayXsEerF4","MasterFingerprint":"7D2F0272","AccountKeyPath":"84\'\\/0\'\\/0\'","CoboVaultFirmwareVersion":"2.6.1(BTC-Only)"}',
|
'{"ExtPubKey":"zpub6riZchHnrWzhhZ3Z4dhCJmesGyafMmZBRC9txhnidR313XJbcv4KiDubderKHhL7rMsqacYd82FQ38e4whgs8Dg7CpsxX3dSGWayXsEerF4","MasterFingerprint":"7D2F0272","AccountKeyPath":"84\'\\/0\'\\/0\'","CoboVaultFirmwareVersion":"2.6.1(BTC-Only)"}',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -408,6 +446,7 @@ describe('import procedure', () => {
|
|||||||
`[{"ExtPubKey":"zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs","MasterFingerprint":"73C5DA0A","AccountKeyPath":"m/84'/0'/0'"},{"ExtPubKey":"ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP","MasterFingerprint":"73C5DA0A","AccountKeyPath":"m/49'/0'/0'"},{"ExtPubKey":"xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj","MasterFingerprint":"73C5DA0A","AccountKeyPath":"m/44'/0'/0'"}]`,
|
`[{"ExtPubKey":"zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs","MasterFingerprint":"73C5DA0A","AccountKeyPath":"m/84'/0'/0'"},{"ExtPubKey":"ypub6Ww3ibxVfGzLrAH1PNcjyAWenMTbbAosGNB6VvmSEgytSER9azLDWCxoJwW7Ke7icmizBMXrzBx9979FfaHxHcrArf3zbeJJJUZPf663zsP","MasterFingerprint":"73C5DA0A","AccountKeyPath":"m/49'/0'/0'"},{"ExtPubKey":"xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj","MasterFingerprint":"73C5DA0A","AccountKeyPath":"m/44'/0'/0'"}]`,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -442,6 +481,7 @@ describe('import procedure', () => {
|
|||||||
'{"ExtPubKey":"zpub6qT7amLcp2exr4mU4AhXZMjD9CFkopECVhUxc9LHW8pNsJG2B9ogs5sFbGZpxEeT5TBjLmc7EFYgZA9EeWEM1xkJMFLefzZc8eigRFhKB8Q","MasterFingerprint":"01EBDA7D","AccountKeyPath":"m/84\'/0\'/0\'"}',
|
'{"ExtPubKey":"zpub6qT7amLcp2exr4mU4AhXZMjD9CFkopECVhUxc9LHW8pNsJG2B9ogs5sFbGZpxEeT5TBjLmc7EFYgZA9EeWEM1xkJMFLefzZc8eigRFhKB8Q","MasterFingerprint":"01EBDA7D","AccountKeyPath":"m/84\'/0\'/0\'"}',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -456,6 +496,7 @@ describe('import procedure', () => {
|
|||||||
'trip ener cloc puls hams ghos inha crow inju vibr seve chro',
|
'trip ener cloc puls hams ghos inha crow inju vibr seve chro',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store1.callbacks,
|
...store1.callbacks,
|
||||||
);
|
);
|
||||||
await promise1;
|
await promise1;
|
||||||
@ -470,6 +511,7 @@ describe('import procedure', () => {
|
|||||||
'docu gosp razo chao nort ches nomi fati swam firs deca boy icon virt gap prep seri anch',
|
'docu gosp razo chao nort ches nomi fati swam firs deca boy icon virt gap prep seri anch',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store2.callbacks,
|
...store2.callbacks,
|
||||||
);
|
);
|
||||||
await promise2;
|
await promise2;
|
||||||
@ -484,6 +526,7 @@ describe('import procedure', () => {
|
|||||||
'rece own flig sent tide hood sile bunk deri mana wink belt loud apol mons pill raw gate hurd matc nigh wish todd achi',
|
'rece own flig sent tide hood sile bunk deri mana wink belt loud apol mons pill raw gate hurd matc nigh wish todd achi',
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store3.callbacks,
|
...store3.callbacks,
|
||||||
);
|
);
|
||||||
await promise3;
|
await promise3;
|
||||||
@ -500,7 +543,7 @@ describe('import procedure', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const store = createStore('1');
|
const store = createStore('1');
|
||||||
const { promise } = startImport(process.env.BIP47_HD_MNEMONIC.split(':')[0], true, false, ...store.callbacks);
|
const { promise } = startImport(process.env.BIP47_HD_MNEMONIC.split(':')[0], true, false, false, ...store.callbacks);
|
||||||
await promise;
|
await promise;
|
||||||
assert.strictEqual(store.state.wallets[0].type, HDLegacyP2PKHWallet.type);
|
assert.strictEqual(store.state.wallets[0].type, HDLegacyP2PKHWallet.type);
|
||||||
assert.strictEqual(store.state.wallets[1].type, HDSegwitBech32Wallet.type);
|
assert.strictEqual(store.state.wallets[1].type, HDSegwitBech32Wallet.type);
|
||||||
@ -513,6 +556,7 @@ describe('import procedure', () => {
|
|||||||
fs.readFileSync('tests/unit/fixtures/coldcardmk4/descriptor.txt').toString('utf8'),
|
fs.readFileSync('tests/unit/fixtures/coldcardmk4/descriptor.txt').toString('utf8'),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -530,6 +574,7 @@ describe('import procedure', () => {
|
|||||||
fs.readFileSync('tests/unit/fixtures/coldcardmk4/new-wasabi.json').toString('utf8'),
|
fs.readFileSync('tests/unit/fixtures/coldcardmk4/new-wasabi.json').toString('utf8'),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
||||||
@ -547,6 +592,7 @@ describe('import procedure', () => {
|
|||||||
fs.readFileSync('tests/unit/fixtures/coldcardmk4/sparrow-export.json').toString('utf8'),
|
fs.readFileSync('tests/unit/fixtures/coldcardmk4/sparrow-export.json').toString('utf8'),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
...store.callbacks,
|
...store.callbacks,
|
||||||
);
|
);
|
||||||
await promise;
|
await promise;
|
Loading…
Reference in New Issue
Block a user