diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42e943d4..5af4d725 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,8 +4,12 @@ on: push: branches: - master + - dev pull_request: +env: + node_version: 16 + jobs: Test: runs-on: ${{ matrix.os }} @@ -16,12 +20,8 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: 16 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} - restore-keys: ${{ runner.os }}-node- + cache: yarn + node-version: ${{ env.node_version }} - run: yarn --frozen-lockfile - run: yarn test --ci --coverage --maxWorkers=2 Lint: @@ -30,11 +30,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: 16 - - uses: actions/cache@v2 - with: - path: '**/node_modules' - key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} - restore-keys: ${{ runner.os }}-node- + cache: yarn + node-version: ${{ env.node_version }} - run: yarn --frozen-lockfile - run: yarn lint diff --git a/package.json b/package.json index 2569eace..f310d3c6 100644 --- a/package.json +++ b/package.json @@ -73,16 +73,16 @@ "@ethereumjs/tx": "3.3.0", "@ledgerhq/hw-app-eth": "6.12.2", "@obsidiansystems/hw-app-avalanche": "0.2.2", - "@openzeppelin/contracts": "4.3.2", + "@openzeppelin/contracts": "4.3.3", "avalanche": "3.8.1", "big.js": "^6.1.1", + "bip32": "^2.0.6", "bip32-path": "^0.4.2", "bip39": "^3.0.4", "bn.js": "5.1.1", "create-hash": "1.2.0", "ethereumjs-util": "^7.0.7", "ethers": "^5.1.4", - "hdkey": "2.0.1", "moment": "^2.29.1", "rollup-plugin-commonjs": "^10.1.0", "sockette": "^2.0.6", diff --git a/src/Asset/Erc20Token.ts b/src/Asset/Erc20Token.ts index 89c65d04..4247dd74 100644 --- a/src/Asset/Erc20Token.ts +++ b/src/Asset/Erc20Token.ts @@ -34,10 +34,15 @@ export default class Erc20Token { //@ts-ignore let contract = new web3.eth.Contract(ERC20Abi.abi, address); + let contractCalls = await Promise.all([ + contract.methods.name().call(), + contract.methods.symbol().call(), + contract.methods.decimals().call(), + ]); // Purify the values for XSS protection - let name = xss(await contract.methods.name().call()); - let symbol = xss(await contract.methods.symbol().call()); - let decimals = parseInt(await contract.methods.decimals().call()); + let name = xss(contractCalls[0]); + let symbol = xss(contractCalls[1]); + let decimals = parseInt(contractCalls[2]); if (!activeNetwork) { throw NO_NETWORK; diff --git a/src/Explorer/index.ts b/src/Explorer/index.ts new file mode 100644 index 00000000..04bca77e --- /dev/null +++ b/src/Explorer/index.ts @@ -0,0 +1 @@ +export * from './utils'; diff --git a/src/Explorer/Explorer.ts b/src/Explorer/utils.ts similarity index 83% rename from src/Explorer/Explorer.ts rename to src/Explorer/utils.ts index 6823f21d..e79ac6fd 100644 --- a/src/Explorer/Explorer.ts +++ b/src/Explorer/utils.ts @@ -1,7 +1,7 @@ import { explorer_api } from '@/Network/network'; import { NO_EXPLORER_API } from '@/errors'; -async function isAddressUsedX(addr: string) { +export async function isAddressUsedX(addr: string) { if (!explorer_api) { throw NO_EXPLORER_API; } @@ -14,7 +14,7 @@ async function isAddressUsedX(addr: string) { else return false; } -async function getAddressDetailX(addr: string) { +export async function getAddressDetailX(addr: string) { if (!explorer_api) { throw NO_EXPLORER_API; } @@ -27,7 +27,7 @@ async function getAddressDetailX(addr: string) { } // Given an array of addresses, checks which chain each address was already used on -async function getAddressChains(addrs: string[]) { +export async function getAddressChains(addrs: string[]) { if (!explorer_api) { throw NO_EXPLORER_API; } @@ -46,5 +46,3 @@ async function getAddressChains(addrs: string[]) { return res.data.addressChains; } - -export { getAddressDetailX, isAddressUsedX, getAddressChains }; diff --git a/src/Network/providers/AVMWebSocketProvider.ts b/src/Network/providers/AVMWebSocketProvider.ts index cf84c11f..6fa9cf1a 100644 --- a/src/Network/providers/AVMWebSocketProvider.ts +++ b/src/Network/providers/AVMWebSocketProvider.ts @@ -87,7 +87,7 @@ export default class AVMWebSocketProvider { * Creates a bloom filter from the addresses of the tracked wallets and subscribes to * transactions on the node. */ - updateFilterAddresses(): void { + updateFilterAddresses() { if (!this.isConnected) { return; } @@ -97,7 +97,7 @@ export default class AVMWebSocketProvider { let addrs = []; for (let i = 0; i < wallets.length; i++) { let w = wallets[i]; - let externalAddrs = w.getExternalAddressesX(); + let externalAddrs = w.getExternalAddressesXSync(); let addrsLen = externalAddrs.length; let startIndex = Math.max(0, addrsLen - FILTER_ADDRESS_SIZE); let addAddrs = externalAddrs.slice(startIndex); diff --git a/src/Wallet/HDWalletAbstract.ts b/src/Wallet/HDWalletAbstract.ts index afcbac8c..517c4bae 100644 --- a/src/Wallet/HDWalletAbstract.ts +++ b/src/Wallet/HDWalletAbstract.ts @@ -1,21 +1,20 @@ import { WalletProvider } from '@/Wallet/Wallet'; import HdScanner from '@/Wallet/HdScanner'; -import HDKey from 'hdkey'; import { UTXOSet as AVMUTXOSet } from 'avalanche/dist/apis/avm/utxos'; import { avalanche } from '@/Network/network'; import { UTXOSet as PlatformUTXOSet } from 'avalanche/dist/apis/platformvm'; import { iHDWalletIndex } from '@/Wallet/types'; import { bintools } from '@/common'; -import { networkEvents } from '@/Network/eventEmitter'; +import * as bip32 from 'bip32'; import { NetworkConfig } from '@/Network'; export abstract class HDWalletAbstract extends WalletProvider { protected internalScan: HdScanner; protected externalScan: HdScanner; - protected accountKey: HDKey; + protected accountKey: bip32.BIP32Interface; public isHdReady = false; - protected constructor(accountKey: HDKey) { + protected constructor(accountKey: bip32.BIP32Interface) { super(); this.internalScan = new HdScanner(accountKey, true); @@ -69,35 +68,67 @@ export abstract class HDWalletAbstract extends WalletProvider { /** * Returns every external X chain address used by the wallet up to now. */ - public getExternalAddressesX(): string[] { - return this.externalScan.getAllAddresses('X'); + public async getExternalAddressesX(): Promise { + return await this.externalScan.getAllAddresses('X'); + } + + /** + * Returns every external X chain address used by the wallet up to now. + */ + public getExternalAddressesXSync(): string[] { + return this.externalScan.getAllAddressesSync('X'); } /** * Returns every internal X chain address used by the wallet up to now. */ - public getInternalAddressesX(): string[] { - return this.internalScan.getAllAddresses('X'); + public async getInternalAddressesX(): Promise { + return await this.internalScan.getAllAddresses('X'); + } + + /** + * Returns every internal X chain address used by the wallet up to now. + */ + public getInternalAddressesXSync(): string[] { + return this.internalScan.getAllAddressesSync('X'); + } + + /** + * Returns every X chain address used by the wallet up to now (internal + external). + */ + public async getAllAddressesX(): Promise { + return [...(await this.getExternalAddressesX()), ...(await this.getInternalAddressesX())]; } /** * Returns every X chain address used by the wallet up to now (internal + external). */ - public getAllAddressesX(): string[] { - return [...this.getExternalAddressesX(), ...this.getInternalAddressesX()]; + public getAllAddressesXSync(): string[] { + return [...this.getExternalAddressesXSync(), ...this.getInternalAddressesXSync()]; } - public getExternalAddressesP(): string[] { + public async getExternalAddressesP(): Promise { return this.externalScan.getAllAddresses('P'); } + public getExternalAddressesPSync(): string[] { + return this.externalScan.getAllAddressesSync('P'); + } + /** * Returns every P chain address used by the wallet up to now. */ - public getAllAddressesP(): string[] { + public getAllAddressesP(): Promise { return this.getExternalAddressesP(); } + /** + * Returns every P chain address used by the wallet up to now. + */ + public getAllAddressesPSync(): string[] { + return this.getExternalAddressesPSync(); + } + /** * Scans the network and initializes internal and external addresses on P and X chains. * - Heavy operation @@ -120,7 +151,7 @@ export abstract class HDWalletAbstract extends WalletProvider { }; } - public async setHdIndices(external: number, internal: number) { + public setHdIndices(external: number, internal: number) { this.externalScan.setIndex(external); this.internalScan.setIndex(internal); diff --git a/src/Wallet/HdScanner.ts b/src/Wallet/HdScanner.ts index 6212d181..8da19f3b 100644 --- a/src/Wallet/HdScanner.ts +++ b/src/Wallet/HdScanner.ts @@ -1,17 +1,24 @@ -import HDKey from 'hdkey'; +import * as bip32 from 'bip32'; import { getPreferredHRP } from 'avalanche/dist/utils'; import { activeNetwork, avalanche, pChain, xChain } from '@/Network/network'; import { KeyPair as AVMKeyPair, KeyChain as AVMKeyChain } from 'avalanche/dist/apis/avm/keychain'; import { KeyChain as PlatformKeyChain, KeyPair as PlatformKeyPair } from 'avalanche/dist/apis/platformvm'; import { HdChainType } from './types'; import { Buffer } from 'avalanche'; -import { INDEX_RANGE, SCAN_RANGE, SCAN_SIZE } from './constants'; -import { getAddressChains } from '../Explorer/Explorer'; +import { + DERIVATION_SLEEP_INTERVAL, + HD_SCAN_GAP_SIZE, + HD_SCAN_LOOK_UP_WINDOW, + SCAN_RANGE, + SCAN_SIZE, +} from './constants'; +import { getAddressChains } from '@/Explorer'; import { NO_NETWORK } from '@/errors'; import { bintools } from '@/common'; +import { sleep } from '@/utils'; type AddressCache = { - [index: string]: HDKey; + [index: string]: bip32.BIP32Interface; }; type KeyCacheX = { @@ -29,11 +36,15 @@ export default class HdScanner { protected keyCacheX: KeyCacheX = {}; protected keyCacheP: KeyCacheP = {}; readonly changePath: string; - readonly accountKey: HDKey; + private avmAddrFactory: AVMKeyPair; + readonly accountKey: bip32.BIP32Interface; - constructor(accountKey: HDKey, isInternal = true) { + constructor(accountKey: bip32.BIP32Interface, isInternal = true) { this.changePath = isInternal ? '1' : '0'; this.accountKey = accountKey; + // We need an instance of an AVM key to generate adddresses from public keys + let hrp = getPreferredHRP(avalanche.getNetworkID()); + this.avmAddrFactory = new AVMKeyPair(hrp, 'X'); } getIndex() { @@ -58,19 +69,53 @@ export default class HdScanner { return this.getAddressForIndex(this.index, 'P'); } - public getAllAddresses(chainId: HdChainType = 'X'): string[] { + /** + * Returns every address up to and including the current index. + * @param chainId Either X or P + */ + public async getAllAddresses(chainId: HdChainType = 'X'): Promise { + let upTo = this.index; + return await this.getAddressesInRange(0, upTo + 1, chainId); + } + + /** + * Returns every address up to and including the current index synchronously. + * @param chainId Either X or P + */ + public getAllAddressesSync(chainId: HdChainType = 'X'): string[] { let upTo = this.index; - let addrs = []; - for (let i = 0; i <= upTo; i++) { - addrs.push(this.getAddressForIndex(i, chainId)); + return this.getAddressesInRangeSync(0, upTo + 1, chainId); + } + + /** + * Returns addresses in the given range + * @param start Start index + * @param end End index, exclusive + * @param chainId `X` or `P` optional, returns X by default + */ + public async getAddressesInRange(start: number, end: number, chainId: HdChainType = 'X'): Promise { + let res = []; + for (let i = start; i < end; i++) { + res.push(this.getAddressForIndex(i, chainId)); + + // Sleep every Nth address to open up the thread + if ((i - start) % DERIVATION_SLEEP_INTERVAL === 0) { + await sleep(0); + } } - return addrs; + return res; } - getAddressesInRange(start: number, end: number): string[] { + /** + * Returns addresses in the given range + * @param start Start index + * @param end End index, exclusive + * @param chainId `X` or `P` optional, returns X by default + */ + public getAddressesInRangeSync(start: number, end: number, chainId: HdChainType = 'X'): string[] { let res = []; for (let i = start; i < end; i++) { - res.push(this.getAddressForIndex(i)); + res.push(this.getAddressForIndex(i, chainId)); } return res; } @@ -98,7 +143,7 @@ export default class HdScanner { if (cache) return cache; let hdKey = this.getHdKeyForIndex(index); - let pkHex = hdKey.privateKey.toString('hex'); + let pkHex = hdKey.privateKey!.toString('hex'); let pkBuf: Buffer = new Buffer(pkHex, 'hex'); @@ -114,7 +159,7 @@ export default class HdScanner { if (cache) return cache; let hdKey = this.getHdKeyForIndex(index); - let pkHex = hdKey.privateKey.toString('hex'); + let pkHex = hdKey.privateKey!.toString('hex'); let pkBuf: Buffer = new Buffer(pkHex, 'hex'); @@ -126,18 +171,18 @@ export default class HdScanner { return keypair; } - private getHdKeyForIndex(index: number): HDKey { - let key: HDKey; + private getHdKeyForIndex(index: number): bip32.BIP32Interface { + let key: bip32.BIP32Interface; if (this.addressCache[index]) { key = this.addressCache[index]; } else { - key = this.accountKey.derive(`m/${this.changePath}/${index}`) as HDKey; + key = this.accountKey.derivePath(`${this.changePath}/${index}`); this.addressCache[index] = key; } return key; } - private getAddressForIndex(index: number, chainId: HdChainType = 'X'): string { + public getAddressForIndex(index: number, chainId: HdChainType = 'X'): string { let key = this.getHdKeyForIndex(index); let publicKey = key.publicKey.toString('hex'); @@ -145,8 +190,7 @@ export default class HdScanner { let hrp = getPreferredHRP(avalanche.getNetworkID()); - let keypair = new AVMKeyPair(hrp, chainId); - let addrBuf = keypair.addressFromPublicKey(publicKeyBuff); + let addrBuf = this.avmAddrFactory.addressFromPublicKey(publicKeyBuff); let addr = bintools.addressToString(hrp, chainId, addrBuf); return addr; @@ -169,15 +213,13 @@ export default class HdScanner { // Scans the address space of this hd path and finds the last used index using the // explorer API. private async findAvailableIndexExplorer(startIndex = 0): Promise { - let upTo = 512; - - let addrs = this.getAddressesInRange(startIndex, startIndex + upTo); + let addrs = await this.getAddressesInRange(startIndex, startIndex + HD_SCAN_LOOK_UP_WINDOW); let addrChains = await getAddressChains(addrs); - for (let i = 0; i < addrs.length - INDEX_RANGE; i++) { + for (let i = 0; i < addrs.length - HD_SCAN_GAP_SIZE; i++) { let gapSize: number = 0; - for (let n = 0; n < INDEX_RANGE; n++) { + for (let n = 0; n < HD_SCAN_GAP_SIZE; n++) { let scanIndex = i + n; let scanAddr = addrs[scanIndex]; @@ -194,12 +236,12 @@ export default class HdScanner { } // If the gap is reached return the index - if (gapSize === INDEX_RANGE) { + if (gapSize === HD_SCAN_GAP_SIZE) { return startIndex + i; } } - return await this.findAvailableIndexExplorer(startIndex + (upTo - INDEX_RANGE)); + return await this.findAvailableIndexExplorer(startIndex + (HD_SCAN_LOOK_UP_WINDOW - HD_SCAN_GAP_SIZE)); } // Uses the node to find last used HD index @@ -219,10 +261,10 @@ export default class HdScanner { let utxoSetX = (await xChain.getUTXOs(addrsX)).utxos; let utxoSetP = (await pChain.getUTXOs(addrsP)).utxos; - // Scan UTXOs of these indexes and try to find a gap of INDEX_RANGE - for (let i: number = 0; i < addrsX.length - INDEX_RANGE; i++) { + // Scan UTXOs of these indexes and try to find a gap of HD_SCAN_GAP_SIZE + for (let i: number = 0; i < addrsX.length - HD_SCAN_GAP_SIZE; i++) { let gapSize: number = 0; - for (let n: number = 0; n < INDEX_RANGE; n++) { + for (let n: number = 0; n < HD_SCAN_GAP_SIZE; n++) { let scanIndex: number = i + n; let addr: string = addrsX[scanIndex]; let addrBuf = bintools.parseAddress(addr, 'X'); @@ -238,7 +280,7 @@ export default class HdScanner { } // If we found a gap of 20, we can return the last fullIndex+1 - if (gapSize === INDEX_RANGE) { + if (gapSize === HD_SCAN_GAP_SIZE) { let targetIndex = start + i; return targetIndex; } diff --git a/src/Wallet/LedgerWallet.ts b/src/Wallet/LedgerWallet.ts index ee3c5707..8cd9914e 100644 --- a/src/Wallet/LedgerWallet.ts +++ b/src/Wallet/LedgerWallet.ts @@ -50,6 +50,7 @@ import createHash from 'create-hash'; //@ts-ignore import bippath from 'bip32-path'; import { bintools } from '@/common'; +import * as bip32 from 'bip32'; export default class LedgerWallet extends HDWalletAbstract { evmWallet: EvmWalletReadonly; @@ -60,7 +61,13 @@ export default class LedgerWallet extends HDWalletAbstract { appAvax: AppAvax; ethApp: Eth; - constructor(avaxAcct: HDKey, evmAcct: HDKey, avaxApp: AppAvax, ethApp: Eth, config: ILedgerAppConfig) { + constructor( + avaxAcct: bip32.BIP32Interface, + evmAcct: HDKey, + avaxApp: AppAvax, + ethApp: Eth, + config: ILedgerAppConfig + ) { super(avaxAcct); this.evmAccount = evmAcct; this.config = config; @@ -98,11 +105,13 @@ export default class LedgerWallet extends HDWalletAbstract { return await LedgerWallet.fromApp(app, eth); } - static async getAvaxAccount(app: AppAvax): Promise { + static async getAvaxAccount(app: AppAvax): Promise { let res = await app.getWalletExtendedPublicKey(AVAX_ACCOUNT_PATH); - let hd = new HDKey(); - hd.publicKey = res.public_key; - hd.chainCode = res.chain_code; + + let pubKey = res.public_key; + let chainCode = res.chain_code; + + let hd = bip32.fromPublicKey(pubKey, chainCode); return hd; } @@ -186,10 +195,10 @@ export default class LedgerWallet extends HDWalletAbstract { // Returns an array of derivation paths that need to sign this transaction // Used with signTransactionHash and signTransactionParsable - getTransactionPaths( + async getTransactionPaths( unsignedTx: UnsignedTx, chainId: ChainIdType - ): { paths: string[]; isAvaxOnly: boolean } { + ): Promise<{ paths: string[]; isAvaxOnly: boolean }> { let tx = unsignedTx.getTransaction(); let txType = tx.getTxType(); @@ -234,7 +243,7 @@ export default class LedgerWallet extends HDWalletAbstract { for (let j = 0; j < addrs.length; j++) { let srcAddr = addrs[j]; - let pathStr = this.getPathFromAddress(srcAddr); // returns change/index + let pathStr = await this.getPathFromAddress(srcAddr); // returns change/index paths.push(pathStr); } @@ -251,7 +260,7 @@ export default class LedgerWallet extends HDWalletAbstract { for (let j = 0; j < addrs.length; j++) { let srcAddr = addrs[j]; - let pathStr = this.getPathFromAddress(srcAddr); // returns change/index + let pathStr = await this.getPathFromAddress(srcAddr); // returns change/index paths.push(pathStr); } @@ -260,10 +269,10 @@ export default class LedgerWallet extends HDWalletAbstract { return { paths, isAvaxOnly }; } - getPathFromAddress(address: string) { - let externalAddrs = this.externalScan.getAllAddresses(); - let internalAddrs = this.internalScan.getAllAddresses(); - let platformAddrs = this.externalScan.getAllAddresses('P'); + async getPathFromAddress(address: string) { + let externalAddrs = await this.externalScan.getAllAddresses(); + let internalAddrs = await this.internalScan.getAllAddresses(); + let platformAddrs = await this.externalScan.getAllAddresses('P'); let extIndex = externalAddrs.indexOf(address); let intIndex = internalAddrs.indexOf(address); @@ -288,7 +297,7 @@ export default class LedgerWallet extends HDWalletAbstract { let chainId: ChainIdType = 'X'; let parseableTxs = ParseableAvmTxEnum; - let { paths, isAvaxOnly } = this.getTransactionPaths(unsignedTx, chainId); + let { paths, isAvaxOnly } = await this.getTransactionPaths(unsignedTx, chainId); // If ledger doesnt support parsing, sign hash let canLedgerParse = this.config.version >= '0.3.1'; @@ -553,7 +562,7 @@ export default class LedgerWallet extends HDWalletAbstract { let chainId: ChainIdType = 'P'; let parseableTxs = ParseablePlatformEnum; - let { paths, isAvaxOnly } = this.getTransactionPaths(unsignedTx, chainId); + let { paths, isAvaxOnly } = await this.getTransactionPaths(unsignedTx, chainId); // If ledger doesnt support parsing, sign hash let canLedgerParse = this.config.version >= '0.3.1'; let isParsableType = txType in parseableTxs && isAvaxOnly; diff --git a/src/Wallet/MnemonicWallet.ts b/src/Wallet/MnemonicWallet.ts index e7bb4094..d4ac9f5c 100644 --- a/src/Wallet/MnemonicWallet.ts +++ b/src/Wallet/MnemonicWallet.ts @@ -1,6 +1,5 @@ import * as bip39 from 'bip39'; -import HDKey from 'hdkey'; -import { AVAX_ACCOUNT_PATH, ETH_ACCOUNT_PATH } from './constants'; +import * as bip32 from 'bip32'; import EvmWallet from './EvmWallet'; import { UnsafeWallet, WalletNameType } from './types'; import { Buffer } from 'avalanche'; @@ -9,12 +8,7 @@ import { Tx as AVMTx, UnsignedTx as AVMUnsignedTx } from 'avalanche/dist/apis/av import { Tx as PlatformTx, UnsignedTx as PlatformUnsignedTx } from 'avalanche/dist/apis/platformvm'; import { KeyPair as AVMKeyPair, KeyChain as AVMKeyChain } from 'avalanche/dist/apis/avm/keychain'; import { KeyChain as PlatformKeyChain } from 'avalanche/dist/apis/platformvm'; -import { - UnsignedTx as EVMUnsignedTx, - Tx as EVMTx, - KeyChain as EVMKeychain, - KeyPair as EVMKeyPair, -} from 'avalanche/dist/apis/evm'; +import { UnsignedTx as EVMUnsignedTx, Tx as EVMTx, KeyPair as EVMKeyPair } from 'avalanche/dist/apis/evm'; import { avalanche } from '@/Network/network'; import { digestMessage } from '@/utils'; import { HDWalletAbstract } from '@/Wallet/HDWalletAbstract'; @@ -27,12 +21,13 @@ export default class MnemonicWallet extends HDWalletAbstract implements UnsafeWa mnemonic: string; accountIndex: number; - private ethAccountKey: HDKey; + private ethAccountKey: bip32.BIP32Interface; constructor(mnemonic: string, account = 0) { let seed: globalThis.Buffer = bip39.mnemonicToSeedSync(mnemonic); - let masterHdKey: HDKey = HDKey.fromMasterSeed(seed); - let accountKey = masterHdKey.derive(getAccountPathAvalanche(account)); + + let masterHdKey = bip32.fromSeed(seed); + let accountKey = masterHdKey.derivePath(getAccountPathAvalanche(account)); super(accountKey); @@ -41,10 +36,10 @@ export default class MnemonicWallet extends HDWalletAbstract implements UnsafeWa throw new Error('Invalid mnemonic phrase.'); } - let ethAccountKey = masterHdKey.derive(getAccountPathEVM(account)); + let ethAccountKey = masterHdKey.derivePath(getAccountPathEVM(account)); this.ethAccountKey = ethAccountKey; let ethKey = ethAccountKey.privateKey; - let evmWallet = new EvmWallet(ethKey); + let evmWallet = new EvmWallet(ethKey!); this.accountIndex = account; this.mnemonic = mnemonic; diff --git a/src/Wallet/PublicMnemonicWallet.ts b/src/Wallet/PublicMnemonicWallet.ts index 58779fb9..a2d40784 100644 --- a/src/Wallet/PublicMnemonicWallet.ts +++ b/src/Wallet/PublicMnemonicWallet.ts @@ -6,7 +6,7 @@ import { Transaction } from '@ethereumjs/tx'; import { WalletNameType } from '@/Wallet/types'; import EvmWallet from '@/Wallet/EvmWallet'; import EvmWalletReadonly from '@/Wallet/EvmWalletReadonly'; -import HDKey from 'hdkey'; +import * as bip32 from 'bip32'; import { importPublic } from 'ethereumjs-util'; export default class PublicMnemonicWallet extends HDWalletAbstract { @@ -16,8 +16,8 @@ export default class PublicMnemonicWallet extends HDWalletAbstract { * @param xpubEVM of derivation path m/44'/60'/0' */ constructor(xpubAVM: string, xpubEVM: string) { - let avmAcct = HDKey.fromExtendedKey(xpubAVM); - let evmAcct = HDKey.fromExtendedKey(xpubEVM).derive('m/0/0'); + let avmAcct = bip32.fromBase58(xpubAVM); + let evmAcct = bip32.fromBase58(xpubEVM).derivePath('0/0'); super(avmAcct); this.type = 'xpub'; diff --git a/src/Wallet/SingletonWallet.ts b/src/Wallet/SingletonWallet.ts index feecbdac..7ca9fc9d 100644 --- a/src/Wallet/SingletonWallet.ts +++ b/src/Wallet/SingletonWallet.ts @@ -82,11 +82,19 @@ export default class SingletonWallet extends WalletProvider implements UnsafeWal return keyChain.getAddressStrings()[0]; } - getAllAddressesP(): string[] { + async getAllAddressesP(): Promise { return [this.getAddressP()]; } - getAllAddressesX(): string[] { + getAllAddressesPSync(): string[] { + return [this.getAddressP()]; + } + + async getAllAddressesX(): Promise { + return [this.getAddressX()]; + } + + getAllAddressesXSync(): string[] { return [this.getAddressX()]; } @@ -100,15 +108,27 @@ export default class SingletonWallet extends WalletProvider implements UnsafeWal return keypair.getAddressString(); } - getExternalAddressesP(): string[] { + async getExternalAddressesP(): Promise { return [this.getAddressP()]; } - getExternalAddressesX(): string[] { + getExternalAddressesPSync(): string[] { + return [this.getAddressP()]; + } + + async getExternalAddressesX(): Promise { + return [this.getAddressX()]; + } + + getExternalAddressesXSync(): string[] { + return [this.getAddressX()]; + } + + async getInternalAddressesX(): Promise { return [this.getAddressX()]; } - getInternalAddressesX(): string[] { + getInternalAddressesXSync(): string[] { return [this.getAddressX()]; } diff --git a/src/Wallet/Wallet.ts b/src/Wallet/Wallet.ts index 60eae7de..ebcaebe2 100644 --- a/src/Wallet/Wallet.ts +++ b/src/Wallet/Wallet.ts @@ -115,12 +115,17 @@ export abstract class WalletProvider { abstract getAddressC(): string; abstract getEvmAddressBech(): string; - abstract getExternalAddressesX(): string[]; - abstract getInternalAddressesX(): string[]; - abstract getExternalAddressesP(): string[]; - - abstract getAllAddressesX(): string[]; - abstract getAllAddressesP(): string[]; + abstract getExternalAddressesX(): Promise; + abstract getExternalAddressesXSync(): string[]; + abstract getInternalAddressesX(): Promise; + abstract getInternalAddressesXSync(): string[]; + abstract getExternalAddressesP(): Promise; + abstract getExternalAddressesPSync(): string[]; + + abstract getAllAddressesX(): Promise; + abstract getAllAddressesXSync(): string[]; + abstract getAllAddressesP(): Promise; + abstract getAllAddressesPSync(): string[]; protected constructor() { networkEvents.on('network_change', this.onNetworkChange.bind(this)); @@ -194,7 +199,7 @@ export abstract class WalletProvider { let memoBuff = memo ? Buffer.from(memo) : undefined; - let froms = this.getAllAddressesX(); + let froms = await this.getAllAddressesX(); let changeAddress = this.getChangeAddressX(); let utxoSet = this.utxosX; @@ -242,7 +247,7 @@ export abstract class WalletProvider { */ async sendANT(assetID: string, amount: BN, to: string): Promise { let utxoSet = this.getUtxosX(); - let fromAddrs = this.getAllAddressesX(); + let fromAddrs = await this.getAllAddressesX(); let changeAddr = this.getChangeAddressX(); let tx = await xChain.buildBaseTx(utxoSet, amount, assetID, [to], fromAddrs, [changeAddr]); @@ -411,8 +416,7 @@ export abstract class WalletProvider { * - Calls `this.updateBalanceX()` after success. * */ public async updateUtxosX(): Promise { - const addresses = this.getAllAddressesX(); - // let oldUtxos = this.utxosX; + const addresses = await this.getAllAddressesX(); this.utxosX = await avmGetAllUTXOs(addresses); await this.updateUnknownAssetsX(); @@ -434,7 +438,7 @@ export abstract class WalletProvider { * - Updates `this.utxosP` with the new UTXOs */ public async updateUtxosP(): Promise { - let addresses = this.getAllAddressesP(); + let addresses = await this.getAllAddressesP(); this.utxosP = await platformGetAllUTXOs(addresses); this.emitBalanceChangeP(); @@ -453,7 +457,7 @@ export abstract class WalletProvider { * Returns the number AVAX staked by this wallet. */ public async getStake(): Promise { - let addrs = this.getAllAddressesP(); + let addrs = await this.getAllAddressesP(); return await getStakeForAddresses(addrs); } @@ -663,7 +667,7 @@ export abstract class WalletProvider { let destinationAddr = this.getAddressX(); let pChangeAddr = this.getAddressP(); - let fromAddrs = this.getAllAddressesP(); + let fromAddrs = await this.getAllAddressesP(); let xId = xChain.getBlockchainID(); @@ -729,7 +733,7 @@ export abstract class WalletProvider { destinationAddr = this.getEvmAddressBech(); } - let fromAddresses = this.getAllAddressesX(); + let fromAddresses = await this.getAllAddressesX(); let changeAddress = this.getChangeAddressX(); let utxos = this.utxosX; let exportTx = (await buildAvmExportTransaction( @@ -753,13 +757,13 @@ export abstract class WalletProvider { } async getAtomicUTXOsX(chainID: AvmImportChainType) { - let addrs = this.getAllAddressesX(); + let addrs = await this.getAllAddressesX(); let result = await avmGetAtomicUTXOs(addrs, chainID); return result; } async getAtomicUTXOsP(): Promise { - let addrs = this.getAllAddressesP(); + let addrs = await this.getAllAddressesP(); return await platformGetAtomicUTXOs(addrs); } @@ -872,7 +876,7 @@ export abstract class WalletProvider { } async createNftFamily(name: string, symbol: string, groupNum: number) { - let fromAddresses = this.getAllAddressesX(); + let fromAddresses = await this.getAllAddressesX(); let changeAddress = this.getChangeAddressX(); let minterAddress = this.getAddressX(); @@ -898,7 +902,7 @@ export abstract class WalletProvider { let ownerAddress = this.getAddressX(); let changeAddress = this.getChangeAddressX(); - let sourceAddresses = this.getAllAddressesX(); + let sourceAddresses = await this.getAllAddressesX(); let utxoSet = this.utxosX; let tx = await buildMintNftTx( @@ -945,8 +949,7 @@ export abstract class WalletProvider { utxoSet.addArray(utxos); } - let pAddressStrings = this.getAllAddressesP(); - // let pAddressStrings = this.platformHelper.getAllDerivedAddresses() + let pAddressStrings = await this.getAllAddressesP(); let stakeAmount = amt; @@ -995,7 +998,7 @@ export abstract class WalletProvider { utxos?: PlatformUTXO[] ): Promise { let utxoSet = this.utxosP; - let pAddressStrings = this.getAllAddressesP(); + let pAddressStrings = await this.getAllAddressesP(); let stakeAmount = amt; @@ -1071,17 +1074,17 @@ export abstract class WalletProvider { } async getHistoryX(limit = 0): Promise { - let addrs = this.getAllAddressesX(); + let addrs = await this.getAllAddressesX(); return await getAddressHistory(addrs, limit, xChain.getBlockchainID()); } async getHistoryP(limit = 0): Promise { - let addrs = this.getAllAddressesP(); + let addrs = await this.getAllAddressesP(); return await getAddressHistory(addrs, limit, pChain.getBlockchainID()); } async getHistoryC(limit = 0): Promise { - let addrs = [this.getEvmAddressBech(), ...this.getAllAddressesX()]; + let addrs = [this.getEvmAddressBech(), ...(await this.getAllAddressesX())]; return await getAddressHistory(addrs, limit, cChain.getBlockchainID()); } @@ -1101,7 +1104,7 @@ export abstract class WalletProvider { let txsEVM = await this.getHistoryEVM(); - let addrsX = this.getAllAddressesX(); + let addrsX = await this.getAllAddressesX(); let addrBechC = this.getEvmAddressBech(); let addrs = [...addrsX, addrBechC]; @@ -1139,7 +1142,7 @@ export abstract class WalletProvider { * @param txId */ async getHistoryTx(txId: string): Promise { - let addrs = this.getAllAddressesX(); + let addrs = await this.getAllAddressesX(); let addrC = this.getAddressC(); let rawData = await getTx(txId); diff --git a/src/Wallet/constants.ts b/src/Wallet/constants.ts index b40cc409..0b338f13 100644 --- a/src/Wallet/constants.ts +++ b/src/Wallet/constants.ts @@ -8,9 +8,14 @@ export const AVAX_ACCOUNT_PATH: string = `m/44'/${AVAX_TOKEN_INDEX}'/0'`; // Cha export const ETH_ACCOUNT_PATH: string = `m/44'/60'/0'`; export const LEDGER_ETH_ACCOUNT_PATH = ETH_ACCOUNT_PATH + '/0/0'; -export const INDEX_RANGE: number = 20; // a gap of at least 20 indexes is needed to claim an index unused +export const HD_SCAN_GAP_SIZE: number = 20; // a gap of at least 20 indexes is needed to claim an index unused export const SCAN_SIZE: number = 70; // the total number of utxos to look at initially to calculate last index -export const SCAN_RANGE: number = SCAN_SIZE - INDEX_RANGE; // How many items are actually scanned +export const HD_SCAN_LOOK_UP_WINDOW: number = 64; // Number of addresses to check with the explorer at a single call +export const SCAN_RANGE: number = SCAN_SIZE - HD_SCAN_GAP_SIZE; // How many items are actually scanned export const LEDGER_EXCHANGE_TIMEOUT = 90_000; export const MIN_EVM_SUPPORT_V = '0.5.3'; +/** + * In order to free the thread when deriving addresses, the execution will sleep every N address derived + */ +export const DERIVATION_SLEEP_INTERVAL = 200; diff --git a/src/helpers/utxo_helper.ts b/src/helpers/utxo_helper.ts index 3e53b481..5cbe8382 100644 --- a/src/helpers/utxo_helper.ts +++ b/src/helpers/utxo_helper.ts @@ -98,7 +98,6 @@ export async function avmGetAllUTXOsForAddresses(addrs: string[], endIndex?: any } let utxoSet = response.utxos; - let utxos = utxoSet.getAllUTXOs(); let nextEndIndex = response.endIndex; let len = response.numFetched; diff --git a/src/utils/index.ts b/src/utils/index.ts index 14dbb9cb..a9cb5675 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -3,3 +3,4 @@ export * from './number_utils'; export * from './fee_utils'; export * from './price_utils'; export * from './waitTxUtils'; +export * from './sleep'; diff --git a/src/utils/sleep.ts b/src/utils/sleep.ts new file mode 100644 index 00000000..c561b3e1 --- /dev/null +++ b/src/utils/sleep.ts @@ -0,0 +1,5 @@ +export async function sleep(durMs: number) { + return new Promise((resolve) => { + setTimeout(resolve, durMs); + }); +} diff --git a/test/Wallet/HdScanner.test.ts b/test/Wallet/HdScanner.test.ts new file mode 100644 index 00000000..2450619c --- /dev/null +++ b/test/Wallet/HdScanner.test.ts @@ -0,0 +1,125 @@ +import * as bip39 from 'bip39'; +import * as bip32 from 'bip32'; +import { getAccountPathAvalanche } from '@/Wallet/helpers/derivationHelper'; +import { TEST_MNEMONIC, TEST_MNEMONIC_ADDRS_EXT, TEST_MNEMONIC_ADDRS_INT } from './constants'; +import HdScanner from '@/Wallet/HdScanner'; +import { activeNetwork, avalanche, explorer_api } from '@/Network/network'; +import { getAddressChains } from '@/Explorer'; +import { HD_SCAN_GAP_SIZE } from '@/Wallet/constants'; +import { add } from 'husky'; + +const seed = bip39.mnemonicToSeedSync(TEST_MNEMONIC); +let masterHdKey = bip32.fromSeed(seed); +let accountKey = masterHdKey.derivePath(getAccountPathAvalanche(0)); + +jest.mock('@/Network/network', () => { + return { + activeNetwork: { + explorerURL: 'test.explorer.network', + }, + avalanche: { + getNetworkID: jest.fn().mockReturnValue(1), + }, + explorer_api: {}, + }; +}); + +jest.mock('@/Explorer', () => { + return { + getAddressChains: jest.fn(), + }; +}); + +describe('hd scanner', () => { + const scannerExt = new HdScanner(accountKey, false); + const scannerInt = new HdScanner(accountKey, true); + + it('can derive external X addresses', async () => { + const testAddrs = TEST_MNEMONIC_ADDRS_EXT.slice(0, 10); + const addrs = await scannerExt.getAddressesInRange(0, 10); + expect(addrs).toEqual(testAddrs); + }); + + it('can derive internal X addresses', async () => { + const testAddrs = TEST_MNEMONIC_ADDRS_INT.slice(0, 10); + const addrs = await scannerInt.getAddressesInRange(0, 10); + expect(addrs).toEqual(testAddrs); + }); +}); + +function addrsToAddressChains(addrs: string[]) { + const addrsSplit = addrs.map((addr) => addr.split('-')[1]); + let res: { [addr: string]: any } = {}; + for (let i = 0; i < addrsSplit.length; i++) { + res[addrsSplit[i]] = {}; + } + return res; +} + +describe('find index explorer', function () { + const scanner = new HdScanner(accountKey, false); + + it('10 consecutive slots used', async () => { + const tenAddrs = TEST_MNEMONIC_ADDRS_EXT.slice(0, 10); + const dict = addrsToAddressChains(tenAddrs); + + getAddressChains.mockReturnValue(dict); + + const foundIndex = await scanner.resetIndex(); + expect(foundIndex).toEqual(10); + }); + + it('Multiple gaps up to index', async () => { + const addrs = [ + scanner.getAddressForIndex(2), + scanner.getAddressForIndex(4), + scanner.getAddressForIndex(5), + scanner.getAddressForIndex(14), + scanner.getAddressForIndex(19), + ]; + + const dict = addrsToAddressChains(addrs); + getAddressChains.mockReturnValue(dict); + + const foundIndex = await scanner.resetIndex(); + expect(foundIndex).toEqual(20); + }); + + it('Maximum gap of HD_SCAN_GAP_SIZE - 1', async () => { + const addr = scanner.getAddressForIndex(HD_SCAN_GAP_SIZE - 1); + const dict = addrsToAddressChains([addr]); + getAddressChains.mockReturnValue(dict); + + const foundIndex = await scanner.resetIndex(); + expect(foundIndex).toEqual(HD_SCAN_GAP_SIZE); + }); + + it('Single address in the middle', async () => { + const addrs = [scanner.getAddressForIndex(13)]; + + const dict = addrsToAddressChains(addrs); + getAddressChains.mockReturnValue(dict); + + const foundIndex = await scanner.resetIndex(); + expect(foundIndex).toEqual(14); + }); + + it('Large gaps', async () => { + const addrs = [scanner.getAddressForIndex(13), scanner.getAddressForIndex(28), scanner.getAddressForIndex(43)]; + + const dict = addrsToAddressChains(addrs); + getAddressChains.mockReturnValue(dict); + + const foundIndex = await scanner.resetIndex(); + expect(foundIndex).toEqual(44); + }); + + it('Address above HD_SCAN_GAP_SIZE', async () => { + const addrs = [scanner.getAddressForIndex(15), scanner.getAddressForIndex(15 + HD_SCAN_GAP_SIZE + 1)]; + const dict = addrsToAddressChains(addrs); + getAddressChains.mockReturnValue(dict); + + const foundIndex = await scanner.resetIndex(); + expect(foundIndex).toEqual(16); + }); +}); diff --git a/test/Wallet/MnemonicWallet.test.ts b/test/Wallet/MnemonicWallet.test.ts index 4747ea18..0ba9978d 100644 --- a/test/Wallet/MnemonicWallet.test.ts +++ b/test/Wallet/MnemonicWallet.test.ts @@ -1,4 +1,5 @@ import MnemonicWallet from '@/Wallet/MnemonicWallet'; +import { TEST_MNEMONIC } from './constants'; jest.mock('web3', () => { let web3Mock: any = jest.fn().mockImplementation(() => {}); @@ -39,11 +40,8 @@ jest.mock('@/Network/index', () => { }; }); -const MNEMONIC = - 'chimney noodle canyon tunnel sample stuff scan symbol sight club net own arrive cause suffer purity manage squirrel boost diesel bring cement father slide'; - describe('Mnemonic Wallet', () => { - const wallet = MnemonicWallet.fromMnemonic(MNEMONIC); + const wallet = MnemonicWallet.fromMnemonic(TEST_MNEMONIC); it('can return initial X address', () => { expect(wallet.getAddressX()).toEqual('X-avax19v8flm9qt2gv2tctztjjerlgs4k3vgjsfw8udh'); diff --git a/test/Wallet/constants.ts b/test/Wallet/constants.ts new file mode 100644 index 00000000..d4450682 --- /dev/null +++ b/test/Wallet/constants.ts @@ -0,0 +1,51 @@ +export const TEST_MNEMONIC = + 'chimney noodle canyon tunnel sample stuff scan symbol sight club net own arrive cause suffer purity manage squirrel boost diesel bring cement father slide'; + +export const TEST_MNEMONIC_ADDRS_EXT = [ + 'X-avax19v8flm9qt2gv2tctztjjerlgs4k3vgjsfw8udh', + 'X-avax1vu6mxvgjc5yxevfm0hkz7gldaakeatw5qnuwtw', + 'X-avax1k0zecdwk7dq5f7k0nq7e4jfp735rkktpgszyud', + 'X-avax1hr82veadxxtu9t3d92znw0h8z6yrw4m2zqn23c', + 'X-avax1l2zvycg6sqmnk5tpfy4cnyrukkj62c5eyxn5xw', + 'X-avax19aap6sk83kq82jvmn6sxz22kxfzqysztnqrj26', + 'X-avax1vly09cdgx49m7ttxhfpsptufwv9my28snda70f', + 'X-avax1qkc7g5wan8ur3wwl9chs7jacr09uv4gysxfwre', + 'X-avax1m443clxpf3mnkvqf5y8mx0ph5pulywl5vts5md', + 'X-avax1jdyd2phcszd60yxxa4vuh7k2u6wcsarleknz63', + 'X-avax1z0d3c65gf6s5xs2vtuvj4wgzu06qpq0prukfpm', + 'X-avax1wy3f8ch5p9swxhqq05rfwx0p5e0zgxsmcu63nw', + 'X-avax17hp3z4tztzpjd2mky06zr8gxn73gjqqk4h7zcc', + 'X-avax1jygnz8m50kg975yasqvlg6tu9tyvkm925vnq5c', + 'X-avax1snx2dvwlqhs3padhf6pd3zgcqv5yh7uaxpw7cu', + 'X-avax1lcy56wlfgx5d920jw9s8dp3zaz86wj2v0hnsjc', + 'X-avax1zqc4qqf7eunqmym7jns456uvu7k3mg0fy0fge7', + 'X-avax1vn26dy5gux9hu7vsgc8fwj7wrld05c6fxdnx25', + 'X-avax1c9z52azwwdpnp3ghgstmd7f32rymxj64npap49', + 'X-avax10evfcykzvw9qjx6f2wtn2sfsyz747k4u9cs92y', + 'X-avax17p2fgjy8d9am2m7vfhr06fhefjy57drrqv3ae3', + 'X-avax1mhzzfkryehtgu872ahkkxusaznmd3fc9t9wep3', + 'X-avax12tr8624kfnchdu9e9qp4qp7xkvy479fxg0lled', + 'X-avax183edjqja08avc8thawyhdc7360nuscvng6a60s', + 'X-avax140a2ep6gdlq0ku4sx6qugqw389l7l5xytxyxpn', + 'X-avax16a0ls8ud72v786ekwjprexmtzd2c85ejzgwtgq', + 'X-avax1qdsr7cete3lru0zstrn2jpf5wtghaaj84vmuf2', + 'X-avax1taagkk5980eqkwhxgeamkugaq8yr846fxxznn3', + 'X-avax1va7duugqafhf6vdv936uqa3lnw8s4knmtu5qjr', + 'X-avax1nd2625h6uj4m9y5qadltjvw90v82d09scyznqq', + 'X-avax1yudqea73wpevr7rvcfmv06p5lrn92rjye2fsyz', + 'X-avax17xx3j0sy32lvn4jlrzqmam2djp4ykp8uvgk3c6', +]; + +export const TEST_MNEMONIC_ADDRS_INT = [ + 'X-avax1fp5jw95s3s0sgylt5yvegpu03k5aggtypgpe02', + 'X-avax1fznl9qusl8el0dxh537hg79m7ekwlefjq5yfne', + 'X-avax1dw73j6ye5ggt9ug8v3mdt58tnj0zgl2y9dh756', + 'X-avax1jcx7ew3ccle52fannlj5tn3w7cfztuma03qk6j', + 'X-avax1gd8f5qsn3sc4ytec5mt22nx7p92xjqhy44v8l3', + 'X-avax130sz65vj7wpley6wnf7egcp4hx655j9kxzhlpe', + 'X-avax13k783468dxadfmjv0p64exul0pd3ewuh4z4p28', + 'X-avax10lhjdztmg0n5jendxp24x7jj3jrfktcg72l4cg', + 'X-avax1kqzasrwth7v005xgspdq79qz6lergm776emapl', + 'X-avax16cxcg2kdvajvg49mf646u5u00487tgpv4cwjq5', + 'X-avax16xzgjae27pqhye5jxyrake5yv7cj4yawa6g706', +]; diff --git a/yarn.lock b/yarn.lock index b3938900..80c46161 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1948,10 +1948,10 @@ create-hash "1.2.0" jest "^26.4.1" -"@openzeppelin/contracts@4.3.2": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.2.tgz#ff80affd6d352dbe1bbc5b4e1833c41afd6283b6" - integrity sha512-AybF1cesONZStg5kWf6ao9OlqTZuPqddvprc0ky7lrUVOjXeKpmQ2Y9FK+6ygxasb+4aic4O5pneFBfwVsRRRg== +"@openzeppelin/contracts@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.3.tgz#ff6ee919fc2a1abaf72b22814bfb72ed129ec137" + integrity sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g== "@rollup/plugin-json@^4.1.0": version "4.1.0" @@ -2149,6 +2149,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.5.tgz#9a78318a45d75c9523d2396131bd3cca54b2d185" integrity sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg== +"@types/node@10.12.18": + version "10.12.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" + integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== + "@types/node@11.11.6": version "11.11.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" @@ -2707,11 +2712,31 @@ bignumber.js@^9.0.0, bignumber.js@^9.0.1: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== +bindings@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bip32-path@0.4.2, bip32-path@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/bip32-path/-/bip32-path-0.4.2.tgz#5db0416ad6822712f077836e2557b8697c0c7c99" integrity sha1-XbBBataCJxLwd4NuJVe4aXwMfJk= +bip32@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/bip32/-/bip32-2.0.6.tgz#6a81d9f98c4cd57d05150c60d8f9e75121635134" + integrity sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA== + dependencies: + "@types/node" "10.12.18" + bs58check "^2.1.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + tiny-secp256k1 "^1.1.3" + typeforce "^1.11.5" + wif "^2.0.6" + bip39@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0" @@ -2747,7 +2772,7 @@ bn.js@5.1.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.1.tgz#48efc4031a9c4041b9c99c6941d903463ab62eb5" integrity sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.9: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== @@ -2886,7 +2911,7 @@ bs58@^4.0.0: dependencies: base-x "^3.0.2" -bs58check@^2.1.2: +bs58check@<3.0.0, bs58check@^2.1.1, bs58check@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== @@ -4304,6 +4329,11 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -6310,6 +6340,11 @@ multihashes@^0.4.15, multihashes@~0.4.15: multibase "^0.7.0" varint "^5.0.0" +nan@^2.13.2: + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + nano-json-stream-parser@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" @@ -7827,10 +7862,21 @@ timed-out@^4.0.0, timed-out@^4.0.1: resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= +tiny-secp256k1@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz#7e224d2bee8ab8283f284e40e6b4acb74ffe047c" + integrity sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA== + dependencies: + bindings "^1.3.0" + bn.js "^4.11.8" + create-hmac "^1.1.7" + elliptic "^6.4.0" + nan "^2.13.2" + tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: version "2.0.0" @@ -8047,6 +8093,11 @@ typedoc@^0.21.2: shiki "^0.9.3" typedoc-default-themes "^0.12.10" +typeforce@^1.11.5: + version "1.18.0" + resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" + integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== + typescript-transform-paths@^2.2.3: version "2.2.4" resolved "https://registry.yarnpkg.com/typescript-transform-paths/-/typescript-transform-paths-2.2.4.tgz#9d4b74a063918ce528b696766634f854b8ce9134" @@ -8626,6 +8677,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +wif@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704" + integrity sha1-CNP1IFbGZnkplyb63g1DKudLRwQ= + dependencies: + bs58check "<3.0.0" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"