mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 21:35:21 +01:00
feat: refactor import wallet to typescript
This commit is contained in:
parent
967b0642f9
commit
1223945cf9
@ -20,43 +20,57 @@ import {
|
||||
SegwitP2SHWallet,
|
||||
WatchOnlyWallet,
|
||||
} from '.';
|
||||
import type { TWallet } from './wallets/types';
|
||||
import loc from '../loc';
|
||||
import bip39WalletFormats from './bip39_wallet_formats.json'; // https://github.com/spesmilo/electrum/blob/master/electrum/bip39_wallet_formats.json
|
||||
import bip39WalletFormatsBlueWallet from './bip39_wallet_formats_bluewallet.json';
|
||||
|
||||
// https://github.com/bitcoinjs/bip32/blob/master/ts-src/bip32.ts#L43
|
||||
export const validateBip32 = path => path.match(/^(m\/)?(\d+'?\/)*\d+'?$/) !== null;
|
||||
export const validateBip32 = (path: string) => path.match(/^(m\/)?(\d+'?\/)*\d+'?$/) !== null;
|
||||
|
||||
type TReturn = {
|
||||
cancelled: boolean;
|
||||
stopped: boolean;
|
||||
wallets: TWallet[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Function that starts wallet search and import process. It has async generator inside, so
|
||||
* that the process can be stoped at any time. It reporst all the progress through callbacks.
|
||||
*
|
||||
* @param askPassphrase {bool} If true import process will call onPassword callback for wallet with optional password.
|
||||
* @param searchAccounts {bool} If true import process will scan for all known derivation path from bip39_wallet_formats.json. If false it will use limited version.
|
||||
* @param askPassphrase {boolean} If true import process will call onPassword callback for wallet with optional password.
|
||||
* @param searchAccounts {boolean} If true import process will scan for all known derivation path from bip39_wallet_formats.json. If false it will use limited version.
|
||||
* @param onProgress {function} Callback to report scanning progress
|
||||
* @param onWallet {function} Callback to report wallet found
|
||||
* @param onPassword {function} Callback to ask for password if needed
|
||||
* @returns {{promise: Promise, stop: function}}
|
||||
*/
|
||||
const startImport = (importTextOrig, askPassphrase = false, searchAccounts = false, onProgress, onWallet, onPassword) => {
|
||||
const startImport = (
|
||||
importTextOrig: string,
|
||||
askPassphrase: boolean = false,
|
||||
searchAccounts: boolean = false,
|
||||
onProgress: (name: string) => void,
|
||||
onWallet: (wallet: TWallet) => void,
|
||||
onPassword: (title: string, text: string) => Promise<string>,
|
||||
): { promise: Promise<TReturn>; stop: () => void } => {
|
||||
// state
|
||||
let promiseResolve;
|
||||
let promiseReject;
|
||||
let promiseResolve: (arg: TReturn) => void;
|
||||
let promiseReject: (reason?: any) => void;
|
||||
let running = true; // if you put it to false, internal generator stops
|
||||
const wallets = [];
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const wallets: TWallet[] = [];
|
||||
const promise = new Promise<TReturn>((resolve, reject) => {
|
||||
promiseResolve = resolve;
|
||||
promiseReject = reject;
|
||||
});
|
||||
|
||||
// actions
|
||||
const reportProgress = name => {
|
||||
const reportProgress = (name: string) => {
|
||||
onProgress(name);
|
||||
};
|
||||
const reportFinish = (cancelled, stopped) => {
|
||||
const reportFinish = (cancelled: boolean = false, stopped: boolean = false) => {
|
||||
promiseResolve({ cancelled, stopped, wallets });
|
||||
};
|
||||
const reportWallet = wallet => {
|
||||
const reportWallet = (wallet: TWallet) => {
|
||||
if (wallets.some(w => w.getID() === wallet.getID())) return; // do not add duplicates
|
||||
wallets.push(wallet);
|
||||
onWallet(wallet);
|
||||
@ -134,7 +148,7 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
}
|
||||
|
||||
// is it bip38 encrypted
|
||||
if (text.startsWith('6P')) {
|
||||
if (text.startsWith('6P') && password) {
|
||||
const decryptedKey = await bip38.decryptAsync(text, password);
|
||||
|
||||
if (decryptedKey) {
|
||||
@ -184,7 +198,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
yield { progress: 'bip39' };
|
||||
const hd2 = new HDSegwitBech32Wallet();
|
||||
hd2.setSecret(text);
|
||||
hd2.setPassphrase(password);
|
||||
if (password) {
|
||||
hd2.setPassphrase(password);
|
||||
}
|
||||
if (hd2.validateMnemonic()) {
|
||||
let walletFound = false;
|
||||
// by default we don't try all the paths and options
|
||||
@ -214,7 +230,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
for (const path of paths) {
|
||||
const wallet = new WalletClass();
|
||||
wallet.setSecret(text);
|
||||
wallet.setPassphrase(password);
|
||||
if (password) {
|
||||
wallet.setPassphrase(password);
|
||||
}
|
||||
wallet.setDerivationPath(path);
|
||||
yield { progress: `bip39 ${i.script_type} ${path}` };
|
||||
if (await wallet.wasEverUsed()) {
|
||||
@ -230,7 +248,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
// to decide which one is it let's compare number of transactions
|
||||
const m0Legacy = new HDLegacyP2PKHWallet();
|
||||
m0Legacy.setSecret(text);
|
||||
m0Legacy.setPassphrase(password);
|
||||
if (password) {
|
||||
m0Legacy.setPassphrase(password);
|
||||
}
|
||||
m0Legacy.setDerivationPath("m/0'");
|
||||
yield { progress: "bip39 p2pkh m/0'" };
|
||||
// BRD doesn't support passphrase and only works with 12 words seeds
|
||||
@ -332,7 +352,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
yield { progress: 'electrum p2wpkh-p2sh' };
|
||||
const el1 = new HDSegwitElectrumSeedP2WPKHWallet();
|
||||
el1.setSecret(text);
|
||||
el1.setPassphrase(password);
|
||||
if (password) {
|
||||
el1.setPassphrase(password);
|
||||
}
|
||||
if (el1.validateMnemonic()) {
|
||||
yield { wallet: el1 }; // not fetching txs or balances, fuck it, yolo, life is too short
|
||||
}
|
||||
@ -341,7 +363,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
yield { progress: 'electrum p2pkh' };
|
||||
const el2 = new HDLegacyElectrumSeedP2PKHWallet();
|
||||
el2.setSecret(text);
|
||||
el2.setPassphrase(password);
|
||||
if (password) {
|
||||
el2.setPassphrase(password);
|
||||
}
|
||||
if (el2.validateMnemonic()) {
|
||||
yield { wallet: el2 }; // not fetching txs or balances, fuck it, yolo, life is too short
|
||||
}
|
||||
@ -350,7 +374,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
yield { progress: 'aezeed' };
|
||||
const aezeed2 = new HDAezeedWallet();
|
||||
aezeed2.setSecret(text);
|
||||
aezeed2.setPassphrase(password);
|
||||
if (password) {
|
||||
aezeed2.setPassphrase(password);
|
||||
}
|
||||
if (await aezeed2.validateMnemonicAsync()) {
|
||||
yield { wallet: aezeed2 }; // not fetching txs or balances, fuck it, yolo, life is too short
|
||||
}
|
||||
@ -364,14 +390,18 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
|
||||
if (s1.validateMnemonic()) {
|
||||
yield { progress: 'SLIP39 p2wpkh-p2sh' };
|
||||
s1.setPassphrase(password);
|
||||
if (password) {
|
||||
s1.setPassphrase(password);
|
||||
}
|
||||
if (await s1.wasEverUsed()) {
|
||||
yield { wallet: s1 };
|
||||
}
|
||||
|
||||
yield { progress: 'SLIP39 p2pkh' };
|
||||
const s2 = new SLIP39LegacyP2PKHWallet();
|
||||
s2.setPassphrase(password);
|
||||
if (password) {
|
||||
s2.setPassphrase(password);
|
||||
}
|
||||
s2.setSecret(text);
|
||||
if (await s2.wasEverUsed()) {
|
||||
yield { wallet: s2 };
|
||||
@ -380,7 +410,9 @@ const startImport = (importTextOrig, askPassphrase = false, searchAccounts = fal
|
||||
yield { progress: 'SLIP39 p2wpkh' };
|
||||
const s3 = new SLIP39SegwitBech32Wallet();
|
||||
s3.setSecret(text);
|
||||
s3.setPassphrase(password);
|
||||
if (password) {
|
||||
s3.setPassphrase(password);
|
||||
}
|
||||
yield { wallet: s3 };
|
||||
}
|
||||
}
|
@ -1,5 +1,20 @@
|
||||
import bitcoin from 'bitcoinjs-lib';
|
||||
import { CoinSelectOutput, CoinSelectReturnInput } from 'coinselect';
|
||||
import { HDAezeedWallet } from './hd-aezeed-wallet';
|
||||
import { HDLegacyBreadwalletWallet } from './hd-legacy-breadwallet-wallet';
|
||||
import { HDLegacyElectrumSeedP2PKHWallet } from './hd-legacy-electrum-seed-p2pkh-wallet';
|
||||
import { HDLegacyP2PKHWallet } from './hd-legacy-p2pkh-wallet';
|
||||
import { HDSegwitBech32Wallet } from './hd-segwit-bech32-wallet';
|
||||
import { HDSegwitElectrumSeedP2WPKHWallet } from './hd-segwit-electrum-seed-p2wpkh-wallet';
|
||||
import { HDSegwitP2SHWallet } from './hd-segwit-p2sh-wallet';
|
||||
import { LegacyWallet } from './legacy-wallet';
|
||||
import { LightningCustodianWallet } from './lightning-custodian-wallet';
|
||||
import { LightningLdkWallet } from './lightning-ldk-wallet';
|
||||
import { MultisigHDWallet } from './multisig-hd-wallet';
|
||||
import { SegwitBech32Wallet } from './segwit-bech32-wallet';
|
||||
import { SegwitP2SHWallet } from './segwit-p2sh-wallet';
|
||||
import { SLIP39LegacyP2PKHWallet, SLIP39SegwitBech32Wallet, SLIP39SegwitP2SHWallet } from './slip39-wallets';
|
||||
import { WatchOnlyWallet } from './watch-only-wallet';
|
||||
|
||||
export type Utxo = {
|
||||
// Returned by BlueElectrum
|
||||
@ -80,3 +95,22 @@ export type Transaction = {
|
||||
received?: number;
|
||||
value?: number;
|
||||
};
|
||||
|
||||
export type TWallet =
|
||||
| HDAezeedWallet
|
||||
| HDLegacyBreadwalletWallet
|
||||
| HDLegacyElectrumSeedP2PKHWallet
|
||||
| HDLegacyP2PKHWallet
|
||||
| HDSegwitBech32Wallet
|
||||
| HDSegwitElectrumSeedP2WPKHWallet
|
||||
| HDSegwitP2SHWallet
|
||||
| LegacyWallet
|
||||
| LightningCustodianWallet
|
||||
| LightningLdkWallet
|
||||
| MultisigHDWallet
|
||||
| SLIP39LegacyP2PKHWallet
|
||||
| SLIP39SegwitBech32Wallet
|
||||
| SLIP39SegwitP2SHWallet
|
||||
| SegwitBech32Wallet
|
||||
| SegwitP2SHWallet
|
||||
| WatchOnlyWallet;
|
||||
|
38
package-lock.json
generated
38
package-lock.json
generated
@ -117,12 +117,14 @@
|
||||
"@react-native/eslint-config": "^0.72.2",
|
||||
"@react-native/metro-config": "^0.73.0",
|
||||
"@tsconfig/react-native": "^3.0.2",
|
||||
"@types/bip38": "^3.1.2",
|
||||
"@types/bs58check": "^2.1.0",
|
||||
"@types/create-hash": "^1.2.2",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/react": "^18.2.16",
|
||||
"@types/react-native": "^0.72.0",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@types/wif": "^2.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.0",
|
||||
"eslint": "^8.45.0",
|
||||
@ -6556,6 +6558,15 @@
|
||||
"@babel/types": "^7.20.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bip38": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/bip38/-/bip38-3.1.2.tgz",
|
||||
"integrity": "sha512-KF5aiS7DUJs2llJJeg1O1Io129PETszfUfDQotJ4VPBXzytpIUmb7n2MHWEdFYRHs2LYoaRivP/aJbTlF56J+Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bn.js": {
|
||||
"version": "4.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
|
||||
@ -6719,6 +6730,15 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
|
||||
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
|
||||
},
|
||||
"node_modules/@types/wif": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.5.tgz",
|
||||
"integrity": "sha512-addYBlYjDxLfJxDUoyTzICnu0u4snCdGJpICIIFk65zGcdjah3twTJq1Fdy+OdeZSRiof2raFtMqSqF9KeqthQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "16.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz",
|
||||
@ -27490,6 +27510,15 @@
|
||||
"@babel/types": "^7.20.7"
|
||||
}
|
||||
},
|
||||
"@types/bip38": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/bip38/-/bip38-3.1.2.tgz",
|
||||
"integrity": "sha512-KF5aiS7DUJs2llJJeg1O1Io129PETszfUfDQotJ4VPBXzytpIUmb7n2MHWEdFYRHs2LYoaRivP/aJbTlF56J+Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/bn.js": {
|
||||
"version": "4.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
|
||||
@ -27655,6 +27684,15 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
|
||||
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
|
||||
},
|
||||
"@types/wif": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.5.tgz",
|
||||
"integrity": "sha512-addYBlYjDxLfJxDUoyTzICnu0u4snCdGJpICIIFk65zGcdjah3twTJq1Fdy+OdeZSRiof2raFtMqSqF9KeqthQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "16.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz",
|
||||
|
@ -13,12 +13,14 @@
|
||||
"@react-native/eslint-config": "^0.72.2",
|
||||
"@react-native/metro-config": "^0.73.0",
|
||||
"@tsconfig/react-native": "^3.0.2",
|
||||
"@types/bip38": "^3.1.2",
|
||||
"@types/bs58check": "^2.1.0",
|
||||
"@types/create-hash": "^1.2.2",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/react": "^18.2.16",
|
||||
"@types/react-native": "^0.72.0",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
"@types/wif": "^2.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.0",
|
||||
"@typescript-eslint/parser": "^6.2.0",
|
||||
"eslint": "^8.45.0",
|
||||
|
Loading…
Reference in New Issue
Block a user