diff --git a/.circleci/config.yml b/.circleci/config.yml index e8f650031..f8896bff9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,6 +31,9 @@ orbs: no_output_timeout: 30m name: Build packages command: yarn build + - run: + name: Lint + command: yarn lint - run: name: Run unit tests command: yarn test --runInBand --coverage=false diff --git a/.eslintignore b/.eslintignore new file mode 120000 index 000000000..3e4e48b0b --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..d81c41066 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,82 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier", + "plugin:prettier/recommended", + "plugin:jest/recommended" + ], + "plugins": [ + "@typescript-eslint", + "prettier", + "jest", + "simple-import-sort" + ], + "env": { + "node": true, + "browser": true, + "jest/globals": true + }, + "globals": { + "process": "readonly" + }, + "rules": { + "prettier/prettier": "error", + "no-console": [ + "error", + { + "allow": [ + "warn", + "error", + "info", + "debug", + "group", + "groupEnd", + "time", + "timeEnd" + ] + } + ], + "@typescript-eslint/no-use-before-define": "error", + "@typescript-eslint/no-shadow": [ + "error" + ], + "jest/no-standalone-expect": [ + "error", + { + "additionalTestBlockFunctions": [ + "beforeAll", + "beforeEach", + "afterEach", + "afterAll" + ] + } + ], + // TODO: We need to fix these rules later + "@typescript-eslint/no-explicit-any": "off", + "simple-import-sort/imports": [ + "error" + ] + }, + "overrides": [ + { + "files": [ + "**/*.test.{j,t}s" + ], + "rules": { + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-var-requires": "off" + } + }, + { + "files": [ + "integration/**" + ], + "rules": { + "jest/no-export": "off" + } + } + ] +} diff --git a/.gitignore b/.gitignore index 5b8d877c8..2bc26d059 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ !/.circleci !/.circleci/** !/.codeclimate.yml +!/.eslintignore +!/.eslintrc !/.gitbook.yaml !/.github/ !/.github/** diff --git a/examples/sandbox/index.ts b/examples/sandbox/index.ts index 5861a9abb..c6a21fd94 100644 --- a/examples/sandbox/index.ts +++ b/examples/sandbox/index.ts @@ -1,35 +1,34 @@ +import "regenerator-runtime/runtime"; + import * as core from "@shapeshiftoss/hdwallet-core"; import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; import * as keepkeyTcp from "@shapeshiftoss/hdwallet-keepkey-tcp"; import * as keepkeyWebUSB from "@shapeshiftoss/hdwallet-keepkey-webusb"; -import * as ledgerWebUSB from "@shapeshiftoss/hdwallet-ledger-webusb"; import * as ledgerWebHID from "@shapeshiftoss/hdwallet-ledger-webhid"; +import * as ledgerWebUSB from "@shapeshiftoss/hdwallet-ledger-webusb"; +import * as metaMask from "@shapeshiftoss/hdwallet-metamask"; import * as native from "@shapeshiftoss/hdwallet-native"; import * as portis from "@shapeshiftoss/hdwallet-portis"; -import * as metaMask from "@shapeshiftoss/hdwallet-metamask"; -import * as xdefi from "@shapeshiftoss/hdwallet-xdefi"; import * as trezorConnect from "@shapeshiftoss/hdwallet-trezor-connect"; - -import * as debug from "debug"; +import * as xdefi from "@shapeshiftoss/hdwallet-xdefi"; import $ from "jquery"; -import "regenerator-runtime/runtime"; import Web3 from "web3"; +import * as bnbTxJson from "./json/bnbTx.json"; import * as btcBech32TxJson from "./json/btcBech32Tx.json"; -import * as btcTxJson from "./json/btcTx.json"; import * as btcSegWitTxJson from "./json/btcSegWitTx.json"; +import * as btcTxJson from "./json/btcTx.json"; import * as dashTxJson from "./json/dashTx.json"; import * as dogeTxJson from "./json/dogeTx.json"; import * as ltcTxJson from "./json/ltcTx.json"; import * as rippleTxJson from "./json/rippleTx.json"; -import * as bnbTxJson from "./json/bnbTx.json"; import { - thorchainUnsignedTx, + thorchainBinanceBaseTx, thorchainBitcoinBaseTx, thorchainEthereumBaseTx, - thorchainBinanceBaseTx, thorchainNativeRuneBaseTx, thorchainRouterAbi, + thorchainUnsignedTx, } from "./json/thorchainTx.json"; const keyring = new core.Keyring(); @@ -91,6 +90,14 @@ const $metaMask = $("#metaMask"); const $xdefi = $("#xdefi"); const $keyring = $("#keyring"); +const $ethAddr = $("#ethAddr"); +const $ethTx = $("#ethTx"); +const $ethSign = $("#ethSign"); +const $ethSend = $("#ethSend"); +const $ethVerify = $("#ethVerify"); +const $ethResults = $("#ethResults"); +const $ethEIP1559 = $("#ethEIP1559"); + $keepkey.on("click", async (e) => { e.preventDefault(); wallet = await keepkeyAdapter.pairDevice(undefined, /*tryDebugLink=*/ true); @@ -141,8 +148,8 @@ $portis.on("click", async (e) => { let deviceId = "nothing"; try { deviceId = await wallet.getDeviceID(); - } catch (e) { - console.error(e); + } catch (error) { + console.error(error); } $("#keyring select").val(deviceId); }); @@ -162,8 +169,8 @@ $metaMask.on("click", async (e) => { try { deviceID = await wallet.getDeviceID(); $("#keyring select").val(deviceID); - } catch (e) { - console.error(e); + } catch (error) { + console.error(error); } }); $xdefi.on("click", async (e) => { @@ -174,8 +181,8 @@ $xdefi.on("click", async (e) => { try { deviceID = await wallet.getDeviceID(); $("#keyring select").val(deviceID); - } catch (e) { - console.error(e); + } catch (error) { + console.error(error); } }); @@ -198,8 +205,8 @@ async function deviceConnected(deviceId) { keyring.onAny((name: string[], ...values: any[]) => { const [[deviceId, event]] = values; const { from_wallet = false, message_type } = event; - let direction = from_wallet ? "🔑" : "💻"; - debug.default(deviceId)(`${direction} ${message_type}`, event); + const direction = from_wallet ? "🔑" : "💻"; + console.debug(`${deviceId} ${direction} ${message_type}`, event); const log = document.getElementById("eventLog"); log.innerHTML += `
Event: ${name}
Values: ${JSON.stringify(values)}
`; @@ -263,17 +270,17 @@ async function deviceConnected(deviceId) { for (const deviceID of Object.keys(keyring.wallets)) { await deviceConnected(deviceID); } - $keyring.change(async (e) => { + $keyring.change(async () => { if (wallet) { await wallet.disconnect(); } - let deviceID = $keyring.find(":selected").val() as string; + const deviceID = $keyring.find(":selected").val() as string; wallet = keyring.get(deviceID); if (wallet) { if (wallet.transport) { await wallet.transport.connect(); if (keepkey.isKeepKey(wallet)) { - console.log("try connect debuglink"); + console.info("try connect debuglink"); await wallet.transport.tryConnectDebugLink(); } } @@ -287,13 +294,13 @@ async function deviceConnected(deviceId) { wallet = keyring.get(); window["wallet"] = wallet; if (wallet) { - let deviceID = await wallet.getDeviceID(); + const deviceID = await wallet.getDeviceID(); $keyring.val(deviceID).change(); } })(); window["handlePinDigit"] = function (digit) { - let input = document.getElementById("#pinInput") as HTMLInputElement; + const input = document.getElementById("#pinInput") as HTMLInputElement; if (digit === "") { input.value = input.value.slice(0, -1); } else { @@ -306,7 +313,7 @@ window["pinOpen"] = function () { }; window["pinEntered"] = function () { - let input = document.getElementById("#pinInput") as HTMLInputElement; + const input = document.getElementById("#pinInput") as HTMLInputElement; wallet.sendPin(input.value); document.getElementById("#pinModal").className = "modal"; }; @@ -316,7 +323,7 @@ window["passphraseOpen"] = function () { }; window["passphraseEntered"] = function () { - let input = document.getElementById("#passphraseInput") as HTMLInputElement; + const input = document.getElementById("#passphraseInput") as HTMLInputElement; wallet.sendPassphrase(input.value); document.getElementById("#passphraseModal").className = "modal"; }; @@ -326,13 +333,15 @@ window["mnemonicOpen"] = function () { }; window["mnemonicEntered"] = async function () { - let input = document.getElementById("#mnemonicInput") as HTMLInputElement; + const input = document.getElementById("#mnemonicInput") as HTMLInputElement; wallet.loadDevice({ mnemonic: input.value }); document.getElementById("#mnemonicModal").className = "modal"; }; window["useTestWallet"] = async function () { - wallet.loadDevice({ mnemonic: await native.crypto.Isolation.Engines.Dummy.BIP39.Mnemonic.create(testPublicWalletXpubs) }); + wallet.loadDevice({ + mnemonic: await native.crypto.Isolation.Engines.Dummy.BIP39.Mnemonic.create(testPublicWalletXpubs), + }); document.getElementById("#mnemonicModal").className = "modal"; }; @@ -383,7 +392,7 @@ $getVendor.on("click", async (e) => { $manageResults.val("No wallet?"); return; } - let vendor = await wallet.getVendor(); + const vendor = await wallet.getVendor(); $manageResults.val(vendor); }); @@ -393,7 +402,7 @@ $getModel.on("click", async (e) => { $manageResults.val("No wallet?"); return; } - let model = await wallet.getModel(); + const model = await wallet.getModel(); $manageResults.val(model); }); @@ -403,7 +412,7 @@ $getDeviceID.on("click", async (e) => { $manageResults.val("No wallet?"); return; } - let deviceID = await wallet.getDeviceID(); + const deviceID = await wallet.getDeviceID(); $manageResults.val(deviceID); }); @@ -413,7 +422,7 @@ $getFirmware.on("click", async (e) => { $manageResults.val("No wallet?"); return; } - let firmware = await wallet.getFirmwareVersion(); + const firmware = await wallet.getFirmwareVersion(); $manageResults.val(firmware); }); @@ -423,7 +432,7 @@ $getLabel.on("click", async (e) => { $manageResults.val("No wallet?"); return; } - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $manageResults.val(label); }); @@ -594,7 +603,7 @@ $binanceAddr.on("click", async (e) => { return; } if (core.supportsBinance(wallet)) { - let { addressNList } = wallet.binanceGetAccountPaths({ accountIdx: 0 })[0]; + const { addressNList } = wallet.binanceGetAccountPaths({ accountIdx: 0 })[0]; let result = await wallet.binanceGetAddress({ addressNList, showDisplay: false, @@ -606,7 +615,7 @@ $binanceAddr.on("click", async (e) => { }); $binanceResults.val(result); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $binanceResults.val(label + " does not support Binance"); } }); @@ -618,7 +627,7 @@ $binanceTx.on("click", async (e) => { return; } if (core.supportsBinance(wallet)) { - let res = await wallet.binanceSignTx({ + const res = await wallet.binanceSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/714'/0'/0/0`), chain_id: "Binance-Chain-Nile", account_number: "24250", @@ -627,7 +636,7 @@ $binanceTx.on("click", async (e) => { }); $binanceResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $binanceResults.val(label + " does not support Cosmos"); } }); @@ -646,14 +655,14 @@ $rippleAddr.on("click", async (e) => { return; } if (core.supportsRipple(wallet)) { - let { addressNList } = wallet.rippleGetAccountPaths({ accountIdx: 0 })[0]; - let result = await wallet.rippleGetAddress({ + const { addressNList } = wallet.rippleGetAccountPaths({ accountIdx: 0 })[0]; + const result = await wallet.rippleGetAddress({ addressNList, showDisplay: true, }); $rippleResults.val(result); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $rippleResults.val(label + " does not support Ripple"); } }); @@ -665,7 +674,7 @@ $rippleTx.on("click", async (e) => { return; } if (core.supportsRipple(wallet)) { - let res = await wallet.rippleSignTx({ + const res = await wallet.rippleSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/144'/0'/0/0`), tx: rippleTxJson, flags: undefined, @@ -679,7 +688,7 @@ $rippleTx.on("click", async (e) => { }); $rippleResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $rippleResults.val(label + " does not support Ripple"); } }); @@ -698,7 +707,7 @@ $eosAddr.on("click", async (e) => { return; } if (core.supportsEos(wallet)) { - let { addressNList } = wallet.eosGetAccountPaths({ accountIdx: 0 })[0]; + const { addressNList } = wallet.eosGetAccountPaths({ accountIdx: 0 })[0]; let result = await wallet.eosGetPublicKey({ addressNList, showDisplay: false, @@ -710,7 +719,7 @@ $eosAddr.on("click", async (e) => { }); $eosResults.val(result); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $eosResults.val(label + " does not support Eos"); } }); @@ -722,7 +731,7 @@ $eosTx.on("click", async (e) => { return; } if (core.supportsEos(wallet)) { - let unsigned_main = { + const unsigned_main = { expiration: "2020-04-30T22:00:00.000", ref_block_num: 54661, ref_block_prefix: 2118672142, @@ -750,26 +759,26 @@ $eosTx.on("click", async (e) => { ], }; - let chainid_main = "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906"; - let res = await wallet.eosSignTx({ + const chainid_main = "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906"; + const res = await wallet.eosSignTx({ addressNList: core.bip32ToAddressNList("m/44'/194'/0'/0/0"), chain_id: chainid_main, tx: unsigned_main, }); - console.log(res); - console.log("sigV = %d", res.signatureV); - console.log("sigR = %s", core.toHexString(res.signatureR)); - console.log("sigS = %s", core.toHexString(res.signatureS)); - console.log("hash = %s", core.toHexString(res.hash)); - console.log("EosFormatSig = %s", res.eosFormSig); - console.log( + console.info(res); + console.info("sigV = %d", res.signatureV); + console.info("sigR = %s", core.toHexString(res.signatureR)); + console.info("sigS = %s", core.toHexString(res.signatureS)); + console.info("hash = %s", core.toHexString(res.hash)); + console.info("EosFormatSig = %s", res.eosFormSig); + console.info( "EosFormReSig = SIG_K1_Jxa7NRL1hj4Q9wqufaSZa7oAXQQnRxSuAeFSwx6EzHnzPVeB5y6qQge16WCYa3Xod1mDWZv3MnEEPFeK3bEf3iN6es1iVy" ); $eosResults.val(res.eosFormSig); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $eosResults.val(label + " does not support Eos"); } }); @@ -788,7 +797,7 @@ $fioAddr.on("click", async (e) => { return; } if (core.supportsFio(wallet)) { - let { addressNList } = wallet.fioGetAccountPaths({ accountIdx: 0 })[0]; + const { addressNList } = wallet.fioGetAccountPaths({ accountIdx: 0 })[0]; let result = await wallet.fioGetPublicKey({ addressNList, showDisplay: false, @@ -802,7 +811,7 @@ $fioAddr.on("click", async (e) => { }); $fioResults.val(result); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $fioResults.val(label + " does not support "); } }); @@ -814,7 +823,7 @@ $fioTx.on("click", async (e) => { return; } if (core.supportsFio(wallet)) { - let unsigned_main = { + const unsigned_main = { expiration: "2020-04-30T22:00:00.000", ref_block_num: 54661, ref_block_prefix: 2118672142, @@ -825,20 +834,20 @@ $fioTx.on("click", async (e) => { actions: [], }; - let chainid_main = "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906"; - let res = await wallet.fioSignTx({ + const chainid_main = "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906"; + const res = await wallet.fioSignTx({ addressNList: core.bip32ToAddressNList("m/44'/194'/0'/0/0"), chain_id: chainid_main, tx: unsigned_main, }); - console.log(res); - console.log("signature = %d", res.signature); - console.log("serialized = %s", core.toHexString(res.serialized)); + console.info(res); + console.info("signature = %d", res.signature); + console.info("serialized = %s", core.toHexString(res.serialized)); $eosResults.val(res.fioFormSig); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $fioResults.val(label + " does not support Fio"); } }); @@ -857,8 +866,8 @@ $cosmosAddr.on("click", async (e) => { return; } if (core.supportsCosmos(wallet)) { - let { addressNList } = wallet.cosmosGetAccountPaths({ accountIdx: 0 })[0]; - let result = await wallet.cosmosGetAddress({ + const { addressNList } = wallet.cosmosGetAccountPaths({ accountIdx: 0 })[0]; + const result = await wallet.cosmosGetAddress({ addressNList, showDisplay: false, }); @@ -868,7 +877,7 @@ $cosmosAddr.on("click", async (e) => { }); $cosmosResults.val(result); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $cosmosResults.val(label + " does not support Cosmos"); } }); @@ -880,7 +889,7 @@ $cosmosTx.on("click", async (e) => { return; } if (core.supportsCosmos(wallet)) { - let unsigned: core.Cosmos.StdTx = { + const unsigned: core.Cosmos.StdTx = { memo: "KeepKey", fee: { amount: [{ amount: "100", denom: "ATOM" }], @@ -904,7 +913,7 @@ $cosmosTx.on("click", async (e) => { signatures: null, }; - let res = await wallet.cosmosSignTx({ + const res = await wallet.cosmosSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/118'/0'/0/0`), chain_id: "cosmoshub-2", account_number: "24250", @@ -913,7 +922,7 @@ $cosmosTx.on("click", async (e) => { }); $cosmosResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $cosmosResults.val(label + " does not support Cosmos"); } }); @@ -944,8 +953,8 @@ $thorchainAddr.on("click", async (e) => { return; } if (core.supportsThorchain(wallet)) { - let { addressNList } = wallet.thorchainGetAccountPaths({ accountIdx: 0 })[0]; - let result = await wallet.thorchainGetAddress({ + const { addressNList } = wallet.thorchainGetAccountPaths({ accountIdx: 0 })[0]; + const result = await wallet.thorchainGetAddress({ addressNList, showDisplay: false, }); @@ -955,7 +964,7 @@ $thorchainAddr.on("click", async (e) => { }); $thorchainNativeResults.val(result); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainNativeResults.val(label + " does not support THORChain"); } }); @@ -967,7 +976,7 @@ $thorchainTx.on("click", async (e) => { return; } if (core.supportsThorchain(wallet)) { - let res = await wallet.thorchainSignTx({ + const res = await wallet.thorchainSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/931'/0'/0/0`), chain_id: "thorchain", account_number: "24250", @@ -976,7 +985,7 @@ $thorchainTx.on("click", async (e) => { }); $thorchainNativeResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainNativeResults.val(label + " does not support THORChain"); } }); @@ -988,7 +997,7 @@ $thorchainSignSwap.on("click", async (e) => { return; } if (!$thorchainDestAddress.val().match(/^[a-z0-9]+$/i) && $thorchainDestAddress.val() != "") { - console.log($thorchainDestAddress.val()); + console.info($thorchainDestAddress.val()); $thorchainSwapResults.val("Invalid destination address"); return; } @@ -999,8 +1008,7 @@ $thorchainSignSwap.on("click", async (e) => { const routerContractAddress = "0x0000000000000000000000000000000000000000"; const vaultAddress = "0x0000000000000000000000000000000000000000"; let tx = {}; - let res = {}; - let memo = `SWAP:${$thorchainDestChain.val()}:${$thorchainDestAddress.val()}:${$thorchainAmount.val()}`; + const memo = `SWAP:${$thorchainDestChain.val()}:${$thorchainDestAddress.val()}:${$thorchainAmount.val()}`; switch ($thorchainSourceChain.val()) { case "BTC.BTC": tx = thorchainBitcoinBaseTx; @@ -1010,7 +1018,7 @@ $thorchainSignSwap.on("click", async (e) => { const hex = "010000000181f605ead676d8182975c16e7191c21d833972dd0ed50583ce4628254d28b6a3010000008a47304402207f3220930276204c83b1740bae1da18e5a3fa2acad34944ecdc3b361b419e3520220598381bdf8273126e11460a8c720afdbb679233123d2d4e94561f75e9b280ce30141045da61d81456b6d787d576dce817a2d61d7f8cb4623ee669cbe711b0bcff327a3797e3da53a2b4e3e210535076c087c8fb98aef60e42dfeea8388435fc99dca43ffffffff0250ec0e00000000001976a914f7b9e0239571434f0ccfdba6f772a6d23f2cfb1388ac10270000000000001976a9149c9d21f47382762df3ad81391ee0964b28dd951788ac00000000"; - let inputs = [ + const inputs = [ { addressNList: [0x80000000 + 44, 0x80000000 + 0, 0x80000000 + 0, 0, 0], scriptType: core.BTCInputScriptType.SpendAddress, @@ -1022,7 +1030,7 @@ $thorchainSignSwap.on("click", async (e) => { }, ]; - let outputs = [ + const outputs = [ { address: "bc1q6m9u2qsu8mh8y7v8rr2ywavtj8g5arzlyhcej7", addressType: core.BTCOutputAddressType.Spend, @@ -1032,7 +1040,7 @@ $thorchainSignSwap.on("click", async (e) => { }, ]; - let res = await wallet.btcSignTx({ + const res = await wallet.btcSignTx({ coin: "Bitcoin", inputs: inputs, outputs: outputs, @@ -1043,24 +1051,24 @@ $thorchainSignSwap.on("click", async (e) => { $thorchainSwapResults.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainSwapResults.val(label + " does not support BTC"); } break; case "ETH.ETH": if (core.supportsETH(wallet)) { const web3 = new Web3(); - console.log(thorchainRouterAbi[0]); + console.info(thorchainRouterAbi[0]); const routerContract = new web3.eth.Contract(thorchainRouterAbi, routerContractAddress); tx = thorchainEthereumBaseTx; tx["addressNList"] = core.bip32ToAddressNList("m/44'/60'/0'/0/0"); tx["data"] = routerContract.methods .deposit(vaultAddress, "0x0000000000000000000000000000000000000000", 0, memo) .encodeABI(); - res = await wallet.ethSignTx(tx as any); + const res = await wallet.ethSignTx(tx as any); $thorchainSwapResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainSwapResults.val(label + " does not support ETH"); } break; @@ -1068,8 +1076,8 @@ $thorchainSignSwap.on("click", async (e) => { if (core.supportsBinance(wallet)) { tx = thorchainBinanceBaseTx; tx["memo"] = memo; - console.log(tx); - let res = await wallet.binanceSignTx({ + console.info(tx); + const res = await wallet.binanceSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/714'/0'/0/0`), chain_id: "Binance-Chain-Nile", account_number: "24250", @@ -1078,7 +1086,7 @@ $thorchainSignSwap.on("click", async (e) => { }); $thorchainSwapResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainSwapResults.val(label + " does not support Cosmos"); } break; @@ -1086,8 +1094,8 @@ $thorchainSignSwap.on("click", async (e) => { if (core.supportsBinance(wallet)) { tx = thorchainNativeRuneBaseTx; tx["memo"] = memo; - console.log(tx); - let res = await wallet.binanceSignTx({ + console.info(tx); + const res = await wallet.binanceSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/714'/0'/0/0`), chain_id: "Binance-Chain-Nile", account_number: "24250", @@ -1096,7 +1104,7 @@ $thorchainSignSwap.on("click", async (e) => { }); $thorchainSwapResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainSwapResults.val(label + " does not support Cosmos"); } break; @@ -1104,8 +1112,8 @@ $thorchainSignSwap.on("click", async (e) => { if (core.supportsThorchain(wallet)) { tx = thorchainUnsignedTx; tx["memo"] = memo; - console.log(tx); - let res = await wallet.thorchainSignTx({ + console.info(tx); + const res = await wallet.thorchainSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/931'/0'/0/0`), chain_id: "thorchain", account_number: "24250", @@ -1114,7 +1122,7 @@ $thorchainSignSwap.on("click", async (e) => { }); $thorchainSwapResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainSwapResults.val(label + " does not support Cosmos"); } break; @@ -1123,16 +1131,16 @@ $thorchainSignSwap.on("click", async (e) => { tx = thorchainEthereumBaseTx; tx["addressNList"] = core.bip32ToAddressNList("m/44'/60'/0'/0/0"); tx["data"] = "0x"; - res = await wallet.ethSignTx(tx as any); + const res = await wallet.ethSignTx(tx as any); $thorchainSwapResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ethResults.val(label + " does not support ETH"); } break; default: - console.log("Base coin is Unknown."); - console.log("val:", $thorchainSourceChain.val()); + console.info("Base coin is Unknown."); + console.info("val:", $thorchainSourceChain.val()); $thorchainSwapResults.val("Invalid source chain"); return; } @@ -1147,7 +1155,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { return; } if (!$thorchainDestAddress.val().match(/^[a-z0-9]+$/i) && $thorchainDestAddress.val() != "") { - console.log($thorchainDestAddress.val()); + console.info($thorchainDestAddress.val()); $thorchainAddLiquidityResults.val("Invalid destination address"); return; } @@ -1158,8 +1166,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { const routerContractAddress = "0x0000000000000000000000000000000000000000"; const vaultAddress = "0x0000000000000000000000000000000000000000"; let tx = {}; - let res = {}; - let memo = `ADD:${$thorchainLiquidityAsset.val()}:${$thorchainLiquidityPoolAddress.val()}}`; + const memo = `ADD:${$thorchainLiquidityAsset.val()}:${$thorchainLiquidityPoolAddress.val()}}`; switch ($thorchainLiquidityAsset.val()) { case "BTC.BTC": tx = thorchainBitcoinBaseTx; @@ -1169,7 +1176,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { const hex = "010000000181f605ead676d8182975c16e7191c21d833972dd0ed50583ce4628254d28b6a3010000008a47304402207f3220930276204c83b1740bae1da18e5a3fa2acad34944ecdc3b361b419e3520220598381bdf8273126e11460a8c720afdbb679233123d2d4e94561f75e9b280ce30141045da61d81456b6d787d576dce817a2d61d7f8cb4623ee669cbe711b0bcff327a3797e3da53a2b4e3e210535076c087c8fb98aef60e42dfeea8388435fc99dca43ffffffff0250ec0e00000000001976a914f7b9e0239571434f0ccfdba6f772a6d23f2cfb1388ac10270000000000001976a9149c9d21f47382762df3ad81391ee0964b28dd951788ac00000000"; - let inputs = [ + const inputs = [ { addressNList: [0x80000000 + 44, 0x80000000 + 0, 0x80000000 + 0, 0, 0], scriptType: core.BTCInputScriptType.SpendAddress, @@ -1181,7 +1188,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { }, ]; - let outputs = [ + const outputs = [ { addressType: core.BTCOutputAddressType.Spend, opReturnData: Buffer.from(memo, "utf-8"), @@ -1190,7 +1197,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { }, ]; - let res = await wallet.btcSignTx({ + const res = await wallet.btcSignTx({ coin: "Bitcoin", inputs: inputs, outputs: outputs, @@ -1200,14 +1207,14 @@ $thorchainSignAddLiquidity.on("click", async (e) => { $thorchainAddLiquidityResults.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainAddLiquidityResults.val(label + " does not support BTC"); } break; case "ETH.ETH": if (core.supportsETH(wallet)) { const web3 = new Web3(); - console.log(thorchainRouterAbi[0]); + console.info(thorchainRouterAbi[0]); const routerContract = new web3.eth.Contract(thorchainRouterAbi, routerContractAddress); tx = thorchainEthereumBaseTx; tx["value"] = "0x" + $thorchainLiquidityAmount.val().toString(16); @@ -1215,11 +1222,11 @@ $thorchainSignAddLiquidity.on("click", async (e) => { tx["data"] = routerContract.methods .deposit(vaultAddress, "0x0000000000000000000000000000000000000000", 0, memo) .encodeABI(); - console.log(tx); - res = await wallet.ethSignTx(tx as any); + console.info(tx); + const res = await wallet.ethSignTx(tx as any); $thorchainAddLiquidityResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainAddLiquidityResults.val(label + " does not support ETH"); } break; @@ -1231,7 +1238,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { address: $thorchainLiquidityPoolAddress.val(), coins: [{ amount: $thorchainLiquidityAmount.val(), denom: "BNB" }], }; - let res = await wallet.binanceSignTx({ + const res = await wallet.binanceSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/714'/0'/0/0`), chain_id: "Binance-Chain-Nile", account_number: "24250", @@ -1240,7 +1247,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { }); $thorchainAddLiquidityResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainAddLiquidityResults.val(label + " does not support Cosmos"); } break; @@ -1252,7 +1259,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { address: $thorchainLiquidityPoolAddress.val(), coins: [{ amount: $thorchainLiquidityAmount.val(), denom: "BNB" }], }; - let res = await wallet.binanceSignTx({ + const res = await wallet.binanceSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/714'/0'/0/0`), chain_id: "Binance-Chain-Nile", account_number: "24250", @@ -1261,7 +1268,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { }); $thorchainAddLiquidityResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainAddLiquidityResults.val(label + " does not support Cosmos"); } break; @@ -1273,8 +1280,8 @@ $thorchainSignAddLiquidity.on("click", async (e) => { address: $thorchainLiquidityPoolAddress.val(), coins: [{ amount: $thorchainLiquidityAmount.val(), denom: "RUNE" }], }; - console.log(tx); - let res = await wallet.thorchainSignTx({ + console.info(tx); + const res = await wallet.thorchainSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/931'/0'/0/0`), chain_id: "thorchain", account_number: "24250", @@ -1283,7 +1290,7 @@ $thorchainSignAddLiquidity.on("click", async (e) => { }); $thorchainAddLiquidityResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $thorchainAddLiquidityResults.val(label + " does not support Cosmos"); } break; @@ -1294,16 +1301,16 @@ $thorchainSignAddLiquidity.on("click", async (e) => { tx["data"] = "0x"; tx["to"] = $thorchainLiquidityPoolAddress.val(); tx["value"] = $thorchainLiquidityAmount.val(); - res = await wallet.ethSignTx(tx as any); + const res = await wallet.ethSignTx(tx as any); $thorchainAddLiquidityResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ethResults.val(label + " does not support ETH"); } break; default: - console.log("Base coin is Unknown."); - console.log("val:", $thorchainSourceChain.val()); + console.info("Base coin is Unknown."); + console.info("val:", $thorchainSourceChain.val()); $thorchainAddLiquidityResults.val("Invalid source chain"); return; } @@ -1336,8 +1343,8 @@ $osmosisAddress.on("click", async (e) => { return; } if (core.supportsOsmosis(wallet)) { - let { addressNList } = wallet.osmosisGetAccountPaths({ accountIdx: 0 })[0]; - let result = await wallet.osmosisGetAddress({ + const { addressNList } = wallet.osmosisGetAccountPaths({ accountIdx: 0 })[0]; + const result = await wallet.osmosisGetAddress({ addressNList, showDisplay: false, }); @@ -1347,7 +1354,7 @@ $osmosisAddress.on("click", async (e) => { }); $osmosisAddressResults.val(result); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $osmosisAddressResults.val(label + " does not support Osmosis"); } }); @@ -1359,7 +1366,7 @@ $osmosisSignTx.on("click", async (e) => { return; } if (core.supportsOsmosis(wallet)) { - let res = await wallet.osmosisSignTx({ + const res = await wallet.osmosisSignTx({ tx: { chain_id: "osmosis", account_number: "75815", @@ -1399,7 +1406,7 @@ $osmosisSignTx.on("click", async (e) => { }); $osmosisSignTxResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $osmosisSignTxResults.val(label + " does not support Osmosis"); } }); @@ -1411,7 +1418,7 @@ $osmosisDelegate.on("click", async (e) => { return; } if (core.supportsOsmosis(wallet)) { - let res = await wallet.osmosisSignTx({ + const res = await wallet.osmosisSignTx({ tx: { fee: { amount: [ @@ -1445,7 +1452,7 @@ $osmosisDelegate.on("click", async (e) => { }); $osmosisDelegateResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $osmosisDelegateResults.val(label + " does not support Osmosis"); } }); @@ -1457,7 +1464,7 @@ $osmosisUndelegate.on("click", async (e) => { return; } if (core.supportsOsmosis(wallet)) { - let res = await wallet.osmosisSignTx({ + const res = await wallet.osmosisSignTx({ tx: { fee: { amount: [ @@ -1492,7 +1499,7 @@ $osmosisUndelegate.on("click", async (e) => { }); $osmosisUndelegateResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $osmosisUndelegateResults.val(label + " does not support Osmosis"); } }); @@ -1504,13 +1511,6 @@ $osmosisUndelegate.on("click", async (e) => { * Bech32: false */ -const $ethAddr = $("#ethAddr"); -const $ethTx = $("#ethTx"); -const $ethSign = $("#ethSign"); -const $ethSend = $("#ethSend"); -const $ethVerify = $("#ethVerify"); -const $ethResults = $("#ethResults"); -const $ethEIP1559 = $("#ethEIP1559"); let ethEIP1559Selected = false; @@ -1545,7 +1545,7 @@ $ethAddr.on("click", async (e) => { } if (core.supportsETH(wallet)) { - let { hardenedPath, relPath } = wallet.ethGetAccountPaths({ + const { hardenedPath, relPath } = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: 0, })[0]; @@ -1560,7 +1560,7 @@ $ethAddr.on("click", async (e) => { }); $ethResults.val(result); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ethResults.val(label + " does not support ETH"); } }); @@ -1572,10 +1572,10 @@ $ethTx.on("click", async (e) => { return; } if (core.supportsETH(wallet)) { - let res = ethEIP1559Selected ? await wallet.ethSignTx(ethTx1559) : await wallet.ethSignTx(ethTx); + const res = ethEIP1559Selected ? await wallet.ethSignTx(ethTx1559) : await wallet.ethSignTx(ethTx); $ethResults.val(JSON.stringify(res)); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ethResults.val(label + " does not support ETH"); } }); @@ -1587,17 +1587,17 @@ $ethSign.on("click", async (e) => { return; } if (core.supportsETH(wallet)) { - let { hardenedPath: hard, relPath: rel } = wallet.ethGetAccountPaths({ + const { hardenedPath: hard, relPath: rel } = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: 0, })[0]; - let result = await wallet.ethSignMessage({ + const result = await wallet.ethSignMessage({ addressNList: hard.concat(rel), message: "Hello World", }); $ethResults.val(result.address + ", " + result.signature); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ethResults.val(label + " does not support ETH"); } }); @@ -1609,17 +1609,13 @@ $ethSend.on("click", async (e) => { return; } if (core.supportsETH(wallet)) { - let { hardenedPath: hard, relPath: rel } = wallet.ethGetAccountPaths({ - coin: "Ethereum", - accountIdx: 0, - })[0]; - let result = ethEIP1559Selected + const result = ethEIP1559Selected ? await wallet.ethSendTx(ethTx1559 as core.ETHSignTx) : await wallet.ethSendTx(ethTx as core.ETHSignTx); - console.log("Result: ", result); + console.info("Result: ", result); $ethResults.val(result.hash); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ethResults.val(label + " does not support ETH"); } }); @@ -1631,7 +1627,7 @@ $ethVerify.on("click", async (e) => { return; } if (core.supportsETH(wallet)) { - let result = await wallet.ethVerifyMessage({ + const result = await wallet.ethVerifyMessage({ address: "0x2068dD92B6690255553141Dfcf00dF308281f763", message: "Hello World", signature: @@ -1639,12 +1635,12 @@ $ethVerify.on("click", async (e) => { }); $ethResults.val(result ? "✅" : "❌"); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ethResults.val(label + " does not support ETH"); } }); -$ethEIP1559.on("click", async (e) => { +$ethEIP1559.on("click", async () => { if (!ethEIP1559Selected) { $ethEIP1559.attr("class", "button"); } else { @@ -1783,7 +1779,7 @@ $erc20TransferFrom.on("click", async (e) => { erc20SetSetSelected($erc20TransferFrom); }); -$erc20Submit.on("click", async (e) => { +$erc20Submit.on("click", async () => { if (!wallet) { $erc20Results.val("No wallet?"); return; @@ -1793,7 +1789,7 @@ $erc20Submit.on("click", async (e) => { let data: any; if (core.supportsETH(wallet)) { - let { hardenedPath, relPath } = wallet.ethGetAccountPaths({ + const { hardenedPath, relPath } = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: 0, })[0]; @@ -1851,7 +1847,7 @@ $erc20Submit.on("click", async (e) => { parseInt($("#erc20Amount").val(), 10).toString(16).padStart(64, "0"); break; default: - console.log("oops", erc20Selected); + console.info("oops", erc20Selected); return; } if (erc20Selected != $erc20Addr) { @@ -1871,7 +1867,7 @@ $erc20Submit.on("click", async (e) => { $erc20Results.val(label + " does not support ETH"); } - console.log(result); + console.info(result); $erc20Results.val(JSON.stringify(result, null, 4)); }); @@ -1896,7 +1892,7 @@ $btcAddr.on("click", async (e) => { if (core.supportsBTC(wallet)) { //coin 0 (mainnet bitcoin) //path 0 - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: [0x80000000 + 44, 0x80000000 + 0, 0x80000000 + 0, 0, 0], coin: "Bitcoin", scriptType: core.BTCInputScriptType.SpendAddress, @@ -1904,7 +1900,7 @@ $btcAddr.on("click", async (e) => { }); $btcResults.val(res); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $btcResults.val(label + " does not support BTC"); } }); @@ -1921,7 +1917,7 @@ $btcTx.on("click", async (e) => { const hex = "010000000181f605ead676d8182975c16e7191c21d833972dd0ed50583ce4628254d28b6a3010000008a47304402207f3220930276204c83b1740bae1da18e5a3fa2acad34944ecdc3b361b419e3520220598381bdf8273126e11460a8c720afdbb679233123d2d4e94561f75e9b280ce30141045da61d81456b6d787d576dce817a2d61d7f8cb4623ee669cbe711b0bcff327a3797e3da53a2b4e3e210535076c087c8fb98aef60e42dfeea8388435fc99dca43ffffffff0250ec0e00000000001976a914f7b9e0239571434f0ccfdba6f772a6d23f2cfb1388ac10270000000000001976a9149c9d21f47382762df3ad81391ee0964b28dd951788ac00000000"; - let inputs = [ + const inputs = [ { addressNList: [0x80000000 + 44, 0x80000000 + 0, 0x80000000 + 0, 0, 0], scriptType: core.BTCInputScriptType.SpendAddress, @@ -1933,7 +1929,7 @@ $btcTx.on("click", async (e) => { }, ]; - let outputs = [ + const outputs = [ { address: "1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1", addressType: core.BTCOutputAddressType.Spend, @@ -1943,7 +1939,7 @@ $btcTx.on("click", async (e) => { }, ]; - let res = await wallet.btcSignTx({ + const res = await wallet.btcSignTx({ coin: "Bitcoin", inputs: inputs, outputs: outputs, @@ -1953,7 +1949,7 @@ $btcTx.on("click", async (e) => { $btcResults.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $btcResults.val(label + " does not support BTC"); } }); @@ -1965,7 +1961,7 @@ $btcSign.on("click", async (e) => { return; } if (core.supportsBTC(wallet)) { - let res = await wallet.btcSignMessage({ + const res = await wallet.btcSignMessage({ addressNList: core.bip32ToAddressNList("m/44'/0'/0'/0/0"), coin: "Bitcoin", scriptType: core.BTCInputScriptType.SpendAddress, @@ -1973,7 +1969,7 @@ $btcSign.on("click", async (e) => { }); $btcResults.val(res.address + " " + res.signature); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $btcResults.val(label + " does not support BTC"); } }); @@ -1985,7 +1981,7 @@ $btcVerify.on("click", async (e) => { return; } if (core.supportsBTC(wallet)) { - let res = await wallet.btcVerifyMessage({ + const res = await wallet.btcVerifyMessage({ address: "1FH6ehAd5ZFXCM1cLGzHxK1s4dGdq1JusM", coin: "Bitcoin", signature: @@ -1994,7 +1990,7 @@ $btcVerify.on("click", async (e) => { }); $btcResults.val(res ? "✅" : "❌"); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $btcResults.val(label + " does not support BTC"); } }); @@ -2008,7 +2004,6 @@ $btcVerify.on("click", async (e) => { const $ltcAddr = $("#ltcAddr"); const $ltcTx = $("#ltcTx"); const $ltcSign = $("#ltcSign"); -const $ltcVerify = $("#ltcVerify"); const $ltcResults = $("#ltcResults"); const ltcBip44 = { @@ -2023,7 +2018,7 @@ $ltcAddr.on("click", async (e) => { return; } if (core.supportsBTC(wallet)) { - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: ltcBip44.addressNList, coin: "Litecoin", scriptType: ltcBip44.scriptType, @@ -2031,7 +2026,7 @@ $ltcAddr.on("click", async (e) => { }); $ltcResults.val(res); } else { - let label = await wallet.getLabel(); // should be LYXTv5RdsPYKC4qGmb6x6SuKoFMxUdSjLQ + const label = await wallet.getLabel(); // should be LYXTv5RdsPYKC4qGmb6x6SuKoFMxUdSjLQ $ltcResults.val(label + " does not support Litecoin"); } }); @@ -2079,7 +2074,7 @@ $ltcTx.on("click", async (e) => { }); $ltcResults.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ltcResults.val(label + " does not support Litecoin"); } }); @@ -2091,7 +2086,7 @@ $ltcSign.on("click", async (e) => { return; } if (core.supportsBTC(wallet)) { - let res = await wallet.btcSignMessage({ + const res = await wallet.btcSignMessage({ addressNList: ltcBip44.addressNList, coin: "Litecoin", scriptType: core.BTCInputScriptType.SpendAddress, @@ -2101,7 +2096,7 @@ $ltcSign.on("click", async (e) => { // Address: LYXTv5RdsPYKC4qGmb6x6SuKoFMxUdSjLQ // Signature: 1f835c7efaf953e059e7074afa954c5a8535be321f48e393e125e2a839d1721b495b935df1162c2b69f3e698167b75ab8bfd2c9c203f6070ff701ebca49653a056 } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $ltcResults.val(label + " does not support Litecoin"); } }); @@ -2128,7 +2123,7 @@ $dogeAddr.on("click", async (e) => { return; } if (core.supportsBTC(wallet)) { - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: dogeBip44.addressNList.concat([0, 0]), coin: "Dogecoin", scriptType: dogeBip44.scriptType, @@ -2136,7 +2131,7 @@ $dogeAddr.on("click", async (e) => { }); $dogeResults.val(res); } else { - let label = await wallet.getLabel(); // should be DQTjL9vfXVbMfCGM49KWeYvvvNzRPaoiFp for alcohol abuse + const label = await wallet.getLabel(); // should be DQTjL9vfXVbMfCGM49KWeYvvvNzRPaoiFp for alcohol abuse $dogeResults.val(label + " does not support DOGE"); } }); @@ -2184,7 +2179,7 @@ $dogeTx.on("click", async (e) => { }); $dogeResults.val(res.serializedTx); // TODO: Fails for Ledger: "TransportStatusError: Ledger device: Invalid data received (0x6a80)" } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $dogeResults.val(label + " does not support Litecoin"); } }); @@ -2212,7 +2207,7 @@ $bchAddr.on("click", async (e) => { return; } if (core.supportsBTC(wallet)) { - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: bchBip44.addressNList.concat([0, 0]), coin: "BitcoinCash", scriptType: bchBip44.scriptType, @@ -2220,7 +2215,7 @@ $bchAddr.on("click", async (e) => { }); $bchResults.val(res); } else { - let label = await wallet.getLabel(); // KK: bitcoincash:qzqxk2q6rhy3j9fnnc00m08g4n5dm827xv2dmtjzzp or Ledger: 1Ci1rvsLpZqvaMLSq7LiFj6mfnV4p3833E + const label = await wallet.getLabel(); // KK: bitcoincash:qzqxk2q6rhy3j9fnnc00m08g4n5dm827xv2dmtjzzp or Ledger: 1Ci1rvsLpZqvaMLSq7LiFj6mfnV4p3833E $bchResults.val(label + " does not support BCH"); } }); @@ -2269,7 +2264,7 @@ $bchTx.on("click", async (e) => { }); $bchResults.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $bchResults.val(label + " does not support Litecoin"); } }); @@ -2297,7 +2292,7 @@ $dashAddr.on("click", async (e) => { return; } if (core.supportsBTC(wallet)) { - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: dashBip44.addressNList.concat([0, 0]), coin: "Dash", scriptType: dashBip44.scriptType, @@ -2305,7 +2300,7 @@ $dashAddr.on("click", async (e) => { }); $dashResults.val(res); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $dashResults.val(label + " does not support Dash"); } }); @@ -2353,7 +2348,7 @@ $dashTx.on("click", async (e) => { }); $dashResults.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $dashResults.val(label + " does not support Dash"); } }); @@ -2380,7 +2375,7 @@ $dgbAddr.on("click", async (e) => { return; } if (core.supportsBTC(wallet)) { - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: dgbBip44.addressNList.concat([0, 0]), coin: "DigiByte", scriptType: dgbBip44.scriptType, @@ -2388,7 +2383,7 @@ $dgbAddr.on("click", async (e) => { }); $dgbResults.val(res); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $dgbResults.val(label + " does not support Dash"); } }); @@ -2455,7 +2450,7 @@ $dgbTx.on("click", async (e) => { }); $dgbResults.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $dgbResults.val(label + " does not support Dash"); } @@ -2484,7 +2479,7 @@ $btcAddrSegWit.on("click", async (e) => { if (core.supportsBTC(wallet)) { //coin 0 (mainnet bitcoin) //path 0 - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: [0x80000000 + 49, 0x80000000 + 0, 0x80000000 + 0, 0, 0], coin: "Bitcoin", scriptType: core.BTCInputScriptType.SpendP2SHWitness, @@ -2493,7 +2488,7 @@ $btcAddrSegWit.on("click", async (e) => { $btcResultsSegWit.val(res); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $btcResultsSegWit.val(label + " does not support BTC"); } }); @@ -2507,7 +2502,7 @@ $btcAddrSegWitNative.on("click", async (e) => { if (core.supportsBTC(wallet)) { //coin 0 (mainnet bitcoin) //path 0 - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: [0x80000000 + 84, 0x80000000 + 0, 0x80000000 + 0, 0, 0], coin: "Bitcoin", scriptType: core.BTCInputScriptType.SpendWitness, @@ -2515,7 +2510,7 @@ $btcAddrSegWitNative.on("click", async (e) => { }); $btcResultsSegWit.val(res); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $btcResultsSegWit.val(label + " does not support BTC"); } }); @@ -2531,7 +2526,7 @@ $btcTxSegWit.on("click", async (e) => { const hex = "01000000021b09436d8f9fae331e8810ca8ddf5b2bac1c95338a98280ad75efb6773d54a03000000006b48304502210081734b9b58d109997241c85806e6a5c97ba79f4a76ddb98eb227626b21ac1d290220534bee7f3f2a1803b851570b62825a589b5989f69afa44ddee5b591b8f822d3d012103fa044f4e622a9dc7a877155efad20816c6994f95bd1dc21c339a820395a32e01ffffffffe4b64ecf01f1b2e2a8c0ca86662fada7abbb991e9b4974217f5977623d515ea1010000006b4830450221008a2c95c61db777e15ebb7220c9a84565080ed87b97778a0417854fefa87e447202205dafb62309770a98868737d25bc7779caffa4b50993c36c93acf1f07a5d6d69b012102000b4b1051a63e82eeede1f1990ab226685f83ba104a0946edc740e17ce2958bffffffff02a08601000000000017a91463c4b3af0eb54b8b58b07fbde95a4ab3af3b8735874f161100000000001976a91430f7daeb4336f786cb0cf3bb162d83393681ca2d88ac00000000"; - let inputs = [ + const inputs = [ { addressNList: [0x80000000 + 49, 0x80000000 + 0, 0x80000000 + 0, 0, 0], amount: String(100000), @@ -2543,7 +2538,7 @@ $btcTxSegWit.on("click", async (e) => { }, ]; - let outputs: core.BTCSignTxOutput[] = [ + const outputs: core.BTCSignTxOutput[] = [ { address: "3Eq3agTHEhMCC8sZHnJJcCcZFB7BBSJKWr", addressType: core.BTCOutputAddressType.Spend, @@ -2552,7 +2547,7 @@ $btcTxSegWit.on("click", async (e) => { isChange: false, }, ]; - let res = await wallet.btcSignTx({ + const res = await wallet.btcSignTx({ coin: "Bitcoin", inputs: inputs, outputs: outputs, @@ -2561,7 +2556,7 @@ $btcTxSegWit.on("click", async (e) => { }); $btcResultsSegWit.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $btcResultsSegWit.val(label + " does not support BTC"); } }); @@ -2583,7 +2578,7 @@ $btcTxSegWitNative.on("click", async (e) => { const hex = "01000000000101360d7a720e95a6068678eb08e91b3a8a4774222c9f34becf57d0dc4329e0a686000000001716001495f41f5c0e0ec2c7fe27f0ac4bd59a5632a40b5fffffffff02d224000000000000160014ece6935b2a5a5b5ff997c87370b16fa10f16441088ba04000000000017a914dfe58cc93d35fb99e15436f47d3bbfce820328068702483045022100f312e8246e6a00d21fd762f12231c5fb7a20094a32940b9a84e28d712a5ced9b02203b9124d7a94aa7eb1e090ceda32e884511d7068b8d47593aa46537900e3e37d40121037e8bf05c6c7223cfba3ea484ecd61ee910ae38609ea89b4a4839beed2186b3fb00000000"; - let inputs = [ + const inputs = [ { addressNList: [0x80000000 + 84, 0x80000000 + 0, 0x80000000 + 0, 0, 0], amount: String(9426), @@ -2595,7 +2590,7 @@ $btcTxSegWitNative.on("click", async (e) => { }, ]; - let outputs: core.BTCSignTxOutput[] = [ + const outputs: core.BTCSignTxOutput[] = [ { address: "bc1qc5dgazasye0yrzdavnw6wau5up8td8gdqh7t6m", addressType: core.BTCOutputAddressType.Spend, @@ -2604,7 +2599,7 @@ $btcTxSegWitNative.on("click", async (e) => { isChange: false, }, ]; - let res = await wallet.btcSignTx({ + const res = await wallet.btcSignTx({ coin: "Bitcoin", inputs: inputs, outputs: outputs, @@ -2613,7 +2608,7 @@ $btcTxSegWitNative.on("click", async (e) => { }); $btcResultsSegWit.val(res.serializedTx); } else { - let label = await wallet.getLabel(); + const label = await wallet.getLabel(); $btcResultsSegWit.val(label + " does not support BTC"); } diff --git a/examples/sandbox/package.json b/examples/sandbox/package.json index d5134a6b0..98e231d2b 100644 --- a/examples/sandbox/package.json +++ b/examples/sandbox/package.json @@ -23,14 +23,12 @@ "@shapeshiftoss/hdwallet-trezor-connect": "1.19.1", "@shapeshiftoss/hdwallet-xdefi": "1.19.1", "bip32": "^2.0.4", - "debug": "^4.1.1", "jquery": "^3.4.1", "json": "^9.0.6", "parcel": "^2.3.2", "web3": "^1.5.1" }, "devDependencies": { - "@types/debug": "^4.1.2", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", diff --git a/integration/jest.config.js b/integration/jest.config.js index 91575d17c..ec6cddacb 100644 --- a/integration/jest.config.js +++ b/integration/jest.config.js @@ -10,13 +10,13 @@ module.exports = { "^@shapeshiftoss/hdwallet-(.*)": "/../../packages/hdwallet-$1/src", }, globals: { - 'ts-jest': { + "ts-jest": { diagnostics: { // TS(7016) "could not find a declaration file for module" occurs when ts-jest is confused about which *.d.ts to pass // as input to TypeScript's compilation API. When this happens, noImplicitAny will complain. Disabling this error will // suppress it when run under ts-jest; this is OK, because it will still be reported during the normal build process. ignoreCodes: [7016], - } + }, }, }, }; diff --git a/integration/package.json b/integration/package.json index 8f16db4e9..a6157f59f 100644 --- a/integration/package.json +++ b/integration/package.json @@ -19,9 +19,7 @@ "@shapeshiftoss/hdwallet-portis": "1.19.1", "@shapeshiftoss/hdwallet-trezor": "1.19.1", "@shapeshiftoss/hdwallet-xdefi": "1.19.1", - "@types/debug": "^4.1.5", "@types/jest": "^26.0.23", - "debug": "^4.2.0", "fast-json-stable-stringify": "^2.1.0", "jest": "^26.6.3", "jest-environment-jsdom": "^25.5.0", diff --git a/integration/src/binance/binance.ts b/integration/src/binance/binance.ts index c70d89c77..cdef2102e 100644 --- a/integration/src/binance/binance.ts +++ b/integration/src/binance/binance.ts @@ -80,6 +80,7 @@ export function binanceTests(get: () => { wallet: core.HDWallet; info: core.HDWa // Check that the signed transaction matches tx02_signed -- KeepKey doesn't provide this field, // but the tests will be run with hdwallet-native as well, which will prove this invariant under // the assumption that both are generating signatures over the same data. + // eslint-disable-next-line jest/no-conditional-expect if (res.serialized) expect(res.serialized).toEqual(tx02_signed.serialized); const txBytes = Buffer.from(tx02_signed.serialized, "hex"); expect(validateBnbTx(txBytes, input.chain_id)).toEqual(true); diff --git a/integration/src/bitcoin/bitcoin.ts b/integration/src/bitcoin/bitcoin.ts index c93a9d171..131614905 100644 --- a/integration/src/bitcoin/bitcoin.ts +++ b/integration/src/bitcoin/bitcoin.ts @@ -188,17 +188,17 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa ], ], async (args) => { - let mode = args[0] as string; - let coin = args[1] as core.Coin; - let path = args[2] as string; - let scriptType = args[3] as core.BTCInputScriptType; - let expected = args[4] as string; + const mode = args[0] as string; + const coin = args[1] as core.Coin; + const path = args[2] as string; + const scriptType = args[3] as core.BTCInputScriptType; + const expected = args[4] as string; if (!(await wallet.btcSupportsCoin(coin))) return; expect(await info.btcSupportsCoin(coin)).toBeTruthy(); if (!(await wallet.btcSupportsScriptType(coin, scriptType))) return; expect(await info.btcSupportsScriptType(coin, scriptType)).toBeTruthy(); - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: core.bip32ToAddressNList(path), coin: coin, showDisplay: mode === "Show", @@ -266,7 +266,7 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa isChange: false, }, ]; - let res = await wallet.btcSignTx( + const res = await wallet.btcSignTx( deepFreeze({ coin: "Bitcoin", inputs: inputs as core.BTCSignTxInput[], @@ -333,7 +333,7 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa hex: "0100000002cfdd9ee3b0ed9d9045f29a252d4c78ecac6c5814b67a29b5f6998fcff1036ac1010000008b483045022072ba61305fe7cb542d142b8f3299a7b10f9ea61f6ffaab5dca8142601869d53c0221009a8027ed79eb3b9bc13577ac2853269323434558528c6b6a7e542be46e7e9a820141047a2d177c0f3626fc68c53610b0270fa6156181f46586c679ba6a88b34c6f4874686390b4d92e5769fbb89c8050b984f4ec0b257a0e5c4ff8bd3b035a51709503ffffffffaf3e45194a9bb60c6108abe8d9d039e0618e8a147911c68f0c67598d2f9ae31a010000008b48304502200fd63adc8f6cb34359dc6cca9e5458d7ea50376cbd0a74514880735e6d1b8a4c0221008b6ead7fe5fbdab7319d6dfede3a0bc8e2a7c5b5a9301636d1de4aa31a3ee9b101410486ad608470d796236b003635718dfc07c0cac0cfc3bfc3079e4f491b0426f0676e6643a39198e8e7bdaffb94f4b49ea21baa107ec2e237368872836073668214ffffffff0170f30500000000001976a91424a56db43cf6f2b02e838ea493f95d8d6047423188ac00000000", }, ]; - let outputs: core.BTCSignTxOutput[] = [ + const outputs: core.BTCSignTxOutput[] = [ { address: "bc1qksxqxurvejkndenuv0alqawpr3e4vtqkn246cu", addressType: core.BTCOutputAddressType.Spend, @@ -349,7 +349,7 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa }, ]; - let res = await wallet.btcSignTx( + const res = await wallet.btcSignTx( deepFreeze({ coin: "Bitcoin", inputs: inputs as core.BTCSignTxInput[], @@ -381,7 +381,7 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa return; } - let res = wallet.btcSignMessage({ + const res = wallet.btcSignMessage({ addressNList: core.bip32ToAddressNList("m/44'/0'/0'/0/0"), coin: "Bitcoin", scriptType: core.BTCInputScriptType.SpendAddress, @@ -390,6 +390,7 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa // not implemented on portis if (portis.isPortis(wallet)) { + // eslint-disable-next-line jest/no-conditional-expect await expect(res).rejects.toThrowError("not supported"); return; } @@ -413,7 +414,7 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa return; } - let res = await wallet.btcVerifyMessage({ + const res = await wallet.btcVerifyMessage({ address: "1FH6ehAd5ZFXCM1cLGzHxK1s4dGdq1JusM", coin: "Bitcoin", signature: @@ -436,7 +437,7 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa return; } - let res = await wallet.btcVerifyMessage({ + const res = await wallet.btcVerifyMessage({ address: "1FH6ehAd5ZFXCM1cLGzHxK1s4dGdq1JusM", coin: "Bitcoin", signature: @@ -455,6 +456,7 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa if (!wallet) return; expect(typeof (await wallet.btcSupportsSecureTransfer()) === typeof true).toBeTruthy(); if (await wallet.btcSupportsSecureTransfer()) { + // eslint-disable-next-line jest/no-conditional-expect expect(await info.btcSupportsSecureTransfer()).toBeTruthy(); } // TODO: write a testcase that exercise secure transfer, if the wallet claims to support it. @@ -466,8 +468,9 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa "btcSupportsNativeShapeShift()", async () => { if (!wallet) return; - expect(typeof wallet.btcSupportsNativeShapeShift() === typeof true); + expect(typeof wallet.btcSupportsNativeShapeShift()).toBe("boolean"); if (wallet.btcSupportsNativeShapeShift()) { + // eslint-disable-next-line jest/no-conditional-expect expect(info.btcSupportsNativeShapeShift()).toBeTruthy(); } // TODO: write a testcase that exercises native shapeshift, if the wallet claims to support it. @@ -492,21 +495,22 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa ["BitcoinGold", 0, core.BTCInputScriptType.SpendAddress], ], async (args) => { - let coin = args[0] as core.Coin; - let accountIdx = args[1] as number; - let scriptType = args[2] as core.BTCInputScriptType; + const coin = args[0] as core.Coin; + const accountIdx = args[1] as number; + const scriptType = args[2] as core.BTCInputScriptType; if (!wallet) return; if (!(await wallet.btcSupportsCoin(coin))) return; expect(await info.btcSupportsCoin(coin)).toBeTruthy(); if (!(await wallet.btcSupportsScriptType(coin, scriptType))) return; expect(await info.btcSupportsScriptType(coin, scriptType)).toBeTruthy(); - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: coin, accountIdx: accountIdx, scriptType: scriptType, }); expect(paths.length > 0).toBeTruthy(); if (scriptType !== undefined) + // eslint-disable-next-line jest/no-conditional-expect expect( paths.filter((path) => { return path.scriptType !== scriptType; @@ -523,14 +527,19 @@ export function bitcoinTests(get: () => { wallet: core.HDWallet; info: core.HDWa async () => { if (!wallet) return; [0, 1, 9].forEach((idx) => { - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: "Bitcoin", accountIdx: idx, }); expect(typeof wallet.btcIsSameAccount(paths) === typeof true).toBeTruthy(); paths.forEach((path) => { - if (wallet.getVendor() === "Portis") expect(wallet.btcNextAccountPath(path)).toBeUndefined(); - else expect(wallet.btcNextAccountPath(path)).not.toBeUndefined(); + if (wallet.getVendor() === "Portis") { + // eslint-disable-next-line jest/no-conditional-expect + expect(wallet.btcNextAccountPath(path)).toBeUndefined(); + } else { + // eslint-disable-next-line jest/no-conditional-expect + expect(wallet.btcNextAccountPath(path)).not.toBeUndefined(); + } }); }); }, diff --git a/integration/src/bitcoin/index.ts b/integration/src/bitcoin/index.ts index 56f73509b..6bac2768c 100644 --- a/integration/src/bitcoin/index.ts +++ b/integration/src/bitcoin/index.ts @@ -1,8 +1,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { bitcoinTests } from "./bitcoin"; -import { testnetTests } from "./testnet"; import { litecoinTests } from "./litecoin"; +import { testnetTests } from "./testnet"; export function btcTests(get: () => { wallet: core.HDWallet; info: core.HDWalletInfo }): void { bitcoinTests(get); diff --git a/integration/src/bitcoin/litecoin.ts b/integration/src/bitcoin/litecoin.ts index e19f323d7..440b3cb0b 100644 --- a/integration/src/bitcoin/litecoin.ts +++ b/integration/src/bitcoin/litecoin.ts @@ -58,14 +58,14 @@ export function litecoinTests(get: () => { wallet: core.HDWallet; info: core.HDW ], ], async (args) => { - let mode = args[0] as string; - let coin = args[1] as core.Coin; - let path = args[2] as string; - let scriptType = args[3] as core.BTCInputScriptType; - let expected = args[4] as string; + const mode = args[0] as string; + const coin = args[1] as core.Coin; + const path = args[2] as string; + const scriptType = args[3] as core.BTCInputScriptType; + const expected = args[4] as string; if (!(await wallet.btcSupportsScriptType(coin, scriptType))) return; - let res = await wallet.btcGetAddress({ + const res = await wallet.btcGetAddress({ addressNList: core.bip32ToAddressNList(path), coin: coin, showDisplay: mode === "Show", @@ -88,18 +88,18 @@ export function litecoinTests(get: () => { wallet: core.HDWallet; info: core.HDW ["Litecoin", 1, core.BTCInputScriptType.SpendWitness], ], async (args) => { - let coin = args[0] as core.Coin; - let accountIdx = args[1] as number; - let scriptType = args[2] as core.BTCInputScriptType; + const coin = args[0] as core.Coin; + const accountIdx = args[1] as number; + const scriptType = args[2] as core.BTCInputScriptType; if (!wallet) return; if (!(await wallet.btcSupportsCoin(coin))) return; if (!(await wallet.btcSupportsScriptType(coin, scriptType))) return; - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: coin, accountIdx: accountIdx, scriptType: scriptType, }); - expect(paths.length > 0); + expect(paths.length).toBeGreaterThan(0); } ); }, @@ -111,7 +111,7 @@ export function litecoinTests(get: () => { wallet: core.HDWallet; info: core.HDW async () => { if (!wallet) return; [0, 1, 9].forEach((idx) => { - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: "Litecoin", accountIdx: idx, }); diff --git a/integration/src/bitcoin/testnet.ts b/integration/src/bitcoin/testnet.ts index ec0cda6da..eb8b5a3c1 100644 --- a/integration/src/bitcoin/testnet.ts +++ b/integration/src/bitcoin/testnet.ts @@ -41,8 +41,7 @@ export function testnetTests(get: () => { wallet: core.HDWallet; info: core.HDWa amount: String(123456789), vout: 0, txid: "20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337", - hex: - "01000000013a14418ce8bcac00a0cb56bf8a652110f4897cfcd736e1ab5e943b84f0ab2c80000000006a4730440220548e087d0426b20b8a571b03b9e05829f7558b80c53c12143e342f56ab29e51d02205b68cb7fb223981d4c999725ac1485a982c4259c4f50b8280f137878c232998a012102794a25b254a268e59a5869da57fbae2fadc6727cb3309321dab409b12b2fa17cffffffff0215cd5b070000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87ccc69633000000001976a914b84bacdcd8f4cc59274a5bfb73f804ca10f7fd1488ac00000000", + hex: "01000000013a14418ce8bcac00a0cb56bf8a652110f4897cfcd736e1ab5e943b84f0ab2c80000000006a4730440220548e087d0426b20b8a571b03b9e05829f7558b80c53c12143e342f56ab29e51d02205b68cb7fb223981d4c999725ac1485a982c4259c4f50b8280f137878c232998a012102794a25b254a268e59a5869da57fbae2fadc6727cb3309321dab409b12b2fa17cffffffff0215cd5b070000000017a91458b53ea7f832e8f096e896b8713a8c6df0e892ca87ccc69633000000001976a914b84bacdcd8f4cc59274a5bfb73f804ca10f7fd1488ac00000000", }, ]; const outputs: core.BTCSignTxOutput[] = [ diff --git a/integration/src/cosmos/cosmos.ts b/integration/src/cosmos/cosmos.ts index becd06c1f..715170424 100644 --- a/integration/src/cosmos/cosmos.ts +++ b/integration/src/cosmos/cosmos.ts @@ -1,25 +1,18 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import tx_unsigned_transfer from "./tx01.mainnet.cosmos.json"; -import tx_signed_trasnfer from "./tx01.mainnet.cosmos.signed.json"; - -//delgation import tx_unsigned_delegation from "./tx01.mainnet.cosmos.delegate.json"; import tx_signed_delegation from "./tx01.mainnet.cosmos.delegate.signed.json"; - +import tx_unsigned_ibc_cosmos from "./tx01.mainnet.cosmos.ibc.transfer.json"; +import tx_signed_ibc_cosmos from "./tx01.mainnet.cosmos.ibc.transfer.signed.json"; +import tx_unsigned_transfer from "./tx01.mainnet.cosmos.json"; +import tx_unsigned_rewards_cosmos from "./tx01.mainnet.cosmos.rewards.json"; +import tx_signed_rewards_cosmos from "./tx01.mainnet.cosmos.rewards.signed.json"; +import tx_signed_trasnfer from "./tx01.mainnet.cosmos.signed.json"; import tx_unigned_undelegate_cosmos from "./tx01.mainnet.cosmos.undelegate.json"; -import tx_signed_undelegate_cosmos from "./tx01.mainnet.cosmos.undelegate.signed.json"; - import tx_unsigned_redelegate_cosmos from "./tx01.mainnet.cosmos.undelegate.json"; +import tx_signed_undelegate_cosmos from "./tx01.mainnet.cosmos.undelegate.signed.json"; import tx_signed_redelegate_cosmos from "./tx01.mainnet.cosmos.undelegate.signed.json"; -import tx_unsigned_rewards_cosmos from "./tx01.mainnet.cosmos.rewards.json"; -import tx_signed_rewards_cosmos from "./tx01.mainnet.cosmos.rewards.signed.json"; - -//IBC -import tx_unsigned_ibc_cosmos from "./tx01.mainnet.cosmos.ibc.transfer.json"; -import tx_signed_ibc_cosmos from "./tx01.mainnet.cosmos.ibc.transfer.signed.json"; - const MNEMONIC12_NOPIN_NOPASSPHRASE = "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"; const TIMEOUT = 60 * 1000; @@ -77,7 +70,7 @@ export function cosmosTests(get: () => { wallet: core.HDWallet; info: core.HDWal async () => { if (!wallet) return; const input: core.CosmosSignTx = { - tx: (tx_unsigned_transfer as unknown) as core.CosmosTx, + tx: tx_unsigned_transfer as unknown as core.CosmosTx, addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), chain_id: tx_unsigned_transfer.chain_id, account_number: tx_unsigned_transfer.account_number, @@ -90,101 +83,99 @@ export function cosmosTests(get: () => { wallet: core.HDWallet; info: core.HDWal TIMEOUT ); - //delegate tx - test( - "(delegate) cosmosSignTx()", - async () => { - if (!wallet) return; - const input: core.CosmosSignTx = { - tx: (tx_unsigned_delegation as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_delegation.chain_id, - account_number: tx_unsigned_delegation.account_number, - sequence: tx_unsigned_delegation.sequence, - }; - - const res = await wallet.cosmosSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_delegation.signatures[0]); - }, - TIMEOUT - ); - - //undelegate - test( - "(undelegate) cosmosSignTx()", - async () => { - if (!wallet) return; - const input: core.CosmosSignTx = { - tx: (tx_unigned_undelegate_cosmos as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unigned_undelegate_cosmos.chain_id, - account_number: tx_unigned_undelegate_cosmos.account_number, - sequence: tx_unigned_undelegate_cosmos.sequence, - }; - - const res = await wallet.cosmosSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_undelegate_cosmos.signatures[0]); - }, - TIMEOUT - ); - - //redelegate - test( - "(redelegate) cosmosSignTx()", - async () => { - if (!wallet) return; - const input: core.CosmosSignTx = { - tx: (tx_unsigned_redelegate_cosmos as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_redelegate_cosmos.chain_id, - account_number: tx_unsigned_redelegate_cosmos.account_number, - sequence: tx_unsigned_redelegate_cosmos.sequence, - }; - - const res = await wallet.cosmosSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_redelegate_cosmos.signatures[0]); - }, - TIMEOUT - ); - - //claim reward - test( - "(claim) cosmosSignTx()", - async () => { - if (!wallet) return; - const input: core.CosmosSignTx = { - tx: (tx_unsigned_rewards_cosmos as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_rewards_cosmos.chain_id, - account_number: tx_unsigned_rewards_cosmos.account_number, - sequence: tx_unsigned_rewards_cosmos.sequence, - }; - - const res = await wallet.cosmosSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_rewards_cosmos.signatures[0]); - }, - TIMEOUT - ); + //delegate tx + test( + "(delegate) cosmosSignTx()", + async () => { + if (!wallet) return; + const input: core.CosmosSignTx = { + tx: tx_unsigned_delegation as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_delegation.chain_id, + account_number: tx_unsigned_delegation.account_number, + sequence: tx_unsigned_delegation.sequence, + }; + const res = await wallet.cosmosSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_delegation.signatures[0]); + }, + TIMEOUT + ); + + //undelegate + test( + "(undelegate) cosmosSignTx()", + async () => { + if (!wallet) return; + const input: core.CosmosSignTx = { + tx: tx_unigned_undelegate_cosmos as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unigned_undelegate_cosmos.chain_id, + account_number: tx_unigned_undelegate_cosmos.account_number, + sequence: tx_unigned_undelegate_cosmos.sequence, + }; + + const res = await wallet.cosmosSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_undelegate_cosmos.signatures[0]); + }, + TIMEOUT + ); + + //redelegate + test( + "(redelegate) cosmosSignTx()", + async () => { + if (!wallet) return; + const input: core.CosmosSignTx = { + tx: tx_unsigned_redelegate_cosmos as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_redelegate_cosmos.chain_id, + account_number: tx_unsigned_redelegate_cosmos.account_number, + sequence: tx_unsigned_redelegate_cosmos.sequence, + }; + + const res = await wallet.cosmosSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_redelegate_cosmos.signatures[0]); + }, + TIMEOUT + ); + + //claim reward + test( + "(claim) cosmosSignTx()", + async () => { + if (!wallet) return; + const input: core.CosmosSignTx = { + tx: tx_unsigned_rewards_cosmos as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_rewards_cosmos.chain_id, + account_number: tx_unsigned_rewards_cosmos.account_number, + sequence: tx_unsigned_rewards_cosmos.sequence, + }; + + const res = await wallet.cosmosSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_rewards_cosmos.signatures[0]); + }, + TIMEOUT + ); //IBC - test( - "(ibc transfer) cosmosSignTx()", - async () => { - if (!wallet) return; - const input: core.CosmosSignTx = { - tx: (tx_unsigned_ibc_cosmos as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_ibc_cosmos.chain_id, - account_number: tx_unsigned_ibc_cosmos.account_number, - sequence: tx_unsigned_ibc_cosmos.sequence, - }; - - const res = await wallet.cosmosSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_ibc_cosmos.signatures[0]); - }, - TIMEOUT - ); + test( + "(ibc transfer) cosmosSignTx()", + async () => { + if (!wallet) return; + const input: core.CosmosSignTx = { + tx: tx_unsigned_ibc_cosmos as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_ibc_cosmos.chain_id, + account_number: tx_unsigned_ibc_cosmos.account_number, + sequence: tx_unsigned_ibc_cosmos.sequence, + }; + const res = await wallet.cosmosSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_ibc_cosmos.signatures[0]); + }, + TIMEOUT + ); }); } diff --git a/integration/src/eos/eos.ts b/integration/src/eos/eos.ts index 4379868c0..eff3b77e6 100644 --- a/integration/src/eos/eos.ts +++ b/integration/src/eos/eos.ts @@ -34,12 +34,12 @@ export function eosTests(get: () => { wallet: core.HDWallet; info: core.HDWallet "eosGetAccountPaths()", () => { if (!wallet) return; - let paths = wallet.eosGetAccountPaths({ accountIdx: 0 }); + const paths = wallet.eosGetAccountPaths({ accountIdx: 0 }); expect(paths.length > 0).toBe(true); expect(paths[0].addressNList[0] > 0x80000000).toBe(true); paths.forEach((path) => { - let curAddr = path.addressNList.join(); - let nextAddr = core.mustBeDefined(wallet.eosNextAccountPath(path)).addressNList.join(); + const curAddr = path.addressNList.join(); + const nextAddr = core.mustBeDefined(wallet.eosNextAccountPath(path)).addressNList.join(); expect(nextAddr === undefined || nextAddr !== curAddr).toBeTruthy(); }); }, @@ -61,37 +61,51 @@ export function eosTests(get: () => { wallet: core.HDWallet; info: core.HDWallet TIMEOUT ); + // eslint-disable-next-line jest/no-disabled-tests test.skip( "kk integration eosSignTx()", async () => { if (!wallet) return; - let txData = tx01_unsigned as any; - let res = core.mustBeDefined(await wallet.eosSignTx({ - addressNList: core.bip32ToAddressNList("m/44'/194'/0'/0/0"), - chain_id: txData.chain_id as string, - tx: txData.transaction as core.EosTx, - })); + const txData = tx01_unsigned as any; + const res = core.mustBeDefined( + await wallet.eosSignTx({ + addressNList: core.bip32ToAddressNList("m/44'/194'/0'/0/0"), + chain_id: txData.chain_id as string, + tx: txData.transaction as core.EosTx, + }) + ); expect(res.signatureV).toEqual(31); - expect(core.toHexString(res.signatureR)).toEqual("3a58d0889c6e4dde052b76ca092f59f314e2ab4e867164083e108e7a3f40d737"); - expect(core.toHexString(res.signatureS)).toEqual("448f8175217c2fd9bf9dac753adf1baabdfa3132eab7235158fbdf3cbe346805"); + expect(core.toHexString(res.signatureR)).toEqual( + "3a58d0889c6e4dde052b76ca092f59f314e2ab4e867164083e108e7a3f40d737" + ); + expect(core.toHexString(res.signatureS)).toEqual( + "448f8175217c2fd9bf9dac753adf1baabdfa3132eab7235158fbdf3cbe346805" + ); expect(core.toHexString(res.hash)).toEqual("86a946cd06ddac53c256700ef8bfeed4d1f72512909400df597c8d594d1b0591"); }, TIMEOUT ); + // eslint-disable-next-line jest/no-disabled-tests test.skip( "confirmed on chain eosSignTx()", async () => { if (!wallet) return; - let txData = tx02_unsigned as any; - let res = core.mustBeDefined(await wallet.eosSignTx({ - addressNList: core.bip32ToAddressNList("m/44'/194'/0'/0/0"), - chain_id: txData.chain_id as string, - tx: txData.transaction as core.EosTx, - })); + const txData = tx02_unsigned as any; + const res = core.mustBeDefined( + await wallet.eosSignTx({ + addressNList: core.bip32ToAddressNList("m/44'/194'/0'/0/0"), + chain_id: txData.chain_id as string, + tx: txData.transaction as core.EosTx, + }) + ); expect(res.signatureV).toEqual(31); - expect(core.toHexString(res.signatureR)).toEqual("14ce00681a621d1f80a98d5f47a7d703ed515fb9169f0c1f1b54c5199fad7080"); - expect(core.toHexString(res.signatureS)).toEqual("767e9b510b789763fa62aaa8285f48f57ef3d56bb62ce6ebf650ec8a88aca8f0"); + expect(core.toHexString(res.signatureR)).toEqual( + "14ce00681a621d1f80a98d5f47a7d703ed515fb9169f0c1f1b54c5199fad7080" + ); + expect(core.toHexString(res.signatureS)).toEqual( + "767e9b510b789763fa62aaa8285f48f57ef3d56bb62ce6ebf650ec8a88aca8f0" + ); expect(core.toHexString(res.hash)).toEqual("d34082c1b4c6f578ef46500e30dcdc4987715d088323da8f2fb2b296f9db7b12"); }, TIMEOUT diff --git a/integration/src/ethereum/ethereum.ts b/integration/src/ethereum/ethereum.ts index 62972fe36..1122c3a15 100644 --- a/integration/src/ethereum/ethereum.ts +++ b/integration/src/ethereum/ethereum.ts @@ -1,7 +1,7 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as ledger from "@shapeshiftoss/hdwallet-ledger"; -import * as trezor from "@shapeshiftoss/hdwallet-trezor"; import * as portis from "@shapeshiftoss/hdwallet-portis"; +import * as trezor from "@shapeshiftoss/hdwallet-trezor"; const MNEMONIC12_NOPIN_NOPASSPHRASE = "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"; const MNEMONIC_TEST = "smooth antenna immense oppose august casual fresh meadow happy ugly wave control"; @@ -54,13 +54,13 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW async () => { if (!wallet) return; if (await wallet.ethSupportsSecureTransfer()) { - let account0 = core.bip32ToAddressNList("m/44'/60'/0'/0/0"); - let account1 = core.bip32ToAddressNList("m/44'/60'/1'/0/0"); - let account1Addr = await wallet.ethGetAddress({ + const account0 = core.bip32ToAddressNList("m/44'/60'/0'/0/0"); + const account1 = core.bip32ToAddressNList("m/44'/60'/1'/0/0"); + const account1Addr = await wallet.ethGetAddress({ addressNList: account1, showDisplay: false, }); - let res = await wallet.ethSignTx({ + const res = await wallet.ethSignTx({ addressNList: account0, nonce: "0x01", gasPrice: "0x14", @@ -71,6 +71,7 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW chainId: 1, data: "", }); + // eslint-disable-next-line jest/no-conditional-expect expect(res).toEqual({ r: "0x2482a45ee0d2851d3ab76a693edd7a393e8bc99422f7857be78a883bc1d60a5b", s: "0x18d776bcfae586bf08ecc70f714c9bec8959695a20ef73ad0c28233fdaeb1bd2", @@ -87,7 +88,7 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW "ethGetAccountPaths()", () => { if (!wallet) return; - let paths = wallet.ethGetAccountPaths({ + const paths = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: 0, }); @@ -119,10 +120,6 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW "ethSignTx() - ETH", async () => { if (!wallet) return; - let addr = await wallet.ethGetAddress({ - addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), - showDisplay: false, - }); const txToSign = { addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), @@ -136,7 +133,9 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW }; if (wallet.supportsOfflineSigning()) { - let res = await wallet.ethSignTx(txToSign); + const res = await wallet.ethSignTx(txToSign); + + // eslint-disable-next-line jest/no-conditional-expect expect(res).toEqual({ r: "0x63db3dd3bf3e1fe7dde1969c0fc8850e34116d0b501c0483a0e08c0f77b8ce0a", s: "0x28297d012cccf389f6332415e96ee3fc0bbf8474d05f646e029cd281a031464b", @@ -147,6 +146,7 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW } else if (wallet.supportsBroadcast() && wallet.ethSendTx) { const res = await wallet.ethSendTx(txToSign); + // eslint-disable-next-line jest/no-conditional-expect expect(res).toMatchInlineSnapshot(` Object { "hash": "txHash-0x12eC06288EDD7Ae2CC41A843fE089237fC7354F0", @@ -164,7 +164,7 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW return; } - if (!await wallet.ethSupportsEIP1559()) { + if (!(await wallet.ethSupportsEIP1559())) { return; } @@ -176,18 +176,13 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW skipChecksum: true, }); - let addr = await wallet.ethGetAddress({ - addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), - showDisplay: false, - }); - - let res = await wallet.ethSignTx({ + const res = await wallet.ethSignTx({ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), nonce: "0x0", gasLimit: "0x5ac3", maxFeePerGas: "0x16854be509", maxPriorityFeePerGas: "0x540ae480", - value: "0x1550f7dca70000", // 0.006 eth + value: "0x1550f7dca70000", // 0.006 eth to: "0xfc0cc6e85dff3d75e3985e0cb83b090cfd498dd1", chainId: 1, data: "", @@ -207,12 +202,12 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW "ethSignTx() - ETH EIP-1559 (optional)", async () => { if (!wallet) return; - - if (!await wallet.ethSupportsEIP1559()) { - return; - } - - let res = await wallet.ethSignTx({ + + if (!(await wallet.ethSupportsEIP1559())) { + return; + } + + const res = await wallet.ethSignTx({ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), nonce: "0x01", gasPrice: "0x1dcd65000", @@ -256,7 +251,9 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW }; if (wallet.supportsOfflineSigning()) { - let res = await wallet.ethSignTx(txToSign); + const res = await wallet.ethSignTx(txToSign); + + // eslint-disable-next-line jest/no-conditional-expect expect(res).toEqual({ r: "0x1238fd332545415f09a01470350a5a20abc784dbf875cf58f7460560e66c597f", s: "0x10efa4dd6fdb381c317db8f815252c2ac0d2a883bd364901dee3dec5b7d3660a", @@ -267,6 +264,7 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW } else if (wallet.supportsBroadcast() && wallet.ethSendTx) { const res = await wallet.ethSendTx(txToSign); + // eslint-disable-next-line jest/no-conditional-expect expect(res).toMatchInlineSnapshot(` Object { "hash": "txHash-0x41e5560054824ea6b0732e656e3ad64e20e94e45", @@ -346,7 +344,9 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW }; if (wallet.supportsOfflineSigning()) { - let res = await wallet.ethSignTx(txToSign); + const res = await wallet.ethSignTx(txToSign); + + // eslint-disable-next-line jest/no-conditional-expect expect(res).toEqual({ r: "0x5ea245ddd00fdf3958d6223255e37dcb0c61fa62cfa9cfb25e507da16ec8d96a", s: "0x6c428730776958b80fd2b2201600420bb49059f9b34ee3b960cdcce45d4a1e09", @@ -357,6 +357,7 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW } else if (wallet.supportsBroadcast() && wallet.ethSendTx) { const res = await wallet.ethSendTx(txToSign); + // eslint-disable-next-line jest/no-conditional-expect expect(res).toMatchInlineSnapshot(` Object { "hash": "txHash-0xdef1c0ded9bec7f1a1670819833240f027b25eff", @@ -372,7 +373,7 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW async () => { if (!wallet) return; if (ledger.isLedger(wallet)) return; // FIXME: Expected failure - let res = await wallet.ethSignMessage({ + const res = await wallet.ethSignMessage({ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), message: "Hello World", }); @@ -390,16 +391,18 @@ export function ethereumTests(get: () => { wallet: core.HDWallet; info: core.HDW if (!wallet) return; if (wallet.supportsOfflineSigning()) { - let res = await wallet.ethVerifyMessage({ + const res = await wallet.ethVerifyMessage({ address: "0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8", message: "Hello World", signature: "0x29f7212ecc1c76cea81174af267b67506f754ea8c73f144afa900a0d85b24b21319621aeb062903e856352f38305710190869c3ce5a1425d65ef4fa558d0fc251b", }); + + // eslint-disable-next-line jest/no-conditional-expect expect(res).toBeTruthy(); } }, TIMEOUT ); - }); + }); } diff --git a/integration/src/fio/fio.ts b/integration/src/fio/fio.ts index 49c29efb6..ec8fe46ed 100644 --- a/integration/src/fio/fio.ts +++ b/integration/src/fio/fio.ts @@ -1,7 +1,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import * as tx01_unsigned from "./tx01.unsigned.json"; -import * as tx02_signed from "./tx02.signed.json"; +// TODO: Use these test TXs for something? +// import * as tx01_unsigned from "./tx01.unsigned.json"; +// import * as tx02_signed from "./tx02.signed.json"; const MNEMONIC12_NOPIN_NOPASSPHRASE = "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"; const MNEMONIC12_NOPIN_NOPASSPHRASE2 = "all all all all all all all all all all all all"; diff --git a/integration/src/integration.ts b/integration/src/integration.ts index b3c7c816b..8535ac086 100644 --- a/integration/src/integration.ts +++ b/integration/src/integration.ts @@ -7,21 +7,20 @@ import * as portis from "@shapeshiftoss/hdwallet-portis"; import * as trezor from "@shapeshiftoss/hdwallet-trezor"; import * as xdefi from "@shapeshiftoss/hdwallet-xdefi"; +import { binanceTests } from "./binance"; import { btcTests } from "./bitcoin"; -import { ethTests } from "./ethereum"; import { cosmosTests } from "./cosmos"; -import { osmosisTests } from "./osmosis"; -import { binanceTests } from "./binance"; -import { rippleTests } from "./ripple"; import { eosTests } from "./eos"; +import { ethTests } from "./ethereum"; import { fioTests } from "./fio"; -import { thorchainTests } from "./thorchain"; +import { kavaTests } from "./kava"; +import { osmosisTests } from "./osmosis"; +import { rippleTests } from "./ripple"; import { secretTests } from "./secret"; import { terraTests } from "./terra"; -import { kavaTests } from "./kava"; -import { WalletSuite } from "./wallets/suite"; - +import { thorchainTests } from "./thorchain"; import { ethereum } from "./wallets/mocks/@metamask/detect-provider"; +import { WalletSuite } from "./wallets/suite"; jest.mock("@metamask/detect-provider", () => async () => Promise.resolve(ethereum)); diff --git a/integration/src/kava/kava.ts b/integration/src/kava/kava.ts index 9eab7fc66..6c54c8ee2 100644 --- a/integration/src/kava/kava.ts +++ b/integration/src/kava/kava.ts @@ -49,7 +49,16 @@ export function kavaTests(get: () => { wallet: core.HDWallet; info: core.HDWalle path: core.bip32ToAddressNList("m/44'/459'/0'/0/0"), coin: "Kava", }) - ); + ).toMatchInlineSnapshot(` + Object { + "accountIdx": 0, + "coin": "Kava", + "isKnown": true, + "isPrefork": false, + "verbose": "Kava Account #0", + "wholeAccount": true, + } + `); }, TIMEOUT ); @@ -88,6 +97,7 @@ export function kavaTests(get: () => { wallet: core.HDWallet; info: core.HDWalle //expect(res?.signatures?.[0].signature).toEqual(tx_signed.signatures[0].signature_keepkey); break; default: + // eslint-disable-next-line jest/no-conditional-expect expect(res?.signatures?.[0].signature).toEqual(tx_signed.signatures[0].signature); break; } diff --git a/integration/src/osmosis/osmosis.ts b/integration/src/osmosis/osmosis.ts index 9a5117e74..8738475ac 100644 --- a/integration/src/osmosis/osmosis.ts +++ b/integration/src/osmosis/osmosis.ts @@ -1,34 +1,23 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import tx_unsigned_transfer from "./tx01.mainnet.osmosis.transfer.json"; -import tx_signed_transfer from "./tx01.mainnet.osmosis.transfer.signed.json"; - -//delgation import tx_unsigned_delegation from "./tx01.mainnet.osmosis.delegate.json"; import tx_signed_delegation from "./tx01.mainnet.osmosis.delegate.signed.json"; - -import tx_unigned_undelegate_osmosis from "./tx01.mainnet.osmosis.undelegate.json"; -import tx_signed_undelegate_osmosis from "./tx01.mainnet.osmosis.undelegate.signed.json"; - -import tx_unsigned_redelegate_osmosis from "./tx01.mainnet.osmosis.redelegate.json"; -import tx_signed_redelegate_osmosis from "./tx01.mainnet.osmosis.redelegate.signed.json"; - -import tx_unsigned_rewards_osmosis from "./tx01.mainnet.osmosis.rewards.json"; -import tx_signed_rewards_osmosis from "./tx01.mainnet.osmosis.rewards.signed.json"; - -//LP import tx_unsigned_lp_add_osmosis from "./tx01.mainnet.osmosis.lp-add.json"; import tx_signed_lp_add_osmosis from "./tx01.mainnet.osmosis.lp-add.signed.json"; - import tx_unsigned_lp_remove_osmosis from "./tx01.mainnet.osmosis.lp-remove.json"; import tx_signed_lp_remove_osmosis from "./tx01.mainnet.osmosis.lp-remove.signed.json"; - import tx_unsigned_lp_stake_osmosis from "./tx01.mainnet.osmosis.lp-stake.json"; import tx_signed_lp_stake_osmosis from "./tx01.mainnet.osmosis.lp-stake.signed.json"; - import tx_unsigned_lp_unstake_osmosis from "./tx01.mainnet.osmosis.lp-unstake.json"; import tx_signed_lp_unstake_osmosis from "./tx01.mainnet.osmosis.lp-unstake.signed.json"; - +import tx_unsigned_redelegate_osmosis from "./tx01.mainnet.osmosis.redelegate.json"; +import tx_signed_redelegate_osmosis from "./tx01.mainnet.osmosis.redelegate.signed.json"; +import tx_unsigned_rewards_osmosis from "./tx01.mainnet.osmosis.rewards.json"; +import tx_signed_rewards_osmosis from "./tx01.mainnet.osmosis.rewards.signed.json"; +import tx_unsigned_transfer from "./tx01.mainnet.osmosis.transfer.json"; +import tx_signed_transfer from "./tx01.mainnet.osmosis.transfer.signed.json"; +import tx_unigned_undelegate_osmosis from "./tx01.mainnet.osmosis.undelegate.json"; +import tx_signed_undelegate_osmosis from "./tx01.mainnet.osmosis.undelegate.signed.json"; const MNEMONIC12_NOPIN_NOPASSPHRASE = "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"; @@ -43,7 +32,7 @@ export function osmosisTests(get: () => { wallet: core.HDWallet; info: core.HDWa describe("Osmosis", () => { beforeAll(async () => { const { wallet: w } = get(); - if (core.supportsOsmosis(w)){ + if (core.supportsOsmosis(w)) { wallet = w; } }); @@ -88,7 +77,7 @@ export function osmosisTests(get: () => { wallet: core.HDWallet; info: core.HDWa async () => { if (!wallet) return; const input: core.OsmosisSignTx = { - tx: (tx_unsigned_transfer as unknown) as any, + tx: tx_unsigned_transfer as unknown as any, addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), chain_id: tx_unsigned_transfer.chain_id, account_number: tx_unsigned_transfer.account_number, @@ -107,7 +96,7 @@ export function osmosisTests(get: () => { wallet: core.HDWallet; info: core.HDWa async () => { if (!wallet) return; const input: core.OsmosisSignTx = { - tx: (tx_unsigned_delegation as unknown) as any, + tx: tx_unsigned_delegation as unknown as any, addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), chain_id: tx_unsigned_delegation.chain_id, account_number: tx_unsigned_delegation.account_number, @@ -125,13 +114,13 @@ export function osmosisTests(get: () => { wallet: core.HDWallet; info: core.HDWa "(undelegate) osmosisSignTx()", async () => { if (!wallet) return; - const input: core.OsmosisSignTx = { - tx: (tx_unigned_undelegate_osmosis as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unigned_undelegate_osmosis.chain_id, - account_number: tx_unigned_undelegate_osmosis.account_number, - sequence: tx_unigned_undelegate_osmosis.sequence, - }; + const input: core.OsmosisSignTx = { + tx: tx_unigned_undelegate_osmosis as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unigned_undelegate_osmosis.chain_id, + account_number: tx_unigned_undelegate_osmosis.account_number, + sequence: tx_unigned_undelegate_osmosis.sequence, + }; const res = await wallet.osmosisSignTx(input); expect(res?.signatures?.[0]).toEqual(tx_signed_undelegate_osmosis.signatures[0]); @@ -139,37 +128,37 @@ export function osmosisTests(get: () => { wallet: core.HDWallet; info: core.HDWa TIMEOUT ); - //redelegate - test( - "(redelegate) osmosisSignTx()", - async () => { - if (!wallet) return; - const input: core.OsmosisSignTx = { - tx: (tx_unsigned_redelegate_osmosis as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_redelegate_osmosis.chain_id, - account_number: tx_unsigned_redelegate_osmosis.account_number, - sequence: tx_unsigned_redelegate_osmosis.sequence, - }; - - const res = await wallet.osmosisSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_redelegate_osmosis.signatures[0]); - }, - TIMEOUT - ); + //redelegate + test( + "(redelegate) osmosisSignTx()", + async () => { + if (!wallet) return; + const input: core.OsmosisSignTx = { + tx: tx_unsigned_redelegate_osmosis as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_redelegate_osmosis.chain_id, + account_number: tx_unsigned_redelegate_osmosis.account_number, + sequence: tx_unsigned_redelegate_osmosis.sequence, + }; + + const res = await wallet.osmosisSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_redelegate_osmosis.signatures[0]); + }, + TIMEOUT + ); //claim reward test( "(claim) osmosisSignTx()", async () => { if (!wallet) return; - const input: core.OsmosisSignTx = { - tx: (tx_unsigned_rewards_osmosis as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_rewards_osmosis.chain_id, - account_number: tx_unsigned_rewards_osmosis.account_number, - sequence: tx_unsigned_rewards_osmosis.sequence, - }; + const input: core.OsmosisSignTx = { + tx: tx_unsigned_rewards_osmosis as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_rewards_osmosis.chain_id, + account_number: tx_unsigned_rewards_osmosis.account_number, + sequence: tx_unsigned_rewards_osmosis.sequence, + }; const res = await wallet.osmosisSignTx(input); expect(res?.signatures?.[0]).toEqual(tx_signed_rewards_osmosis.signatures[0]); @@ -178,78 +167,78 @@ export function osmosisTests(get: () => { wallet: core.HDWallet; info: core.HDWa ); //lp add - test( - "(lp add) osmosisSignTx()", - async () => { - if (!wallet) return; - const input: core.OsmosisSignTx = { - tx: (tx_unsigned_lp_add_osmosis as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_lp_add_osmosis.chain_id, - account_number: tx_unsigned_lp_add_osmosis.account_number, - sequence: tx_unsigned_lp_add_osmosis.sequence, - }; - - const res = await wallet.osmosisSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_lp_add_osmosis.signatures[0]); - }, - TIMEOUT - ); - - test( - "(lp remove) osmosisSignTx()", - async () => { - if (!wallet) return; - const input: core.OsmosisSignTx = { - tx: (tx_unsigned_lp_remove_osmosis as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_lp_remove_osmosis.chain_id, - account_number: tx_unsigned_lp_remove_osmosis.account_number, - sequence: tx_unsigned_lp_remove_osmosis.sequence, - }; - - const res = await wallet.osmosisSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_lp_remove_osmosis.signatures[0]); - }, - TIMEOUT - ); + test( + "(lp add) osmosisSignTx()", + async () => { + if (!wallet) return; + const input: core.OsmosisSignTx = { + tx: tx_unsigned_lp_add_osmosis as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_lp_add_osmosis.chain_id, + account_number: tx_unsigned_lp_add_osmosis.account_number, + sequence: tx_unsigned_lp_add_osmosis.sequence, + }; + + const res = await wallet.osmosisSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_lp_add_osmosis.signatures[0]); + }, + TIMEOUT + ); + + test( + "(lp remove) osmosisSignTx()", + async () => { + if (!wallet) return; + const input: core.OsmosisSignTx = { + tx: tx_unsigned_lp_remove_osmosis as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_lp_remove_osmosis.chain_id, + account_number: tx_unsigned_lp_remove_osmosis.account_number, + sequence: tx_unsigned_lp_remove_osmosis.sequence, + }; + + const res = await wallet.osmosisSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_lp_remove_osmosis.signatures[0]); + }, + TIMEOUT + ); //lp stake - test( - "(lp stake) osmosisSignTx()", - async () => { - if (!wallet) return; - const input: core.OsmosisSignTx = { - tx: (tx_unsigned_lp_stake_osmosis as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_lp_stake_osmosis.chain_id, - account_number: tx_unsigned_lp_stake_osmosis.account_number, - sequence: tx_unsigned_lp_stake_osmosis.sequence, - }; - - const res = await wallet.osmosisSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_lp_stake_osmosis.signatures[0]); - }, - TIMEOUT - ); - - //lp unstake - test( - "(lp unstake) osmosisSignTx()", - async () => { - if (!wallet) return; - const input: core.OsmosisSignTx = { - tx: (tx_unsigned_lp_unstake_osmosis as unknown) as any, - addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), - chain_id: tx_unsigned_lp_unstake_osmosis.chain_id, - account_number: tx_unsigned_lp_unstake_osmosis.account_number, - sequence: tx_unsigned_lp_unstake_osmosis.sequence, - }; - - const res = await wallet.osmosisSignTx(input); - expect(res?.signatures?.[0]).toEqual(tx_signed_lp_unstake_osmosis.signatures[0]); - }, - TIMEOUT - ); + test( + "(lp stake) osmosisSignTx()", + async () => { + if (!wallet) return; + const input: core.OsmosisSignTx = { + tx: tx_unsigned_lp_stake_osmosis as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_lp_stake_osmosis.chain_id, + account_number: tx_unsigned_lp_stake_osmosis.account_number, + sequence: tx_unsigned_lp_stake_osmosis.sequence, + }; + + const res = await wallet.osmosisSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_lp_stake_osmosis.signatures[0]); + }, + TIMEOUT + ); + + //lp unstake + test( + "(lp unstake) osmosisSignTx()", + async () => { + if (!wallet) return; + const input: core.OsmosisSignTx = { + tx: tx_unsigned_lp_unstake_osmosis as unknown as any, + addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), + chain_id: tx_unsigned_lp_unstake_osmosis.chain_id, + account_number: tx_unsigned_lp_unstake_osmosis.account_number, + sequence: tx_unsigned_lp_unstake_osmosis.sequence, + }; + + const res = await wallet.osmosisSignTx(input); + expect(res?.signatures?.[0]).toEqual(tx_signed_lp_unstake_osmosis.signatures[0]); + }, + TIMEOUT + ); }); } diff --git a/integration/src/ripple/ripple.ts b/integration/src/ripple/ripple.ts index 257a901de..c0bbdc46e 100644 --- a/integration/src/ripple/ripple.ts +++ b/integration/src/ripple/ripple.ts @@ -33,12 +33,12 @@ export function rippleTests(get: () => { wallet: core.HDWallet; info: core.HDWal "rippleGetAccountPaths()", () => { if (!wallet) return; - let paths = wallet.rippleGetAccountPaths({ accountIdx: 0 }); + const paths = wallet.rippleGetAccountPaths({ accountIdx: 0 }); expect(paths.length > 0).toBe(true); expect(paths[0].addressNList[0] > 0x80000000).toBe(true); paths.forEach((path) => { - let curAddr = path.addressNList.join(); - let nextAddr = core.mustBeDefined(wallet.rippleNextAccountPath(path)).addressNList.join(); + const curAddr = path.addressNList.join(); + const nextAddr = core.mustBeDefined(wallet.rippleNextAccountPath(path)).addressNList.join(); expect(nextAddr === undefined || nextAddr !== curAddr).toBeTruthy(); }); }, @@ -64,9 +64,9 @@ export function rippleTests(get: () => { wallet: core.HDWallet; info: core.HDWal async () => { if (!wallet) return; - let res = await wallet.rippleSignTx({ + const res = await wallet.rippleSignTx({ addressNList: core.bip32ToAddressNList(`m/44'/144'/0'/0/0`), - tx: (tx01_unsigned as unknown) as core.RippleTx, + tx: tx01_unsigned as unknown as core.RippleTx, flags: undefined, sequence: "3", lastLedgerSequence: "0", @@ -76,7 +76,7 @@ export function rippleTests(get: () => { wallet: core.HDWallet; info: core.HDWal destinationTag: "1234567890", }, }); - expect(res).toEqual((tx01_signed as unknown) as core.RippleTx); + expect(res).toEqual(tx01_signed as unknown as core.RippleTx); }, TIMEOUT ); diff --git a/integration/src/secret/secret.ts b/integration/src/secret/secret.ts index fd53afca8..afbc6e13f 100644 --- a/integration/src/secret/secret.ts +++ b/integration/src/secret/secret.ts @@ -41,6 +41,7 @@ export function secretTests(get: () => { wallet: core.HDWallet; info: core.HDWal TIMEOUT ); + // eslint-disable-next-line jest/no-disabled-tests test.skip( "describePath() secret", async () => { @@ -50,7 +51,7 @@ export function secretTests(get: () => { wallet: core.HDWallet; info: core.HDWal path: core.bip32ToAddressNList("m/44'/529'/0'/0/0"), coin: "Secret", }) - ); + ).toMatchInlineSnapshot(); }, TIMEOUT ); @@ -78,9 +79,7 @@ export function secretTests(get: () => { wallet: core.HDWallet; info: core.HDWal tx: tx_unsigned as any, addressNList: core.bip32ToAddressNList("m/44'/529'/0'/0/0"), chain_id: tx_verbose.accountInfo.chainId, - // @ts-ignore account_number: tx_verbose.accountInfo.accountNumber, - // @ts-ignore sequence: tx_verbose.accountInfo.sequence, }; @@ -90,6 +89,7 @@ export function secretTests(get: () => { wallet: core.HDWallet; info: core.HDWal //expect(res?.signatures?.[0].signature).toEqual(tx_signed.tx.signatures[0].signature_keepkey); break; default: + // eslint-disable-next-line jest/no-conditional-expect expect(res?.signatures?.[0].signature).toEqual(tx_signed.signatures[0].signature); break; } diff --git a/integration/src/terra/terra.ts b/integration/src/terra/terra.ts index 4fea9030f..fad552278 100644 --- a/integration/src/terra/terra.ts +++ b/integration/src/terra/terra.ts @@ -49,7 +49,13 @@ export function terraTests(get: () => { wallet: core.HDWallet; info: core.HDWall path: core.bip32ToAddressNList("m/44'/931'/0'/0/0"), coin: "Terra", }) - ); + ).toMatchInlineSnapshot(` + Object { + "coin": "Terra", + "isKnown": false, + "verbose": "m/44'/931'/0'/0/0", + } + `); }, TIMEOUT ); diff --git a/integration/src/thorchain/thorchain.ts b/integration/src/thorchain/thorchain.ts index db039200f..be1dbbbe9 100644 --- a/integration/src/thorchain/thorchain.ts +++ b/integration/src/thorchain/thorchain.ts @@ -2,7 +2,6 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import tx_unsigned from "./tx02.mainnet.thorchain.json"; import tx_signed from "./tx02.mainnet.thorchain.signed.json"; - import tx_unsigned_swap from "./tx03.mainnet.thorchain.swap.json"; import tx_signed_swap from "./tx03.mainnet.thorchain.swap.signed.json"; @@ -47,12 +46,36 @@ export function thorchainTests(get: () => { wallet: core.HDWallet; info: core.HD "describePath() thorchain", async () => { if (!wallet) return; - expect( - wallet.describePath({ - path: core.bip32ToAddressNList("m/44'/931'/0'/0/0"), - coin: "Thorchain", - }) - ); + + const out = wallet.describePath({ + path: core.bip32ToAddressNList("m/44'/931'/0'/0/0"), + coin: "Thorchain", + }); + + // This is strange, and probably wrong, behavior... but it's what happens. + if (wallet.getVendor() === "KeepKey") { + // eslint-disable-next-line jest/no-conditional-expect + expect(out).toMatchInlineSnapshot(` + Object { + "coin": "Thorchain", + "isKnown": false, + "scriptType": undefined, + "verbose": "m/44'/931'/0'/0/0", + } + `); + } else { + // eslint-disable-next-line jest/no-conditional-expect + expect(out).toMatchInlineSnapshot(` + Object { + "accountIdx": 0, + "coin": "Thorchain", + "isKnown": true, + "isPrefork": false, + "verbose": "Thorchain Account #0", + "wholeAccount": true, + } + `); + } }, TIMEOUT ); @@ -86,7 +109,6 @@ export function thorchainTests(get: () => { wallet: core.HDWallet; info: core.HD const res = await wallet.thorchainSignTx(input); expect(res?.signatures?.[0].signature).toEqual(tx_signed.signatures[0].signature); - }, TIMEOUT ); @@ -104,8 +126,7 @@ export function thorchainTests(get: () => { wallet: core.HDWallet; info: core.HD }; const res = await wallet.thorchainSignTx(input); - expect(res?.signatures?.[0].signature).toEqual(tx_signed_swap.signatures[0].signature) - + expect(res?.signatures?.[0].signature).toEqual(tx_signed_swap.signatures[0].signature); }, TIMEOUT ); diff --git a/integration/src/wallets/keepkey.ts b/integration/src/wallets/keepkey.ts index a161e31e1..29d843934 100644 --- a/integration/src/wallets/keepkey.ts +++ b/integration/src/wallets/keepkey.ts @@ -2,11 +2,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; import * as keepkeyNodeWebUSB from "@shapeshiftoss/hdwallet-keepkey-nodewebusb"; import * as keepkeyTcp from "@shapeshiftoss/hdwallet-keepkey-tcp"; -import * as debug from "debug"; import AxiosHTTPAdapter from "axios/lib/adapters/http"; -const log = debug.default("keepkey"); - const TIMEOUT = 60 * 1000; export function name(): string { @@ -16,37 +13,46 @@ export function name(): string { async function getBridge(keyring: core.Keyring) { try { const tcpAdapter = keepkeyTcp.TCPKeepKeyAdapter.useKeyring(keyring); - const wallet = await tcpAdapter.pairRawDevice({ - baseURL: "http://localhost:1646", - adapter: AxiosHTTPAdapter, - }, true); - if (wallet) console.log("Using KeepKey Bridge for tests"); + const wallet = await tcpAdapter.pairRawDevice( + { + baseURL: "http://localhost:1646", + adapter: AxiosHTTPAdapter, + }, + true + ); + if (wallet) console.info("Using KeepKey Bridge for tests"); return wallet; - } catch (e) {} - return undefined; + } catch { + return undefined; + } } async function getDevice(keyring: core.Keyring) { try { const keepkeyAdapter = keepkeyNodeWebUSB.NodeWebUSBKeepKeyAdapter.useKeyring(keyring); - let wallet = await keepkeyAdapter.pairDevice(undefined, true); - if (wallet) console.log("Using attached WebUSB KeepKey for tests"); + const wallet = await keepkeyAdapter.pairDevice(undefined, true); + if (wallet) console.info("Using attached WebUSB KeepKey for tests"); return wallet; - } catch (e) {} - return undefined; + } catch { + return undefined; + } } async function getEmulator(keyring: core.Keyring) { try { const tcpAdapter = keepkeyTcp.TCPKeepKeyAdapter.useKeyring(keyring); - const wallet = await tcpAdapter.pairRawDevice({ - baseURL: "http://localhost:5000", - adapter: AxiosHTTPAdapter, - }, true); - if (wallet) console.log("Using KeepKey Emulator for tests"); + const wallet = await tcpAdapter.pairRawDevice( + { + baseURL: "http://localhost:5000", + adapter: AxiosHTTPAdapter, + }, + true + ); + if (wallet) console.info("Using KeepKey Emulator for tests"); return wallet; - } catch (e) {} - return undefined; + } catch { + return undefined; + } } let autoButton = true; @@ -58,7 +64,7 @@ export function createInfo(): core.HDWalletInfo { export async function createWallet(): Promise { const keyring = new core.Keyring(); - const wallet = ((await getBridge(keyring) || await getDevice(keyring))) || (await getEmulator(keyring)); + const wallet = (await getBridge(keyring)) || (await getDevice(keyring)) || (await getEmulator(keyring)); if (!wallet) throw new Error("No suitable test KeepKey found"); @@ -68,10 +74,6 @@ export async function createWallet(): Promise { } }); - wallet.transport?.onAny((event: string | string[], ...values: any[]) => { - //console.info(event, ...values) - }); - return wallet; } @@ -79,9 +81,12 @@ export function selfTest(get: () => core.HDWallet): void { let wallet: keepkey.KeepKeyHDWallet & core.BTCWallet & core.ETHWallet & core.HDWallet; beforeAll(async () => { - let w = get(); - if (keepkey.isKeepKey(w) && core.supportsBTC(w) && core.supportsETH(w)) wallet = w; - else fail("Wallet is not a KeepKey"); + const w = get(); + if (keepkey.isKeepKey(w) && core.supportsBTC(w) && core.supportsETH(w)) { + wallet = w; + } else { + throw new Error("Wallet is not a KeepKey"); + } await wallet.wipe(); await wallet.loadDevice({ @@ -107,7 +112,7 @@ export function selfTest(get: () => core.HDWallet): void { it("uses the same BIP32 paths for ETH as the KeepKey Client", () => { if (!wallet) return; [0, 1, 3, 27].forEach((account) => { - let paths = wallet.ethGetAccountPaths({ + const paths = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: account, }); @@ -135,8 +140,8 @@ export function selfTest(get: () => core.HDWallet): void { async () => { if (!wallet) return; - let addrs = [] as string[]; - await new Promise(async (resolve) => { + const addrs = [] as string[]; + await new Promise((resolve) => { wallet .btcGetAddress({ coin: "Bitcoin", @@ -180,6 +185,7 @@ export function selfTest(get: () => core.HDWallet): void { // TODO: it would appear cancel is not working as expected and resulting in a hanging test. // revisit and look into how cancel is implemented to fix and make test pass + // eslint-disable-next-line jest/no-disabled-tests test.skip( "cancel works", async () => { @@ -223,7 +229,7 @@ export function selfTest(get: () => core.HDWallet): void { it("uses correct bip44 paths", () => { if (!wallet) return; - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: "Litecoin", accountIdx: 3, }); @@ -250,7 +256,7 @@ export function selfTest(get: () => core.HDWallet): void { it("supports ethNextAccountPath", () => { if (!wallet) return; - let paths = wallet.ethGetAccountPaths({ + const paths = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: 5, }); @@ -280,7 +286,7 @@ export function selfTest(get: () => core.HDWallet): void { it("supports btcNextAccountPath", () => { if (!wallet) return; - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: "Litecoin", accountIdx: 3, }); diff --git a/integration/src/wallets/ledger.ts b/integration/src/wallets/ledger.ts index 498c490c4..234872274 100644 --- a/integration/src/wallets/ledger.ts +++ b/integration/src/wallets/ledger.ts @@ -20,7 +20,7 @@ export class MockTransport extends ledger.LedgerTransport { method: U, ...args: Parameters> ): Promise> { - let key = JSON.stringify({ coin: coin, method: method, args: args }); + const key = JSON.stringify({ coin: coin, method: method, args: args }); if (!this.memoized.has(key)) { console.error(coin, method, `JSON.parse('${JSON.stringify(args)}')`); @@ -31,7 +31,7 @@ export class MockTransport extends ledger.LedgerTransport { } public memoize(coin: string | null, method: string, args: any, response: any) { - let key = JSON.stringify({ coin: coin, method: method, args: args }); + const key = JSON.stringify({ coin: coin, method: method, args: args }); this.memoized.set(key, response); } @@ -215,8 +215,8 @@ export function createInfo(): core.HDWalletInfo { } export async function createWallet(type: any = "Bitcoin"): Promise { - let keyring = new core.Keyring(); - let transport = new MockTransport(keyring, type); + const keyring = new core.Keyring(); + const transport = new MockTransport(keyring, type); return ledger.create(transport as any); } @@ -224,9 +224,12 @@ export function selfTest(get: () => core.HDWallet): void { let wallet: ledger.LedgerHDWallet & core.ETHWallet & core.BTCWallet & core.HDWallet; beforeAll(async () => { - let w = get(); - if (ledger.isLedger(w) && core.supportsBTC(w) && core.supportsETH(w)) wallet = w; - else fail("Wallet is not a Ledger"); + const w = get(); + if (ledger.isLedger(w) && core.supportsBTC(w) && core.supportsETH(w)) { + wallet = w; + } else { + throw new Error("Wallet is not a Ledger"); + } }); it("supports Ethereum mainnet", async () => { @@ -248,7 +251,7 @@ export function selfTest(get: () => core.HDWallet): void { it("validates current app", async () => { if (!wallet) return; - expect(await wallet.validateCurrentApp("Bitcoin")).resolves; + await expect(wallet.validateCurrentApp("Bitcoin")).resolves.not.toThrow(); await expect(wallet.validateCurrentApp(undefined)).rejects.toThrow(); // no coin await expect(wallet.validateCurrentApp("FakeCoin")).rejects.toThrow(); // invalid coin await expect(wallet.validateCurrentApp("Ethereum")).rejects.toThrow(); // wrong coin @@ -257,7 +260,7 @@ export function selfTest(get: () => core.HDWallet): void { it("has a non-BIP 44 derivation path for Ethereum", () => { if (!wallet) return; [0, 1, 3, 27].forEach((account) => { - let paths = wallet.ethGetAccountPaths({ + const paths = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: account, }); @@ -289,7 +292,7 @@ export function selfTest(get: () => core.HDWallet): void { it("uses correct bip44 paths", () => { if (!wallet) return; - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: "Litecoin", accountIdx: 3, }); @@ -316,10 +319,12 @@ export function selfTest(get: () => core.HDWallet): void { it("supports btcNextAccountPath", () => { if (!wallet) return; - let paths = core.mustBeDefined(wallet.btcGetAccountPaths({ - coin: "Litecoin", - accountIdx: 3, - })); + const paths = core.mustBeDefined( + wallet.btcGetAccountPaths({ + coin: "Litecoin", + accountIdx: 3, + }) + ); expect( paths diff --git a/integration/src/wallets/metamask.ts b/integration/src/wallets/metamask.ts index f866f5198..560096e5a 100644 --- a/integration/src/wallets/metamask.ts +++ b/integration/src/wallets/metamask.ts @@ -19,12 +19,12 @@ export function selfTest(get: () => core.HDWallet): void { let wallet: metamask.MetaMaskHDWallet; beforeAll(async () => { - let w = get() as metamask.MetaMaskHDWallet; + const w = get() as metamask.MetaMaskHDWallet; if (metamask.isMetaMask(w) && !core.supportsBTC(w) && core.supportsETH(w)) { wallet = w; } else { - fail("Wallet is not a MetaMask"); + throw new Error("Wallet is not a MetaMask"); } }); @@ -56,7 +56,7 @@ export function selfTest(get: () => core.HDWallet): void { it("uses correct eth bip44 paths", () => { if (!wallet) return; [0, 1, 3, 27].forEach((account) => { - let paths = wallet.ethGetAccountPaths({ + const paths = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: account, }); @@ -119,7 +119,7 @@ export function selfTest(get: () => core.HDWallet): void { }); }); - it('should return a valid ETH address', async () => { + it("should return a valid ETH address", async () => { if (!wallet) return; expect( await wallet.ethGetAddress({ @@ -127,11 +127,11 @@ export function selfTest(get: () => core.HDWallet): void { showDisplay: false, }) ).toEqual("0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8"); - }) + }); - it('should sign a message', async () => { + it("should sign a message", async () => { if (!wallet) return; - let res = await wallet.ethSignMessage({ + const res = await wallet.ethSignMessage({ addressNList: core.bip32ToAddressNList("m/44'/60'/0'/0/0"), message: "Hello World", }); diff --git a/integration/src/wallets/mocks/@metamask/detect-provider.ts b/integration/src/wallets/mocks/@metamask/detect-provider.ts index 619aef2e5..72b204a75 100644 --- a/integration/src/wallets/mocks/@metamask/detect-provider.ts +++ b/integration/src/wallets/mocks/@metamask/detect-provider.ts @@ -3,19 +3,21 @@ export const ethereum = { switch (method) { case "eth_accounts": return ["0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8"]; - case "personal_sign": + case "personal_sign": { const [message] = params; - if (message === '48656c6c6f20576f726c64') - return '0x29f7212ecc1c76cea81174af267b67506f754ea8c73f144afa900a0d85b24b21319621aeb062903e856352f38305710190869c3ce5a1425d65ef4fa558d0fc251b' + if (message === "48656c6c6f20576f726c64") + return "0x29f7212ecc1c76cea81174af267b67506f754ea8c73f144afa900a0d85b24b21319621aeb062903e856352f38305710190869c3ce5a1425d65ef4fa558d0fc251b"; - throw new Error('unknown message'); - case "eth_sendTransaction": + throw new Error("unknown message"); + } + case "eth_sendTransaction": { const [{ to }] = params; return `txHash-${to}`; + } default: throw new Error(`ethereum: Unkown method ${method}`); } - }) -} \ No newline at end of file + }), +}; diff --git a/integration/src/wallets/native.ts b/integration/src/wallets/native.ts index 9acb1a8d5..0b24af35b 100644 --- a/integration/src/wallets/native.ts +++ b/integration/src/wallets/native.ts @@ -3,6 +3,7 @@ import * as native from "@shapeshiftoss/hdwallet-native"; import * as _ from "lodash"; // TODO: clean this up +// eslint-disable-next-line jest/no-mocks-import import mswMock from "../../../packages/hdwallet-native/__mocks__/mswMock"; const mnemonic = "all all all all all all all all all all all all"; @@ -16,13 +17,6 @@ export function createInfo(): core.HDWalletInfo { return native.info(); } -export async function createWallet(): Promise { - const mswMock = await setupMswMocks(); - const wallet = new native.NativeHDWallet({ mnemonic, deviceId }); - await wallet.initialize(); - return wallet; -} - export async function setupMswMocks() { const binanceMocks = { get: { @@ -173,16 +167,23 @@ export async function setupMswMocks() { return mswMock(_.merge({}, binanceMocks, fioMocks)).startServer(); } +export async function createWallet(): Promise { + await setupMswMocks(); + const wallet = new native.NativeHDWallet({ mnemonic, deviceId }); + await wallet.initialize(); + return wallet; +} + export function selfTest(get: () => core.HDWallet): void { let wallet: native.NativeHDWallet; beforeAll(async () => { - let w = get() as native.NativeHDWallet; + const w = get() as native.NativeHDWallet; if (native.isNative(w) && core.supportsBTC(w) && core.supportsETH(w)) { wallet = w; } else { - fail("Wallet is not native"); + throw new Error("Wallet is not native"); } }); @@ -206,7 +207,7 @@ export function selfTest(get: () => core.HDWallet): void { it("uses correct eth bip44 paths", () => { if (!wallet) return; [0, 1, 3, 27].forEach((account) => { - let paths = core.mustBeDefined( + const paths = core.mustBeDefined( wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: account, @@ -234,7 +235,7 @@ export function selfTest(get: () => core.HDWallet): void { it("uses correct btc bip44 paths", () => { if (!wallet) return; - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: "Litecoin", accountIdx: 3, }); @@ -258,10 +259,11 @@ export function selfTest(get: () => core.HDWallet): void { ]); }); + // eslint-disable-next-line jest/no-disabled-tests it.skip("supports ethNextAccountPath", () => { if (!wallet) return; - let paths = core.mustBeDefined( + const paths = core.mustBeDefined( wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: 5, @@ -293,7 +295,7 @@ export function selfTest(get: () => core.HDWallet): void { it("supports btcNextAccountPath", () => { if (!wallet) return; - let paths = core.mustBeDefined( + const paths = core.mustBeDefined( wallet.btcGetAccountPaths({ coin: "Litecoin", accountIdx: 3, @@ -400,6 +402,7 @@ export function selfTest(get: () => core.HDWallet): void { }); }); + // eslint-disable-next-line jest/no-disabled-tests it.skip("can describe prefork BitcoinCash", () => { expect( wallet.describePath({ @@ -420,6 +423,7 @@ export function selfTest(get: () => core.HDWallet): void { }); }); + // eslint-disable-next-line jest/no-disabled-tests it.skip("can describe prefork Segwit Native BTG", () => { expect( wallet.describePath({ @@ -440,26 +444,7 @@ export function selfTest(get: () => core.HDWallet): void { }); }); - it.skip("can describe Bitcoin Change Addresses", () => { - expect( - wallet.describePath({ - path: core.bip32ToAddressNList("m/44'/0'/7'/1/5"), - coin: "Bitcoin", - scriptType: core.BTCInputScriptType.SpendAddress, - }) - ).toEqual({ - verbose: "Bitcoin Account #7, Change Address #5 (Legacy)", - coin: "Bitcoin", - isKnown: true, - scriptType: core.BTCInputScriptType.SpendAddress, - accountIdx: 7, - addressIdx: 5, - wholeAccount: false, - isChange: true, - isPrefork: false, - }); - }); - + // eslint-disable-next-line jest/no-disabled-tests it.skip("can describe prefork paths", () => { expect( wallet.describePath({ diff --git a/integration/src/wallets/portis.ts b/integration/src/wallets/portis.ts index 785e2d6d4..86df653c3 100644 --- a/integration/src/wallets/portis.ts +++ b/integration/src/wallets/portis.ts @@ -17,8 +17,7 @@ const mockSignEthTxResponse = { s: "0x28297d012cccf389f6332415e96ee3fc0bbf8474d05f646e029cd281a031464b", v: 38, }, - raw: - "0xf86b018501dcd650008256229412ec06288edd7ae2cc41a843fe089237fc7354f0872c68af0bb140008026a063db3dd3bf3e1fe7dde1969c0fc8850e34116d0b501c0483a0e08c0f77b8ce0aa028297d012cccf389f6332415e96ee3fc0bbf8474d05f646e029cd281a031464b", + raw: "0xf86b018501dcd650008256229412ec06288edd7ae2cc41a843fe089237fc7354f0872c68af0bb140008026a063db3dd3bf3e1fe7dde1969c0fc8850e34116d0b501c0483a0e08c0f77b8ce0aa028297d012cccf389f6332415e96ee3fc0bbf8474d05f646e029cd281a031464b", }; const mockSignERC20TxResponse = { @@ -27,8 +26,7 @@ const mockSignERC20TxResponse = { s: "0x10efa4dd6fdb381c317db8f815252c2ac0d2a883bd364901dee3dec5b7d3660a", v: 37, }, - raw: - "0xf8a20114149441e5560054824ea6b0732e656e3ad64e20e94e4580b844a9059cbb0000000000000000000000001d8ce9022f6284c3a5c317f8f34620107214e54500000000000000000000000000000000000000000000000000000002540be40025a01238fd332545415f09a01470350a5a20abc784dbf875cf58f7460560e66c597fa010efa4dd6fdb381c317db8f815252c2ac0d2a883bd364901dee3dec5b7d3660a", + raw: "0xf8a20114149441e5560054824ea6b0732e656e3ad64e20e94e4580b844a9059cbb0000000000000000000000001d8ce9022f6284c3a5c317f8f34620107214e54500000000000000000000000000000000000000000000000000000002540be40025a01238fd332545415f09a01470350a5a20abc784dbf875cf58f7460560e66c597fa010efa4dd6fdb381c317db8f815252c2ac0d2a883bd364901dee3dec5b7d3660a", }; export async function createWallet(): Promise { @@ -64,9 +62,12 @@ export function selfTest(get: () => core.HDWallet): void { let wallet: portis.PortisHDWallet & core.ETHWallet & core.HDWallet; beforeAll(() => { - let w = get(); - if (portis.isPortis(w) && core.supportsETH(w)) wallet = w; - else fail("Wallet is not Portis"); + const w = get(); + if (portis.isPortis(w) && core.supportsETH(w)) { + wallet = w; + } else { + throw new Error("Wallet is not Portis"); + } }); it("supports Ethereum mainnet", async () => { @@ -81,10 +82,12 @@ export function selfTest(get: () => core.HDWallet): void { it("does not support more than one account path", async () => { if (!wallet) return; - const paths = core.mustBeDefined(await wallet.ethGetAccountPaths({ - coin: "Ethereum", - accountIdx: 0, - })); + const paths = core.mustBeDefined( + await wallet.ethGetAccountPaths({ + coin: "Ethereum", + accountIdx: 0, + }) + ); expect(paths.length).toEqual(1); const nextPath = await wallet.ethNextAccountPath(paths[0]); expect(nextPath).toBeUndefined(); diff --git a/integration/src/wallets/trezor.ts b/integration/src/wallets/trezor.ts index 1c06c8ea4..e8e5b3add 100644 --- a/integration/src/wallets/trezor.ts +++ b/integration/src/wallets/trezor.ts @@ -13,8 +13,8 @@ export class MockTransport extends trezor.TrezorTransport { return "mock#1"; } - public call(method: string, msg: any, msTimeout?: number): Promise { - let key = JSON.stringify({ method: method, msg: msg }); + public call(method: string, msg: any): Promise { + const key = JSON.stringify({ method: method, msg: msg }); if (!this.memoized.has(key)) { console.error(method, `JSON.parse('${JSON.stringify(msg)}')`); throw new Error("mock not yet recorded for arguments"); @@ -27,7 +27,7 @@ export class MockTransport extends trezor.TrezorTransport { } public memoize(method: string, msg: any, response: any) { - let key = JSON.stringify({ method: method, msg: msg }); + const key = JSON.stringify({ method: method, msg: msg }); this.memoized.set(key, response); } @@ -66,8 +66,7 @@ export class MockTransport extends trezor.TrezorTransport { transaction: { to: "0x41e5560054824ea6b0732e656e3ad64e20e94e45", value: "0x00", - data: - "0xa9059cbb0000000000000000000000001d8ce9022f6284c3a5c317f8f34620107214e54500000000000000000000000000000000000000000000000000000002540be400", + data: "0xa9059cbb0000000000000000000000001d8ce9022f6284c3a5c317f8f34620107214e54500000000000000000000000000000000000000000000000000000002540be400", chainId: 1, nonce: "0x01", gasLimit: "0x14", @@ -120,9 +119,7 @@ export class MockTransport extends trezor.TrezorTransport { try { this.memoize( "getAddress", - JSON.parse( - '{"path":"m/49\'/0\'/0\'/0/0","showOnTrezor":true,"coin":"btc"}' - ), + JSON.parse('{"path":"m/49\'/0\'/0\'/0/0","showOnTrezor":true,"coin":"btc"}'), JSON.parse( '{"payload":{"address":"3AnYTd2FGxJLNKL1AzxfW3FJMntp9D2KKX","path":[2147483697,2147483648,2147483648,0,0],"serializedPath":"m/49\'/0\'/0\'/0/0"},"id":2,"success":true}' ) @@ -185,9 +182,7 @@ export class MockTransport extends trezor.TrezorTransport { this.memoize("wipeDevice", {}, { success: true }); this.memoize( "getAddress", - JSON.parse( - '{"path":"m/44\'/0\'/0\'/0/0","showOnTrezor":false,"coin":"btc"}' - ), + JSON.parse('{"path":"m/44\'/0\'/0\'/0/0","showOnTrezor":false,"coin":"btc"}'), JSON.parse( '{"payload":{"address":"1FH6ehAd5ZFXCM1cLGzHxK1s4dGdq1JusM","path":[2147483692,2147483648,2147483648,0,0],"serializedPath":"m/44\'/0\'/0\'/0/0"},"id":2,"success":true}' ) @@ -203,27 +198,21 @@ export class MockTransport extends trezor.TrezorTransport { ); this.memoize( "getAddress", - JSON.parse( - '{"path":"m/49\'/2\'/0\'/0/0","showOnTrezor":false,"coin":"ltc"}' - ), + JSON.parse('{"path":"m/49\'/2\'/0\'/0/0","showOnTrezor":false,"coin":"ltc"}'), JSON.parse( '{"payload":{"address":"MFoQRU1KQq365Sy3cXhix3ygycEU4YWB1V","path":[2147483697,2147483650,2147483648,0,0],"serializedPath":"m/49\'/2\'/0\'/0/0"},"id":2,"success":true}' ) ); this.memoize( "getAddress", - JSON.parse( - '{"path":"m/44\'/5\'/0\'/0/0","showOnTrezor":false,"coin":"dash"}' - ), + JSON.parse('{"path":"m/44\'/5\'/0\'/0/0","showOnTrezor":false,"coin":"dash"}'), JSON.parse( '{"payload":{"address":"XxKhGNv6ECbqVswm9KYcLPQnyWgZ86jJ6Q","path":[2147483692,2147483653,2147483648,0,0],"serializedPath":"m/44\'/5\'/0\'/0/0"},"id":2,"success":true}' ) ); this.memoize( "getAddress", - JSON.parse( - '{"path":"m/44\'/2\'/0\'/0/0","showOnTrezor":false,"coin":"ltc"}' - ), + JSON.parse('{"path":"m/44\'/2\'/0\'/0/0","showOnTrezor":false,"coin":"ltc"}'), JSON.parse( '{"payload":{"address":"LYXTv5RdsPYKC4qGmb6x6SuKoFMxUdSjLQ","path":[2147483692,2147483650,2147483648,0,0],"serializedPath":"m/44\'/2\'/0\'/0/0"},"id":2,"success":true}' ) @@ -239,18 +228,14 @@ export class MockTransport extends trezor.TrezorTransport { ); this.memoize( "getAddress", - JSON.parse( - '{"path":"m/84\'/2\'/0\'/0/0","showOnTrezor":false,"coin":"ltc"}' - ), + JSON.parse('{"path":"m/84\'/2\'/0\'/0/0","showOnTrezor":false,"coin":"ltc"}'), JSON.parse( '{"payload":{"address":"ltc1qf6pwfkw4wd0fetq2pfrwzlfknskjg6nyvt6ngv","path":[2147483732,2147483650,2147483648,0,0],"serializedPath":"m/84\'/2\'/0\'/0/0"},"id":2,"success":true}' ) ); this.memoize( "getAddress", - JSON.parse( - '{"path":"m/84\'/2\'/0\'/0/0","showOnTrezor":false,"coin":"ltc"}' - ), + JSON.parse('{"path":"m/84\'/2\'/0\'/0/0","showOnTrezor":false,"coin":"ltc"}'), JSON.parse( '{"payload":{"address":"ltc1qf6pwfkw4wd0fetq2pfrwzlfknskjg6nyvt6ngv","path":[2147483732,2147483650,2147483648,0,0],"serializedPath":"m/84\'/2\'/0\'/0/0"},"id":2,"success":true}' ) @@ -271,18 +256,14 @@ export class MockTransport extends trezor.TrezorTransport { ); this.memoize( "getAddress", - JSON.parse( - '{"path":"m/49\'/0\'/0\'/0/0","showOnTrezor":false,"coin":"btc"}' - ), + JSON.parse('{"path":"m/49\'/0\'/0\'/0/0","showOnTrezor":false,"coin":"btc"}'), JSON.parse( '{"payload":{"address":"3AnYTd2FGxJLNKL1AzxfW3FJMntp9D2KKX","path":[2147483697,2147483648,2147483648,0,0],"serializedPath":"m/49\'/0\'/0\'/0/0"},"id":2,"success":true}' ) ); this.memoize( "getAddress", - JSON.parse( - '{"path":"m/49\'/0\'/0\'/0/0","showOnTrezor":false,"coin":"btc"}' - ), + JSON.parse('{"path":"m/49\'/0\'/0\'/0/0","showOnTrezor":false,"coin":"btc"}'), JSON.parse( '{"payload":{"address":"3AnYTd2FGxJLNKL1AzxfW3FJMntp9D2KKX","path":[2147483697,2147483648,2147483648,0,0],"serializedPath":"m/49\'/0\'/0\'/0/0"},"id":2,"success":true}' ) @@ -353,8 +334,8 @@ export function name(): string { } export async function createWallet(): Promise { - let keyring = new core.Keyring(); - let transport = new MockTransport(keyring); + const keyring = new core.Keyring(); + const transport = new MockTransport(keyring); return trezor.create(transport as trezor.TrezorTransport); } @@ -366,9 +347,12 @@ export function selfTest(get: () => core.HDWallet): void { let wallet: trezor.TrezorHDWallet & core.ETHWallet & core.BTCWallet & core.HDWallet; beforeAll(async () => { - let w = get(); - if (trezor.isTrezor(w) && core.supportsBTC(w) && core.supportsETH(w)) wallet = w; - else fail("Wallet is not a Trezor"); + const w = get(); + if (trezor.isTrezor(w) && core.supportsBTC(w) && core.supportsETH(w)) { + wallet = w; + } else { + throw new Error("Wallet is not a Trezor"); + } }); it("supports Ethereum mainnet", async () => { @@ -391,7 +375,7 @@ export function selfTest(get: () => core.HDWallet): void { it("uses the same BIP32 paths for ETH as wallet.trezor.io", () => { if (!wallet) return; [0, 1, 3, 27].forEach((account) => { - let paths = wallet.ethGetAccountPaths({ + const paths = wallet.ethGetAccountPaths({ coin: "Ethereum", accountIdx: account, }); @@ -417,7 +401,7 @@ export function selfTest(get: () => core.HDWallet): void { it("uses correct bip44 paths", () => { if (!wallet) return; - let paths = wallet.btcGetAccountPaths({ + const paths = wallet.btcGetAccountPaths({ coin: "Litecoin", accountIdx: 3, }); @@ -444,7 +428,7 @@ export function selfTest(get: () => core.HDWallet): void { it("supports btcNextAccountPath", () => { if (!wallet) return; - let paths = core.mustBeDefined( + const paths = core.mustBeDefined( wallet.btcGetAccountPaths({ coin: "Litecoin", accountIdx: 3, diff --git a/integration/src/wallets/xdefi.ts b/integration/src/wallets/xdefi.ts index 1e5e0d989..155077d27 100644 --- a/integration/src/wallets/xdefi.ts +++ b/integration/src/wallets/xdefi.ts @@ -54,26 +54,28 @@ export async function createWallet(): Promise { case "eth_accounts": case "eth_requestAccounts": return ["0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8"]; - case "personal_sign": + case "personal_sign": { const [message] = params; if (message === "48656c6c6f20576f726c64") return "0x29f7212ecc1c76cea81174af267b67506f754ea8c73f144afa900a0d85b24b21319621aeb062903e856352f38305710190869c3ce5a1425d65ef4fa558d0fc251b"; throw new Error("unknown message"); - case "eth_sendTransaction": + } + case "eth_sendTransaction": { const [{ to }] = params; return `txHash-${to}`; + } default: throw new Error(`ethereum: Unknown method ${method}`); } - }) - } + }), + }, }; - const adapter = xdefi.XDeFiAdapter.useKeyring(new core.Keyring()) - const wallet = await adapter.pairDevice() + const adapter = xdefi.XDeFiAdapter.useKeyring(new core.Keyring()); + const wallet = await adapter.pairDevice(); wallet.ethSignTx = jest .fn() @@ -107,9 +109,12 @@ export function selfTest(get: () => core.HDWallet): void { let wallet: xdefi.XDeFiHDWallet & core.ETHWallet & core.HDWallet; beforeAll(() => { - let w = get(); - if (xdefi.isXDeFi(w) && core.supportsETH(w)) wallet = w; - else fail("Wallet is not XDeFi"); + const w = get(); + if (xdefi.isXDeFi(w) && core.supportsETH(w)) { + wallet = w; + } else { + throw new Error("Wallet is not XDeFi"); + } }); it("supports Ethereum mainnet", async () => { diff --git a/jest.config.js b/jest.config.js index 75e9a335c..9ca106c39 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,10 +11,10 @@ module.exports = { "^@shapeshiftoss/hdwallet-(.*)": "/hdwallet-$1/src", }, globals: { - 'ts-jest': { + "ts-jest": { diagnostics: { ignoreCodes: [7016], - } + }, }, }, setupFiles: ["fake-indexeddb/auto"], diff --git a/package.json b/package.json index 164073348..820f7a126 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ }, "scripts": { "clean": "lerna run clean --scope @shapeshiftoss/* && rm -rf coverage test-report node_modules && yarn cache clean", + "lint": "eslint --cache --max-warnings=0 .", + "lint:fix": "yarn lint --fix", "lint:ts": "tsc --noEmit", "format": "prettier .", "build": "yarn tsc --build", @@ -35,6 +37,13 @@ "postinstall": "patch-package" }, "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.14.0", + "@typescript-eslint/parser": "^5.14.0", + "eslint": "^8.10.0", + "eslint-config-prettier": "8.5.0", + "eslint-plugin-jest": "26.1.1", + "eslint-plugin-prettier": "4.0.0", + "eslint-plugin-simple-import-sort": "^7.0.0", "fake-indexeddb": "^3.1.7", "jest": "^26.6.3", "jest-environment-jsdom": "^25.5.0", diff --git a/packages/hdwallet-core/src/binance.ts b/packages/hdwallet-core/src/binance.ts index cba8e74b2..f9542bb9d 100644 --- a/packages/hdwallet-core/src/binance.ts +++ b/packages/hdwallet-core/src/binance.ts @@ -6,7 +6,9 @@ export interface BinanceGetAddress { showDisplay?: boolean; } +// eslint-disable-next-line @typescript-eslint/no-namespace namespace Binance { + // eslint-disable-next-line @typescript-eslint/no-namespace namespace sdk { export type Coins = Coin[]; @@ -100,8 +102,8 @@ export interface BinanceWallet extends BinanceWalletInfo, HDWallet { } export function binanceDescribePath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Binance", isKnown: false, @@ -127,7 +129,7 @@ export function binanceDescribePath(path: BIP32Path): PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Binance Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/bitcoin.ts b/packages/hdwallet-core/src/bitcoin.ts index 1ab52165e..1676c413a 100644 --- a/packages/hdwallet-core/src/bitcoin.ts +++ b/packages/hdwallet-core/src/bitcoin.ts @@ -388,7 +388,7 @@ export function describeUTXOPath(path: BIP32Path, coin: Coin, scriptType: BTCInp if ((path[0] & 0x80000000) >>> 0 !== 0x80000000) return unknown; - let purpose = path[0] & 0x7fffffff; + const purpose = path[0] & 0x7fffffff; if (![44, 49, 84].includes(purpose)) return unknown; @@ -396,9 +396,9 @@ export function describeUTXOPath(path: BIP32Path, coin: Coin, scriptType: BTCInp if (purpose === 49 && scriptType !== BTCInputScriptType.SpendP2SHWitness) return unknown; - let wholeAccount = path.length === 3; + const wholeAccount = path.length === 3; - let script = ( + const script = ( { [BTCInputScriptType.SpendAddress]: ["Legacy"], [BTCInputScriptType.SpendP2SHWitness]: [], @@ -445,9 +445,9 @@ export function describeUTXOPath(path: BIP32Path, coin: Coin, scriptType: BTCInp break; } - let attr = attributes.length ? ` (${attributes.join(", ")})` : ""; + const attr = attributes.length ? ` (${attributes.join(", ")})` : ""; - let accountIdx = path[2] & 0x7fffffff; + const accountIdx = path[2] & 0x7fffffff; if (wholeAccount) { return { @@ -460,8 +460,8 @@ export function describeUTXOPath(path: BIP32Path, coin: Coin, scriptType: BTCInp isPrefork, }; } else { - let change = path[3] === 1 ? "Change " : ""; - let addressIdx = path[4]; + const change = path[3] === 1 ? "Change " : ""; + const addressIdx = path[4]; return { coin, verbose: `${coin} Account #${accountIdx}, ${change}Address #${addressIdx}${attr}`, diff --git a/packages/hdwallet-core/src/cosmos.ts b/packages/hdwallet-core/src/cosmos.ts index 1eedd664d..d2393abc9 100644 --- a/packages/hdwallet-core/src/cosmos.ts +++ b/packages/hdwallet-core/src/cosmos.ts @@ -6,6 +6,7 @@ export interface CosmosGetAddress { showDisplay?: boolean; } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Cosmos { export interface Msg { type: string; @@ -24,6 +25,7 @@ export namespace Cosmos { gas: string; } + // eslint-disable-next-line @typescript-eslint/no-namespace namespace crypto { export interface PubKey { type: string; @@ -61,10 +63,10 @@ export interface CosmosSignTx { } export interface CosmosSignedTx { - serialized: string - body: string - authInfoBytes: string - signatures: string[] + serialized: string; + body: string; + authInfoBytes: string; + signatures: string[]; } export interface CosmosGetAccountPaths { @@ -98,8 +100,8 @@ export interface CosmosWallet extends CosmosWalletInfo, HDWallet { } export function cosmosDescribePath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Atom", isKnown: false, @@ -125,7 +127,7 @@ export function cosmosDescribePath(path: BIP32Path): PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Cosmos Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/eos.ts b/packages/hdwallet-core/src/eos.ts index 73201f418..42548568d 100644 --- a/packages/hdwallet-core/src/eos.ts +++ b/packages/hdwallet-core/src/eos.ts @@ -18,6 +18,7 @@ export interface eosNextAccountPath { accountIdx: number; } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Eos { export interface EosPermissionLevel { actor: string; @@ -50,6 +51,7 @@ export interface EosToSignTx { } /* device response asking for next action */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface export interface EosTxActionRequest {} /* diff --git a/packages/hdwallet-core/src/ethereum.ts b/packages/hdwallet-core/src/ethereum.ts index db1a6ec03..25d4908b7 100644 --- a/packages/hdwallet-core/src/ethereum.ts +++ b/packages/hdwallet-core/src/ethereum.ts @@ -1,5 +1,5 @@ import { addressNListToBIP32, slip44ByCoin } from "./utils"; -import { ExchangeType, BIP32Path, HDWallet, HDWalletInfo, PathDescription } from "./wallet"; +import { BIP32Path, ExchangeType, HDWallet, HDWalletInfo, PathDescription } from "./wallet"; export enum ETHTransactionType { ETH_TX_TYPE_LEGACY = 0, @@ -142,8 +142,8 @@ export interface ETHWallet extends ETHWalletInfo, HDWallet { } export function describeETHPath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Ethereum", isKnown: false, @@ -161,7 +161,7 @@ export function describeETHPath(path: BIP32Path): PathDescription { if (path[4] !== 0) return unknown; - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Ethereum Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/fio.ts b/packages/hdwallet-core/src/fio.ts index f8da72547..be8e411fd 100644 --- a/packages/hdwallet-core/src/fio.ts +++ b/packages/hdwallet-core/src/fio.ts @@ -18,6 +18,7 @@ export interface FioNextAccountPath { accountIdx: number; } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Fio { export interface PermissionLevel { actor?: string; @@ -229,8 +230,8 @@ export interface FioWallet extends FioWalletInfo, HDWallet { } export function fioDescribePath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Fio", isKnown: false, @@ -256,7 +257,7 @@ export function fioDescribePath(path: BIP32Path): PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Fio Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/kava.ts b/packages/hdwallet-core/src/kava.ts index 17d33d685..dae27f3bf 100644 --- a/packages/hdwallet-core/src/kava.ts +++ b/packages/hdwallet-core/src/kava.ts @@ -7,6 +7,7 @@ export interface KavaGetAddress { testnet?: boolean; } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Kava { export interface Msg { type: string; @@ -25,6 +26,7 @@ export namespace Kava { gas: string; } + // eslint-disable-next-line @typescript-eslint/no-namespace namespace crypto { export interface PubKey { type: string; @@ -95,8 +97,8 @@ export interface KavaWallet extends KavaWalletInfo, HDWallet { } export function kavaDescribePath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Kava", isKnown: false, @@ -122,7 +124,7 @@ export function kavaDescribePath(path: BIP32Path): PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Kava Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/keyring.ts b/packages/hdwallet-core/src/keyring.ts index ca9de78fb..4701eb654 100644 --- a/packages/hdwallet-core/src/keyring.ts +++ b/packages/hdwallet-core/src/keyring.ts @@ -33,11 +33,13 @@ export class Keyring extends eventemitter2.EventEmitter2 { } public async exec(method: string, ...args: any[]): Promise<{ [deviceID: string]: any }> { - return Promise.all(Object.values(this.wallets).map((w) => { - const fn: unknown = (w as any)[method]; - if (typeof fn !== "function") throw new Error(`can't exec non-existent method ${method}`); - return fn.call(w, ...args); - })).then((values) => + return Promise.all( + Object.values(this.wallets).map((w) => { + const fn: unknown = (w as any)[method]; + if (typeof fn !== "function") throw new Error(`can't exec non-existent method ${method}`); + return fn.call(w, ...args); + }) + ).then((values) => values.reduce((final, response, i) => { final[Object.keys(this.wallets)[i]] = response; return final; @@ -62,7 +64,7 @@ export class Keyring extends eventemitter2.EventEmitter2 { } catch (e) { console.error(e); } finally { - let aliasee = this.aliases[deviceID]; + const aliasee = this.aliases[deviceID]; if (aliasee) { delete this.aliases[deviceID]; delete this.wallets[aliasee]; @@ -88,6 +90,8 @@ export class Keyring extends eventemitter2.EventEmitter2 { const wallet: HDWallet | null = this.get(deviceID); if (!wallet) return; const vendor: string = wallet.getVendor(); - events.onAny((e: string | string[], ...values: any[]) => this.emit([vendor, deviceID, (typeof e === "string" ? e : e.join(";"))], [deviceID, ...values])); + events.onAny((e: string | string[], ...values: any[]) => + this.emit([vendor, deviceID, typeof e === "string" ? e : e.join(";")], [deviceID, ...values]) + ); } } diff --git a/packages/hdwallet-core/src/osmosis.ts b/packages/hdwallet-core/src/osmosis.ts index e239a65fc..9b616101d 100644 --- a/packages/hdwallet-core/src/osmosis.ts +++ b/packages/hdwallet-core/src/osmosis.ts @@ -6,6 +6,7 @@ export interface OsmosisGetAddress { showDisplay?: boolean; } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Osmosis { export interface Msg { type: string; @@ -24,6 +25,7 @@ export namespace Osmosis { gas: string; } + // eslint-disable-next-line @typescript-eslint/no-namespace namespace crypto { export interface PubKey { type: string; @@ -61,10 +63,10 @@ export interface OsmosisSignTx { } export interface OsmosisSignedTx { - serialized: string - body: string - authInfoBytes: string - signatures: string[] + serialized: string; + body: string; + authInfoBytes: string; + signatures: string[]; } export interface OsmosisGetAccountPaths { @@ -98,8 +100,8 @@ export interface OsmosisWallet extends OsmosisWalletInfo, HDWallet { } export function osmosisDescribePath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Atom", isKnown: false, @@ -125,7 +127,7 @@ export function osmosisDescribePath(path: BIP32Path): PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Osmosis Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/ripple.ts b/packages/hdwallet-core/src/ripple.ts index 5747a4a61..41d437f6c 100644 --- a/packages/hdwallet-core/src/ripple.ts +++ b/packages/hdwallet-core/src/ripple.ts @@ -4,7 +4,10 @@ export interface RippleGetAddress { addressNList: BIP32Path; showDisplay?: boolean; } + +// eslint-disable-next-line @typescript-eslint/no-namespace declare namespace Ripple { + // eslint-disable-next-line @typescript-eslint/no-namespace namespace sdk { interface Msg { type: string; @@ -21,6 +24,8 @@ declare namespace Ripple { amount: sdk.Coins; gas: string; } + + // eslint-disable-next-line @typescript-eslint/no-namespace namespace crypto { interface PubKey { type: string; diff --git a/packages/hdwallet-core/src/secret.ts b/packages/hdwallet-core/src/secret.ts index 27eb28604..1d6ab5009 100644 --- a/packages/hdwallet-core/src/secret.ts +++ b/packages/hdwallet-core/src/secret.ts @@ -7,6 +7,7 @@ export interface SecretGetAddress { testnet?: boolean; } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Secret { export interface Msg { type: string; @@ -25,6 +26,7 @@ export namespace Secret { gas: string; } + // eslint-disable-next-line @typescript-eslint/no-namespace namespace crypto { export interface PubKey { type: string; @@ -96,8 +98,8 @@ export interface SecretWallet extends SecretWalletInfo, HDWallet { } export function secretDescribePath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Secret", isKnown: false, @@ -123,7 +125,7 @@ export function secretDescribePath(path: BIP32Path): PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Secret Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/terra.ts b/packages/hdwallet-core/src/terra.ts index 270fdd6dc..8313c07dc 100644 --- a/packages/hdwallet-core/src/terra.ts +++ b/packages/hdwallet-core/src/terra.ts @@ -7,6 +7,7 @@ export interface TerraGetAddress { testnet?: boolean; } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Terra { export interface Msg { type: string; @@ -25,6 +26,7 @@ export namespace Terra { gas: string; } + // eslint-disable-next-line @typescript-eslint/no-namespace namespace crypto { export interface PubKey { type: string; @@ -95,8 +97,8 @@ export interface TerraWallet extends TerraWalletInfo, HDWallet { } export function terraDescribePath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Terra", isKnown: false, @@ -122,7 +124,7 @@ export function terraDescribePath(path: BIP32Path): PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Terra Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/thorchain.ts b/packages/hdwallet-core/src/thorchain.ts index 04d84d4ad..66a4bcd87 100644 --- a/packages/hdwallet-core/src/thorchain.ts +++ b/packages/hdwallet-core/src/thorchain.ts @@ -7,6 +7,7 @@ export interface ThorchainGetAddress { testnet?: boolean; } +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace Thorchain { export interface Msg { type: string; @@ -25,6 +26,7 @@ export namespace Thorchain { gas: string; } + // eslint-disable-next-line @typescript-eslint/no-namespace namespace crypto { export interface PubKey { type: string; @@ -95,8 +97,8 @@ export interface ThorchainWallet extends ThorchainWalletInfo, HDWallet { } export function thorchainDescribePath(path: BIP32Path): PathDescription { - let pathStr = addressNListToBIP32(path); - let unknown: PathDescription = { + const pathStr = addressNListToBIP32(path); + const unknown: PathDescription = { verbose: pathStr, coin: "Rune", isKnown: false, @@ -122,7 +124,7 @@ export function thorchainDescribePath(path: BIP32Path): PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Thorchain Account #${index}`, accountIdx: index, diff --git a/packages/hdwallet-core/src/transport.ts b/packages/hdwallet-core/src/transport.ts index dcda503ac..a50296d91 100644 --- a/packages/hdwallet-core/src/transport.ts +++ b/packages/hdwallet-core/src/transport.ts @@ -20,10 +20,12 @@ export abstract class Transport extends eventemitter2.EventEmitter2 { /** * Optional method to bootstrap connection to device */ + // eslint-disable-next-line @typescript-eslint/no-empty-function public async connect(): Promise {} /** * Optional function that gets called to clean up connection to device */ + // eslint-disable-next-line @typescript-eslint/no-empty-function public async disconnect(): Promise {} } diff --git a/packages/hdwallet-core/src/utils.test.ts b/packages/hdwallet-core/src/utils.test.ts index 1fdf26d6f..773791077 100644 --- a/packages/hdwallet-core/src/utils.test.ts +++ b/packages/hdwallet-core/src/utils.test.ts @@ -1,12 +1,12 @@ import { - isArray, - fromHexString, - toHexString, + bip32Like, bip32ToAddressNList, - slip44ByCoin, + fromHexString, + isArray, satsFromStr, + slip44ByCoin, stripHexPrefix, - bip32Like, + toHexString, } from "./utils"; describe("isArray", () => { diff --git a/packages/hdwallet-core/src/utils.ts b/packages/hdwallet-core/src/utils.ts index 900239ac7..3ff977e4f 100644 --- a/packages/hdwallet-core/src/utils.ts +++ b/packages/hdwallet-core/src/utils.ts @@ -4,7 +4,7 @@ import * as RxOp from "rxjs/operators"; import { BIP32Path, Coin } from "./wallet"; -export type Constructor = new (...args: any[]) => T; +export type Constructor = new (...args: any[]) => T; export const DEFAULT_TIMEOUT = 5000; // 5 seconds export const LONG_TIMEOUT = 5 * 60 * 1000; // 5 minutes @@ -35,7 +35,7 @@ export function arrayify(value: string): Uint8Array { throw new Error("can only convert hex strings"); } - let match = value.match(/^(0x)?[0-9a-fA-F]*$/); + const match = value.match(/^(0x)?[0-9a-fA-F]*$/); if (!match) { throw new Error("invalid hexadecimal string"); @@ -59,6 +59,12 @@ export function arrayify(value: string): Uint8Array { } const HARDENED = 0x80000000; + +export function bip32Like(path: string): boolean { + if (path == "m/") return true; + return /^m(((\/[0-9]+h)+|(\/[0-9]+H)+|(\/[0-9]+')*)((\/[0-9]+)*))$/.test(path); +} + export function bip32ToAddressNList(path: string): number[] { if (!bip32Like(path)) { throw new Error(`Not a bip32 path: '${path}'`); @@ -70,7 +76,7 @@ export function bip32ToAddressNList(path: string): number[] { if (segments.length === 1 && segments[0] === "") return []; const ret = new Array(segments.length); for (let i = 0; i < segments.length; i++) { - const tmp = /(\d+)([hH\']?)/.exec(segments[i]); + const tmp = /(\d+)([hH']?)/.exec(segments[i]); if (tmp === null) { throw new Error("Invalid input"); } @@ -91,12 +97,10 @@ export function addressNListToBIP32(address: number[]): string { return `m/${address.map((num) => (num >= HARDENED ? `${num - HARDENED}'` : num)).join("/")}`; } -export function bip32Like(path: string): boolean { - if (path == "m/") return true; - return /^m(((\/[0-9]+h)+|(\/[0-9]+H)+|(\/[0-9]+')*)((\/[0-9]+)*))$/.test(path); -} - -export function takeFirstOfManyEvents(eventEmitter: eventemitter2.EventEmitter2, events: string[]): Rx.Observable<{}> { +export function takeFirstOfManyEvents( + eventEmitter: eventemitter2.EventEmitter2, + events: string[] +): Rx.Observable { return Rx.merge(...events.map((event) => Rx.fromEvent(eventEmitter, event))).pipe(RxOp.first()); } @@ -109,11 +113,11 @@ export function stripHexPrefixAndLower(value: string): string { } export function base64toHEX(base64: string): string { - var raw = atob(base64); - var HEX = ""; + const raw = atob(base64); + let HEX = ""; for (let i = 0; i < raw.length; i++) { - var _hex = raw.charCodeAt(i).toString(16); + const _hex = raw.charCodeAt(i).toString(16); HEX += _hex.length == 2 ? _hex : "0" + _hex; } @@ -146,14 +150,14 @@ const slip44Table = Object.freeze({ Terra: 330, Kava: 459, } as const); -type Slip44ByCoin = (T extends keyof typeof slip44Table ? typeof slip44Table[T] : number | undefined); +type Slip44ByCoin = T extends keyof typeof slip44Table ? typeof slip44Table[T] : number | undefined; export function slip44ByCoin(coin: T): Slip44ByCoin { return (slip44Table as any)[coin]; } export function satsFromStr(coins: string): number { - let index = coins.indexOf("."); - let exponent = index > 0 ? 8 - (coins.length - index - 1) : 8; + const index = coins.indexOf("."); + const exponent = index > 0 ? 8 - (coins.length - index - 1) : 8; return Number(coins.replace(/\./g, "")) * 10 ** exponent; } @@ -179,12 +183,21 @@ export function mustBeDefined(x: T): NonNullable { // accessed in any other way. Useful as dummy data for required parameters. (Probably a bad idea // in production.) export function untouchable(message: string): any { - const out = new Proxy({}, new Proxy({}, { get(_, p) { - return (_: any, p2: any) => { - if (p === "get" && p2 === "valueOf") return () => out; - throw new Error(`${String(p)}(${String(p2)}): ${message}`); - }; - }})) as any; + const out = new Proxy( + {}, + new Proxy( + {}, + { + get(_, p) { + // eslint-disable-next-line @typescript-eslint/no-shadow + return (_: any, p2: any) => { + if (p === "get" && p2 === "valueOf") return () => out; + throw new Error(`${String(p)}(${String(p2)}): ${message}`); + }; + }, + } + ) + ) as any; return out; } @@ -206,10 +219,9 @@ export function checkBufferConcat(): boolean { export function compatibleBufferConcat(list: Uint8Array[]): Buffer { if (!checkBufferConcat()) return Buffer.concat(list); - return Buffer.concat(list.map(x => Buffer.isBuffer(x) ? x : Buffer.from(x))); + return Buffer.concat(list.map((x) => (Buffer.isBuffer(x) ? x : Buffer.from(x)))); } - /** * Type guard for things that might have (string-keyed) properties. Useful to make * TypeScript happy when you want to check if an object of unknown type has a particular @@ -239,5 +251,5 @@ export function compatibleBufferConcat(list: Uint8Array[]): Buffer { * isIndexable("foo") === false */ export function isIndexable(x: unknown): x is Record { - return x !== null && ["object", "function"].includes(typeof x) + return x !== null && ["object", "function"].includes(typeof x); } diff --git a/packages/hdwallet-core/src/wallet.test.ts b/packages/hdwallet-core/src/wallet.test.ts index 67fec0400..f17c7989f 100644 --- a/packages/hdwallet-core/src/wallet.test.ts +++ b/packages/hdwallet-core/src/wallet.test.ts @@ -1,4 +1,4 @@ -import { infoBTC, infoETH, supportsBTC, supportsETH, supportsDebugLink, HDWallet } from "./wallet"; +import { HDWallet, infoBTC, infoETH, supportsBTC, supportsDebugLink, supportsETH } from "./wallet"; describe("wallet : guards", () => { it.each([infoBTC, infoETH, supportsBTC, supportsETH, supportsDebugLink])( @@ -12,8 +12,10 @@ describe("wallet : guards", () => { it("infoBTC should be truthy", () => expect(infoBTC({ _supportsBTCInfo: true } as unknown as HDWallet)).toBeTruthy()); it("infoETH should be truthy", () => expect(infoETH({ _supportsETHInfo: true } as unknown as HDWallet)).toBeTruthy()); - it("supportsBTC should be truthy", () => expect(supportsBTC({ _supportsBTC: true } as unknown as HDWallet)).toBeTruthy()); - it("supportsETH should be truthy", () => expect(supportsETH({ _supportsETH: true } as unknown as HDWallet)).toBeTruthy()); + it("supportsBTC should be truthy", () => + expect(supportsBTC({ _supportsBTC: true } as unknown as HDWallet)).toBeTruthy()); + it("supportsETH should be truthy", () => + expect(supportsETH({ _supportsETH: true } as unknown as HDWallet)).toBeTruthy()); it("supportsDebugLink should be truthy", () => expect(supportsDebugLink({ _supportsDebugLink: true } as unknown as HDWallet)).toBeTruthy()); }); diff --git a/packages/hdwallet-core/src/wallet.ts b/packages/hdwallet-core/src/wallet.ts index a50d2bfee..3ed2ec9b7 100644 --- a/packages/hdwallet-core/src/wallet.ts +++ b/packages/hdwallet-core/src/wallet.ts @@ -3,12 +3,12 @@ import _ from "lodash"; import { BinanceWallet, BinanceWalletInfo } from "./binance"; import { BTCInputScriptType, BTCWallet, BTCWalletInfo } from "./bitcoin"; import { CosmosWallet, CosmosWalletInfo } from "./cosmos"; -import { OsmosisWallet, OsmosisWalletInfo } from "./osmosis"; import { DebugLinkWallet } from "./debuglink"; import { EosWallet, EosWalletInfo } from "./eos"; import { ETHWallet, ETHWalletInfo } from "./ethereum"; import { FioWallet, FioWalletInfo } from "./fio"; import { KavaWallet, KavaWalletInfo } from "./kava"; +import { OsmosisWallet, OsmosisWalletInfo } from "./osmosis"; import { RippleWallet, RippleWalletInfo } from "./ripple"; import { SecretWallet, SecretWalletInfo } from "./secret"; import { TerraWallet, TerraWalletInfo } from "./terra"; @@ -256,12 +256,12 @@ export interface HDWalletInfo { * Will the device allow for transactions to be signed offline to be * broadcasted separately? */ - supportsOfflineSigning(): boolean + supportsOfflineSigning(): boolean; /** * Can the device broadcast signed transactions internally? */ - supportsBroadcast(): boolean + supportsBroadcast(): boolean; /** * Describes a BIP32 path in plain English. diff --git a/packages/hdwallet-keepkey-chromeusb/src/adapter.ts b/packages/hdwallet-keepkey-chromeusb/src/adapter.ts index 4867afc11..cbbdaa9e2 100644 --- a/packages/hdwallet-keepkey-chromeusb/src/adapter.ts +++ b/packages/hdwallet-keepkey-chromeusb/src/adapter.ts @@ -1,9 +1,9 @@ import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; import { TransportDelegate } from "./transport"; -import { VENDOR_ID, WEBUSB_PRODUCT_ID, HID_PRODUCT_ID, assertChromeUSB, chromeUSB, makePromise } from "./utils"; +import { assertChromeUSB, chromeUSB, HID_PRODUCT_ID, makePromise, VENDOR_ID, WEBUSB_PRODUCT_ID } from "./utils"; -type Device = USBDevice & {serialNumber: string}; +type Device = USBDevice & { serialNumber: string }; export const ChromeUSBAdapterDelegate = { async getTransportDelegate(device: Device) { @@ -25,14 +25,11 @@ export const ChromeUSBAdapterDelegate = { })) as USBDevice[]; return devices.filter((d) => d.serialNumber !== undefined) as Device[]; }, - registerCallbacks( - handleConnect: (device: Device) => void, - handleDisconnect: (device: Device) => void - ) { + registerCallbacks(handleConnect: (device: Device) => void, handleDisconnect: (device: Device) => void) { assertChromeUSB(chromeUSB); chromeUSB.onDeviceAdded.addListener(handleConnect); chromeUSB.onDeviceRemoved.addListener(handleDisconnect); - } + }, }; export const Adapter = keepkey.Adapter.fromDelegate(ChromeUSBAdapterDelegate); diff --git a/packages/hdwallet-keepkey-chromeusb/src/transport.ts b/packages/hdwallet-keepkey-chromeusb/src/transport.ts index 85af3130d..75f7796e1 100644 --- a/packages/hdwallet-keepkey-chromeusb/src/transport.ts +++ b/packages/hdwallet-keepkey-chromeusb/src/transport.ts @@ -1,19 +1,19 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; -import { VENDOR_ID, WEBUSB_PRODUCT_ID, chromeUSB, assertChromeUSB, makePromise } from "./utils"; +import { assertChromeUSB, chromeUSB, makePromise, VENDOR_ID, WEBUSB_PRODUCT_ID } from "./utils"; export class TransportDelegate implements keepkey.TransportDelegate { - usbDevice: USBDevice & {serialNumber: string}; + usbDevice: USBDevice & { serialNumber: string }; private connectionHandle: any; - constructor(usbDevice: USBDevice & {serialNumber: string}) { + constructor(usbDevice: USBDevice & { serialNumber: string }) { if (usbDevice.vendorId !== VENDOR_ID) throw new core.WebUSBCouldNotPair("KeepKey", "bad vendor id"); if (usbDevice.productId !== WEBUSB_PRODUCT_ID) throw new core.FirmwareUpdateRequired("KeepKey", "6.1.0"); this.usbDevice = usbDevice; } - static async create(usbDevice: USBDevice & {serialNumber: string}): Promise { + static async create(usbDevice: USBDevice & { serialNumber: string }): Promise { if (usbDevice.vendorId !== VENDOR_ID) return null; return new TransportDelegate(usbDevice); } @@ -31,7 +31,8 @@ export class TransportDelegate implements keepkey.TransportDelegate { if (await this.isOpened()) throw new Error("cannot connect an already-connected connection"); this.connectionHandle = await makePromise(chromeUSB.openDevice, this.usbDevice); - if (this.connectionHandle.configuration === null) await makePromise(chromeUSB.setConfiguration, this.connectionHandle, 1); + if (this.connectionHandle.configuration === null) + await makePromise(chromeUSB.setConfiguration, this.connectionHandle, 1); await makePromise(chromeUSB.claimInterface, this.connectionHandle, 0); } @@ -41,7 +42,7 @@ export class TransportDelegate implements keepkey.TransportDelegate { // If the device is disconnected, this will fail and throw, which is fine. chromeUSB.closeDevice(this.connectionHandle); } catch (e) { - console.log("Disconnect Error (Ignored):", e); + console.warn("Disconnect Error (Ignored):", e); } } @@ -63,7 +64,7 @@ export class TransportDelegate implements keepkey.TransportDelegate { length: keepkey.SEGMENT_SIZE + 1, timeout: 0, }); - console.log(resultCode, data); + console.debug(resultCode, data); if (resultCode > 0) throw new Error("Error occured reading chunk"); return data; } diff --git a/packages/hdwallet-keepkey-chromeusb/src/utils.ts b/packages/hdwallet-keepkey-chromeusb/src/utils.ts index 86374908c..254c0480b 100644 --- a/packages/hdwallet-keepkey-chromeusb/src/utils.ts +++ b/packages/hdwallet-keepkey-chromeusb/src/utils.ts @@ -2,25 +2,30 @@ export const VENDOR_ID = 0x2b24; export const WEBUSB_PRODUCT_ID = 0x0002; export const HID_PRODUCT_ID = 0x0001; -export function makePromise(func: Function, ...args: any[]): Promise { +export function makePromise(func: (...fnArgs: any) => unknown, ...args: any[]): Promise { return new Promise((resolve) => { func(...args, resolve); }); } declare const chrome: any; -export const chromeUSB = chrome?.["usb"] as unknown +export const chromeUSB = chrome?.["usb"] as unknown; export type ChromeUSB = { - openDevice: any, - onDeviceAdded: { addListener: Function }, - onDeviceRemoved: { addListener: Function }, - getDevices: any, - closeDevice: any, - setConfiguration: any, - claimInterface: any, - interruptTransfer: any, -} + openDevice: any; + // eslint-disable-next-line @typescript-eslint/ban-types + onDeviceAdded: { addListener: Function }; + // eslint-disable-next-line @typescript-eslint/ban-types + onDeviceRemoved: { addListener: Function }; + getDevices: any; + closeDevice: any; + setConfiguration: any; + claimInterface: any; + interruptTransfer: any; +}; export function assertChromeUSB(c: any): asserts c is ChromeUSB { - if (!c) throw new Error("ChromeUSB is not available in this process. This package is intended for Chrome apps and extensions."); + if (!c) + throw new Error( + "ChromeUSB is not available in this process. This package is intended for Chrome apps and extensions." + ); } diff --git a/packages/hdwallet-keepkey-electron/src/client.ts b/packages/hdwallet-keepkey-electron/src/client.ts index 6446fa6df..54d033e9c 100644 --- a/packages/hdwallet-keepkey-electron/src/client.ts +++ b/packages/hdwallet-keepkey-electron/src/client.ts @@ -1,16 +1,16 @@ import * as electron from "electron"; import { + CONNECT, + DISCONNECT, GET_DEVICE, + GET_DEVICE_ID, GET_DEVICES, GET_TRANSPORT_DELEGATE, IS_OPENED, - GET_DEVICE_ID, - CONNECT, + READ_CHUNK, TRY_CONNECT_DEBUG_LINK, - DISCONNECT, WRITE_CHUNK, - READ_CHUNK, } from "./utils"; export const CLIENT_TAG = "apiHDWalletKeepKeyElectronClient"; @@ -48,6 +48,6 @@ export const Client = { }, expose() { electron.contextBridge.exposeInMainWorld(CLIENT_TAG, Client); - } -} + }, +}; export type Client = typeof Client; diff --git a/packages/hdwallet-keepkey-electron/src/proxies.ts b/packages/hdwallet-keepkey-electron/src/proxies.ts index 9f34497d3..afb221588 100644 --- a/packages/hdwallet-keepkey-electron/src/proxies.ts +++ b/packages/hdwallet-keepkey-electron/src/proxies.ts @@ -1,24 +1,11 @@ import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; -import { CLIENT_TAG, Client } from "./client"; +import { Client, CLIENT_TAG } from "./client"; function getClient(): Client { return ((window as any)?.[CLIENT_TAG] as Client) ?? Client; } -export const AdapterDelegateProxy = { - getDevices(): Promise { - return getClient().getDevices(); - }, - getDevice(serialNumber?: string): Promise { - return getClient().getDevice(serialNumber); - }, - async getTransportDelegate(handle: string) { - await getClient().getTransportDelegate(handle); - return new TransportDelegateProxy(handle); - }, -}; - export class TransportDelegateProxy implements keepkey.TransportDelegate { handle: string; constructor(handle: string) { @@ -49,3 +36,16 @@ export class TransportDelegateProxy implements keepkey.TransportDelegate { return getClient().readChunk(this.handle, debugLink); } } + +export const AdapterDelegateProxy = { + getDevices(): Promise { + return getClient().getDevices(); + }, + getDevice(serialNumber?: string): Promise { + return getClient().getDevice(serialNumber); + }, + async getTransportDelegate(handle: string) { + await getClient().getTransportDelegate(handle); + return new TransportDelegateProxy(handle); + }, +}; diff --git a/packages/hdwallet-keepkey-electron/src/server.ts b/packages/hdwallet-keepkey-electron/src/server.ts index fd94f5223..6384b4953 100644 --- a/packages/hdwallet-keepkey-electron/src/server.ts +++ b/packages/hdwallet-keepkey-electron/src/server.ts @@ -43,8 +43,9 @@ export class Server this.getDevice(serialNumber)); - if (this.delegate.getDevices !== undefined) electron.ipcMain.handle(GET_DEVICES, (_) => this.getDevices()); + if (this.delegate.getDevice !== undefined) + electron.ipcMain.handle(GET_DEVICE, (_, serialNumber?: string) => this.getDevice(serialNumber)); + if (this.delegate.getDevices !== undefined) electron.ipcMain.handle(GET_DEVICES, () => this.getDevices()); electron.ipcMain.handle(GET_TRANSPORT_DELEGATE, (_, handle: string) => this.getTransportDelegate(handle)); for (const delegateMethod of [ "isOpened", @@ -56,19 +57,23 @@ export class Server => { - const device = this.devices.get(handle); - if (!device) throw new Error("invalid device handle"); - const delegate = this.delegates.get(device); - if (!delegate) throw new Error("no delegate for device"); - switch (delegateMethod) { - case "tryConnectDebugLink": - if (delegate.tryConnectDebugLink === undefined) return false; - return await delegate.tryConnectDebugLink(); - default: - return await (delegate[delegateMethod] as (...args: any[]) => Promise)(...args); + electron.ipcMain.handle( + `${PREFIX}:${delegateMethod}`, + async (_, handle: string, ...args: any[]): Promise => { + const device = this.devices.get(handle); + if (!device) throw new Error("invalid device handle"); + const delegate = this.delegates.get(device); + if (!delegate) throw new Error("no delegate for device"); + switch (delegateMethod) { + case "tryConnectDebugLink": + if (delegate.tryConnectDebugLink === undefined) return false; + return await delegate.tryConnectDebugLink(); + default: + // eslint-disable-next-line @typescript-eslint/no-shadow + return await (delegate[delegateMethod] as (...args: any[]) => Promise)(...args); + } } - }); + ); } } } diff --git a/packages/hdwallet-keepkey-nodehid/src/adapter.ts b/packages/hdwallet-keepkey-nodehid/src/adapter.ts index 492c543f7..c31fd8ea3 100644 --- a/packages/hdwallet-keepkey-nodehid/src/adapter.ts +++ b/packages/hdwallet-keepkey-nodehid/src/adapter.ts @@ -2,7 +2,7 @@ import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; import * as hid from "node-hid"; import { Device, TransportDelegate } from "./transport"; -import { VENDOR_ID, PRODUCT_ID } from "./utils"; +import { PRODUCT_ID, VENDOR_ID } from "./utils"; export const HIDKeepKeyAdapterDelegate = { async inspectDevice(device: Device) { diff --git a/packages/hdwallet-keepkey-nodehid/src/transport.ts b/packages/hdwallet-keepkey-nodehid/src/transport.ts index 967d22511..13c0e91e7 100644 --- a/packages/hdwallet-keepkey-nodehid/src/transport.ts +++ b/packages/hdwallet-keepkey-nodehid/src/transport.ts @@ -1,7 +1,7 @@ import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; import * as hid from "node-hid"; -import { VENDOR_ID, PRODUCT_ID } from "./utils"; +import { PRODUCT_ID, VENDOR_ID } from "./utils"; export function requestPair(): hid.HID { return new hid.HID(VENDOR_ID, PRODUCT_ID); @@ -36,7 +36,7 @@ export class TransportDelegate implements keepkey.TransportDelegate { // If the device is disconnected, this will fail and throw, which is fine. await this.hidRef.close(); } catch (e) { - console.log("Disconnect Error (Ignored):", e); + console.warn("Disconnect Error (Ignored):", e); } } @@ -46,7 +46,7 @@ export class TransportDelegate implements keepkey.TransportDelegate { } async writeChunk(buf: Uint8Array): Promise { - const numArray = buf.reduce((a, x, i) => (a[i] = x, a), new Array(buf.length)); + const numArray = buf.reduce((a, x, i) => ((a[i] = x), a), new Array(buf.length)); await this.hidRef.write(numArray); } } diff --git a/packages/hdwallet-keepkey-nodewebusb/src/adapter.ts b/packages/hdwallet-keepkey-nodewebusb/src/adapter.ts index 0c1a7fdcd..780a88ec8 100644 --- a/packages/hdwallet-keepkey-nodewebusb/src/adapter.ts +++ b/packages/hdwallet-keepkey-nodewebusb/src/adapter.ts @@ -2,14 +2,12 @@ import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; import * as webusb from "webusb"; import { Device, TransportDelegate } from "./transport"; -import { VENDOR_ID, WEBUSB_PRODUCT_ID, HID_PRODUCT_ID } from "./utils"; +import { HID_PRODUCT_ID, VENDOR_ID, WEBUSB_PRODUCT_ID } from "./utils"; export const NodeWebUSBAdapterDelegate = { async getDevices(): Promise { const devices = (await webusb.usb.getDevices()).filter((d) => d.serialNumber !== undefined) as Device[]; - return devices.filter( - (x) => x.vendorId === VENDOR_ID && [WEBUSB_PRODUCT_ID, HID_PRODUCT_ID].includes(x.productId) - ); + return devices.filter((x) => x.vendorId === VENDOR_ID && [WEBUSB_PRODUCT_ID, HID_PRODUCT_ID].includes(x.productId)); }, async getDevice(serialNumber?: string): Promise { const out = await webusb.usb.requestDevice({ diff --git a/packages/hdwallet-keepkey-nodewebusb/src/transport.ts b/packages/hdwallet-keepkey-nodewebusb/src/transport.ts index 05e26f660..8a5f25494 100644 --- a/packages/hdwallet-keepkey-nodewebusb/src/transport.ts +++ b/packages/hdwallet-keepkey-nodewebusb/src/transport.ts @@ -3,7 +3,7 @@ import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; import { VENDOR_ID, WEBUSB_PRODUCT_ID } from "./utils"; -export type Device = USBDevice & {serialNumber: string}; +export type Device = USBDevice & { serialNumber: string }; export class TransportDelegate implements keepkey.TransportDelegate { usbDevice: Device; @@ -11,12 +11,12 @@ export class TransportDelegate implements keepkey.TransportDelegate { constructor(usbDevice: Device) { if (usbDevice.vendorId !== VENDOR_ID) throw new core.WebUSBCouldNotPair("KeepKey", "bad vendor id"); if (usbDevice.productId !== WEBUSB_PRODUCT_ID) throw new core.FirmwareUpdateRequired("KeepKey", "6.1.0"); - this.usbDevice = usbDevice + this.usbDevice = usbDevice; } async create(usbDevice: Device): Promise { if (usbDevice.vendorId !== VENDOR_ID) return null; - return new TransportDelegate(usbDevice) + return new TransportDelegate(usbDevice); } async isOpened(): Promise { @@ -27,9 +27,9 @@ export class TransportDelegate implements keepkey.TransportDelegate { return this.usbDevice.serialNumber; } - async connect(): Promise { + async connect(): Promise { if (await this.isOpened()) throw new Error("cannot connect an already-connected connection"); - + await this.usbDevice.open(); if (this.usbDevice.configuration === null) await this.usbDevice.selectConfiguration(1); @@ -63,12 +63,15 @@ export class TransportDelegate implements keepkey.TransportDelegate { // If the device is disconnected, this will fail and throw, which is fine. await this.usbDevice.close(); } catch (e) { - console.log("Disconnect Error (Ignored):", e); + console.warn("Disconnect Error (Ignored):", e); } } async writeChunk(buf: Uint8Array, debugLink?: boolean): Promise { - const result = await this.usbDevice.transferOut(debugLink ? 2 : 1, buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)); + const result = await this.usbDevice.transferOut( + debugLink ? 2 : 1, + buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) + ); if (result.status !== "ok" || result.bytesWritten !== buf.length) throw new Error("bad write"); } @@ -81,6 +84,8 @@ export class TransportDelegate implements keepkey.TransportDelegate { throw new Error("bad read"); } - return new Uint8Array(result.data.buffer.slice(result.data.byteOffset, result.data.byteOffset + result.data.byteLength)) + return new Uint8Array( + result.data.buffer.slice(result.data.byteOffset, result.data.byteOffset + result.data.byteLength) + ); } } diff --git a/packages/hdwallet-keepkey-tcp/src/transport.ts b/packages/hdwallet-keepkey-tcp/src/transport.ts index 9cd7cd54d..1852b550a 100644 --- a/packages/hdwallet-keepkey-tcp/src/transport.ts +++ b/packages/hdwallet-keepkey-tcp/src/transport.ts @@ -15,7 +15,7 @@ export class TransportDelegate implements keepkey.TransportDelegate { "content-type": "application/json", "Access-Control-Allow-Origin": config.baseURL, }, - } + }; } async getDeviceID(): Promise { diff --git a/packages/hdwallet-keepkey-webusb/src/adapter.ts b/packages/hdwallet-keepkey-webusb/src/adapter.ts index 427129483..7ba74b3a4 100644 --- a/packages/hdwallet-keepkey-webusb/src/adapter.ts +++ b/packages/hdwallet-keepkey-webusb/src/adapter.ts @@ -2,23 +2,21 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; import { Device, TransportDelegate } from "./transport"; -import { VENDOR_ID, WEBUSB_PRODUCT_ID, HID_PRODUCT_ID } from "./utils"; +import { HID_PRODUCT_ID, VENDOR_ID, WEBUSB_PRODUCT_ID } from "./utils"; // This avoids a prompt ReferenceError if the module is imported outside a browser. -const webUSB = typeof window === "object" && window?.navigator?.usb as unknown; +const webUSB = typeof window === "object" && (window?.navigator?.usb as unknown); type WebUSB = typeof window.navigator.usb; -function assertWebUSB(webUSB: any): asserts webUSB is WebUSB { - if (!webUSB) throw new core.WebUSBNotAvailable(); +function assertWebUSB(x: any): asserts x is WebUSB { + if (!x) throw new core.WebUSBNotAvailable(); } export const AdapterDelegate = { async getDevices(): Promise { assertWebUSB(webUSB); const devices = (await webUSB.getDevices()).filter((d) => d.serialNumber !== undefined) as Device[]; - return devices.filter( - (x) => x.vendorId === VENDOR_ID && [WEBUSB_PRODUCT_ID, HID_PRODUCT_ID].includes(x.productId) - ); + return devices.filter((x) => x.vendorId === VENDOR_ID && [WEBUSB_PRODUCT_ID, HID_PRODUCT_ID].includes(x.productId)); }, async getDevice(serialNumber?: string): Promise { assertWebUSB(webUSB); diff --git a/packages/hdwallet-keepkey-webusb/src/transport.ts b/packages/hdwallet-keepkey-webusb/src/transport.ts index 1f9ae1525..3f1d98f87 100644 --- a/packages/hdwallet-keepkey-webusb/src/transport.ts +++ b/packages/hdwallet-keepkey-webusb/src/transport.ts @@ -1,9 +1,9 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as keepkey from "@shapeshiftoss/hdwallet-keepkey"; -import { VENDOR_ID, WEBUSB_PRODUCT_ID } from "./utils" +import { VENDOR_ID, WEBUSB_PRODUCT_ID } from "./utils"; -export type Device = USBDevice & {serialNumber: string}; +export type Device = USBDevice & { serialNumber: string }; export class TransportDelegate implements keepkey.TransportDelegate { usbDevice: Device; @@ -61,12 +61,15 @@ export class TransportDelegate implements keepkey.TransportDelegate { // If the device is disconnected, this will fail and throw, which is fine. await this.usbDevice.close(); } catch (e) { - console.log("Error closing connection with usbDevice"); + console.warn("Error closing connection with usbDevice"); } } async writeChunk(buf: Uint8Array, debugLink: boolean): Promise { - await this.usbDevice.transferOut(debugLink ? 2 : 1, buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)); + await this.usbDevice.transferOut( + debugLink ? 2 : 1, + buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) + ); } async readChunk(debugLink: boolean): Promise { diff --git a/packages/hdwallet-keepkey/package.json b/packages/hdwallet-keepkey/package.json index 776722e8f..17defd4db 100644 --- a/packages/hdwallet-keepkey/package.json +++ b/packages/hdwallet-keepkey/package.json @@ -31,6 +31,7 @@ "tiny-secp256k1": "^1.1.6" }, "devDependencies": { + "@types/create-hash": "^1.2.2", "@types/crypto-js": "^4.0.0", "@types/google-protobuf": "^3.15.1", "@types/icepick": "^2.3.0", diff --git a/packages/hdwallet-keepkey/src/adapter.ts b/packages/hdwallet-keepkey/src/adapter.ts index 5b136c281..c504a9e4f 100644 --- a/packages/hdwallet-keepkey/src/adapter.ts +++ b/packages/hdwallet-keepkey/src/adapter.ts @@ -18,10 +18,7 @@ export interface AdapterDelegate { getDevice?(serialNumber?: string): Promise; getDevices?(): Promise>; getTransportDelegate(device: DeviceType): Promise; - registerCallbacks?( - handleConnect: (device: DeviceType) => void, - handleDisconnect: (device: DeviceType) => void - ): void; + registerCallbacks?(handleConnect: (device: DeviceType) => void, handleDisconnect: (device: DeviceType) => void): void; } export type DeviceType> = T extends AdapterDelegate ? R : never; @@ -44,7 +41,7 @@ export class Adapter> { delegate: DelegateType ): AdapterConstructor { const fn = (keyring: core.Keyring) => new Adapter(keyring, delegate); - const out = fn as unknown as AdapterConstructor + const out = fn as unknown as AdapterConstructor; out.useKeyring = fn; return out; } @@ -55,7 +52,7 @@ export class Adapter> { ): Promise { const props = (await Promise.resolve(delegate.inspectDevice?.(device))) ?? - (["object", "function"].includes(typeof device) ? device as Partial : {}); + (["object", "function"].includes(typeof device) ? (device as Partial) : {}); if (!props.serialNumber && typeof device === "string") props.serialNumber = device; return { get productName() { @@ -88,7 +85,9 @@ export class Adapter> { const { productName, serialNumber } = await this.inspectDevice(device); try { await this.keyring.remove(serialNumber); - } catch {} + } catch { + // swallow keyring removal errors + } await this.keyring.emit([productName, serialNumber, core.Events.DISCONNECT], serialNumber); } @@ -122,9 +121,11 @@ export class Adapter> { if (!serialNumber) throw new Error("no default device specified"); const devices = await this.getDevices(); return ( - (await Promise.all( - devices.map(async (x) => ((await this.inspectDevice(x)).serialNumber === serialNumber ? x : null)) - )).filter((x) => x !== null) as DeviceType[] + ( + await Promise.all( + devices.map(async (x) => ((await this.inspectDevice(x)).serialNumber === serialNumber ? x : null)) + ) + ).filter((x) => x !== null) as DeviceType[] )[0]; } @@ -134,7 +135,9 @@ export class Adapter> { let defaultDevice: DeviceType | undefined = undefined; try { defaultDevice = await this.getDevice(); - } catch {} + } catch { + // swallow default device fetch errors + } return defaultDevice ? [defaultDevice] : []; } diff --git a/packages/hdwallet-keepkey/src/binance.ts b/packages/hdwallet-keepkey/src/binance.ts index 67c266edd..89f2880d7 100644 --- a/packages/hdwallet-keepkey/src/binance.ts +++ b/packages/hdwallet-keepkey/src/binance.ts @@ -1,5 +1,5 @@ -import * as BinanceMessages from "@keepkey/device-protocol/lib/messages-binance_pb"; import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; +import * as BinanceMessages from "@keepkey/device-protocol/lib/messages-binance_pb"; import * as core from "@shapeshiftoss/hdwallet-core"; import BigNumber from "bignumber.js"; import CryptoJS from "crypto-js"; @@ -15,10 +15,7 @@ export function binanceGetAccountPaths(msg: core.BinanceGetAccountPaths): Array< ]; } -export async function binanceSignTx( - transport: Transport, - msg: core.BinanceSignTx -): Promise { +export async function binanceSignTx(transport: Transport, msg: core.BinanceSignTx): Promise { return transport.lockDuring(async () => { if (msg.testnet) throw new Error("testnet not supported"); @@ -43,37 +40,33 @@ export async function binanceSignTx( //verify not a batch tx if (msg.tx.msgs.length > 1) throw new Error("Binance batch sending not supported!"); - let message = msg.tx.msgs[0]; + const message = msg.tx.msgs[0]; //tell device not a batch tx signTx.setMsgCount(1); //tell device im about to send a tx to sign - let resp = await transport.call( - Messages.MessageType.MESSAGETYPE_BINANCESIGNTX, - signTx, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true - } - ); + let resp = await transport.call(Messages.MessageType.MESSAGETYPE_BINANCESIGNTX, signTx, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); const outputAmount = new BigNumber(message.outputs[0].coins[0].amount); const inputAmount = new BigNumber(message.inputs[0].coins[0].amount); if (!outputAmount.isInteger()) throw new Error("Output amount must be an integer"); if (!inputAmount.isInteger()) throw new Error("Input amount must be an integer"); - let coinOut = new BinanceMessages.BinanceTransferMsg.BinanceCoin(); + const coinOut = new BinanceMessages.BinanceTransferMsg.BinanceCoin(); coinOut.setAmount(outputAmount.toString()); coinOut.setDenom(message.outputs[0].coins[0].denom); - let outputs = new BinanceMessages.BinanceTransferMsg.BinanceInputOutput(); + const outputs = new BinanceMessages.BinanceTransferMsg.BinanceInputOutput(); outputs.setAddress(message.outputs[0].address); outputs.setCoinsList([coinOut]); - let coinIn = new BinanceMessages.BinanceTransferMsg.BinanceCoin(); + const coinIn = new BinanceMessages.BinanceTransferMsg.BinanceCoin(); coinIn.setAmount(inputAmount.toString()); coinIn.setDenom(message.inputs[0].coins[0].denom); - let inputs = new BinanceMessages.BinanceTransferMsg.BinanceInputOutput(); + const inputs = new BinanceMessages.BinanceTransferMsg.BinanceInputOutput(); inputs.setAddress(message.inputs[0].address); inputs.setCoinsList([coinIn]); @@ -82,20 +75,16 @@ export async function binanceSignTx( send.addOutputs(outputs); //sent tx to device - resp = await transport.call( - Messages.MessageType.MESSAGETYPE_BINANCETRANSFERMSG, - send, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true - } - ); + resp = await transport.call(Messages.MessageType.MESSAGETYPE_BINANCETRANSFERMSG, send, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_BINANCESIGNEDTX) { throw new Error(`binance: unexpected response ${resp.message_type}`); } - let signedTx = new BinanceMessages.BinanceSignedTx(); + const signedTx = new BinanceMessages.BinanceSignedTx(); signedTx.setSignature(resp.message.signature); signedTx.setPublicKey(resp.message.publicKey); diff --git a/packages/hdwallet-keepkey/src/bitcoin.ts b/packages/hdwallet-keepkey/src/bitcoin.ts index 968dc252e..c97f119f9 100644 --- a/packages/hdwallet-keepkey/src/bitcoin.ts +++ b/packages/hdwallet-keepkey/src/bitcoin.ts @@ -1,14 +1,13 @@ import * as Exchange from "@keepkey/device-protocol/lib/exchange_pb"; import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; import * as Types from "@keepkey/device-protocol/lib/types_pb"; -import * as core from "@shapeshiftoss/hdwallet-core"; import * as bitcoinjs from "@shapeshiftoss/bitcoinjs-lib"; +import * as core from "@shapeshiftoss/hdwallet-core"; +import { thaw } from "icepick"; import { Transport } from "./transport"; import { toUTF8Array, translateInputScriptType, translateOutputScriptType } from "./utils"; -import { thaw } from "icepick"; - // FIXME: load this from the device's coin table, or from some static features // table... instead of, you know, adding another God-forsaken coin table. // :facepalm: @@ -78,7 +77,7 @@ function prepareSignTx( const signedExchange = Exchange.SignedExchangeResponse.deserializeBinary(core.arrayify(signedHex)); // decode the deposit amount from a little-endian Uint8Array into an unsigned uint64 - let depAmt = core.mustBeDefined(signedExchange.getResponsev2()).getDepositAmount_asU8(); + const depAmt = core.mustBeDefined(signedExchange.getResponsev2()).getDepositAmount_asU8(); let val = 0; for (let jj = depAmt.length - 1; jj >= 0; jj--) { val += depAmt[jj] * Math.pow(2, 8 * (depAmt.length - jj - 1)); @@ -150,7 +149,7 @@ function prepareSignTx( }, sequence: input.sequence, })), - vout: tx.outs.map((output, i) => ({ + vout: tx.outs.map((output) => ({ value: String(output.value), scriptPubKey: { hex: output.script.toString("hex"), @@ -189,7 +188,7 @@ function prepareSignTx( }); if (coin === "Dash") { - let dip2_type: number = prevTx.type || 0; + const dip2_type: number = prevTx.type || 0; // DIP2 Special Tx with payload if (prevTx.version === 3 && dip2_type !== 0) { if (!prevTx.extraPayload) throw new Error("Payload missing in DIP2 transaction"); @@ -275,13 +274,9 @@ export async function btcGetAddress( addr.setShowDisplay(msg.showDisplay || false); addr.setScriptType(translateInputScriptType(msg.scriptType || core.BTCInputScriptType.SpendAddress)); - const response = await transport.call( - Messages.MessageType.MESSAGETYPE_GETADDRESS, - addr, - { - msgTimeout: core.LONG_TIMEOUT, - } - ); + const response = await transport.call(Messages.MessageType.MESSAGETYPE_GETADDRESS, addr, { + msgTimeout: core.LONG_TIMEOUT, + }); if (response.message_type === core.Events.CANCEL) throw response; @@ -331,39 +326,41 @@ export async function btcSignTx( let responseType: number | undefined; let response: any; - const { message_enum, proto } = await transport.call( - Messages.MessageType.MESSAGETYPE_SIGNTX, - tx, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); // 5 Minute timeout + const { message_enum, proto } = await transport.call(Messages.MessageType.MESSAGETYPE_SIGNTX, tx, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); // 5 Minute timeout responseType = message_enum; response = proto; // Prepare structure for signatures const signatures: (string | null)[] = new Array(msg.inputs.length).fill(null); - let serializedTx: string = ""; + let serializedTx = ""; try { // Begin callback loop + // eslint-disable-next-line no-constant-condition while (true) { if (responseType !== Messages.MessageType.MESSAGETYPE_TXREQUEST) { throw new Error(`Unexpected message type: ${responseType}`); } - let txRequest = response as Messages.TxRequest; + const txRequest = response as Messages.TxRequest; // If there's some part of signed transaction, add it + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (txRequest.hasSerialized() && txRequest.getSerialized()!.hasSerializedTx()) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion serializedTx += core.toHexString(txRequest.getSerialized()!.getSerializedTx_asU8()); } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (txRequest.hasSerialized() && txRequest.getSerialized()!.hasSignatureIndex()) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const sigIdx = txRequest.getSerialized()!.getSignatureIndex()!; if (signatures[sigIdx] !== null) { throw new Error(`Signature for index ${sigIdx} already filled`); } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion signatures[sigIdx] = core.toHexString(txRequest.getSerialized()!.getSignature_asU8()); } @@ -373,13 +370,15 @@ export async function btcSignTx( } let currentTx: Types.TransactionType; - let msg: Types.TransactionType; + let currentMsg: Types.TransactionType; let txAck: Messages.TxAck; // Device asked for one more information, let's process it. if (!txRequest.hasDetails()) throw new Error("expected details"); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const reqDetails = txRequest.getDetails()!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (!reqDetails!.hasTxHash()) { currentTx = txmap["unsigned"]; } else { @@ -387,30 +386,29 @@ export async function btcSignTx( } if (txRequest.getRequestType() === Types.RequestType.TXMETA) { - msg = new Types.TransactionType(); - if (currentTx.hasVersion()) msg.setVersion(currentTx.getVersion()!); - if (currentTx.hasLockTime()) msg.setLockTime(currentTx.getLockTime()!); - if (currentTx.hasInputsCnt()) msg.setInputsCnt(currentTx.getInputsCnt()!); + currentMsg = new Types.TransactionType(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (currentTx.hasVersion()) currentMsg.setVersion(currentTx.getVersion()!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (currentTx.hasLockTime()) currentMsg.setLockTime(currentTx.getLockTime()!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (currentTx.hasInputsCnt()) currentMsg.setInputsCnt(currentTx.getInputsCnt()!); if (reqDetails.hasTxHash()) { - msg.setOutputsCnt(currentTx.getBinOutputsList().length); + currentMsg.setOutputsCnt(currentTx.getBinOutputsList().length); } else { - msg.setOutputsCnt(currentTx.getOutputsList().length); + currentMsg.setOutputsCnt(currentTx.getOutputsList().length); } if (currentTx.hasExtraData()) { - msg.setExtraDataLen(currentTx.getExtraData_asU8().length); + currentMsg.setExtraDataLen(currentTx.getExtraData_asU8().length); } else { - msg.setExtraDataLen(0); + currentMsg.setExtraDataLen(0); } txAck = new Messages.TxAck(); - txAck.setTx(msg); - const message = await transport.call( - Messages.MessageType.MESSAGETYPE_TXACK, - txAck, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); // 5 Minute timeout + txAck.setTx(currentMsg); + const message = await transport.call(Messages.MessageType.MESSAGETYPE_TXACK, txAck, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); // 5 Minute timeout responseType = message.message_enum; response = message.proto; continue; @@ -418,19 +416,16 @@ export async function btcSignTx( if (txRequest.getRequestType() === Types.RequestType.TXINPUT) { if (!reqDetails.hasRequestIndex()) throw new Error("expected request index"); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const reqIndex = reqDetails.getRequestIndex()!; - msg = new Types.TransactionType(); - msg.setInputsList([currentTx.getInputsList()[reqIndex]]); + currentMsg = new Types.TransactionType(); + currentMsg.setInputsList([currentTx.getInputsList()[reqIndex]]); txAck = new Messages.TxAck(); - txAck.setTx(msg); - const message = await transport.call( - Messages.MessageType.MESSAGETYPE_TXACK, - txAck, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); // 5 Minute timeout + txAck.setTx(currentMsg); + const message = await transport.call(Messages.MessageType.MESSAGETYPE_TXACK, txAck, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); // 5 Minute timeout responseType = message.message_enum; response = message.proto; continue; @@ -438,24 +433,21 @@ export async function btcSignTx( if (txRequest.getRequestType() === Types.RequestType.TXOUTPUT) { if (!reqDetails.hasRequestIndex()) throw new Error("expected request index"); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const reqIndex = reqDetails.getRequestIndex()!; - msg = new Types.TransactionType(); + currentMsg = new Types.TransactionType(); if (reqDetails.hasTxHash()) { - msg.setBinOutputsList([currentTx.getBinOutputsList()[reqIndex]]); + currentMsg.setBinOutputsList([currentTx.getBinOutputsList()[reqIndex]]); } else { - msg.setOutputsList([currentTx.getOutputsList()[reqIndex]]); - msg.setOutputsCnt(1); + currentMsg.setOutputsList([currentTx.getOutputsList()[reqIndex]]); + currentMsg.setOutputsCnt(1); } txAck = new Messages.TxAck(); - txAck.setTx(msg); - const message = await transport.call( - Messages.MessageType.MESSAGETYPE_TXACK, - txAck, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); // 5 Minute timeout + txAck.setTx(currentMsg); + const message = await transport.call(Messages.MessageType.MESSAGETYPE_TXACK, txAck, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); // 5 Minute timeout responseType = message.message_enum; response = message.proto; continue; @@ -464,20 +456,18 @@ export async function btcSignTx( if (txRequest.getRequestType() === Types.RequestType.TXEXTRADATA) { if (!reqDetails.hasExtraDataOffset() || !reqDetails.hasExtraDataLen()) throw new Error("missing extra data offset and length"); - let offset = reqDetails.getExtraDataOffset()!; - let length = reqDetails.getExtraDataLen()!; - msg = new Types.TransactionType(); - msg.setExtraData(currentTx.getExtraData_asU8().slice(offset, offset + length)); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const offset = reqDetails.getExtraDataOffset()!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const length = reqDetails.getExtraDataLen()!; + currentMsg = new Types.TransactionType(); + currentMsg.setExtraData(currentTx.getExtraData_asU8().slice(offset, offset + length)); txAck = new Messages.TxAck(); - txAck.setTx(msg); - const message = await transport.call( - Messages.MessageType.MESSAGETYPE_TXACK, - txAck, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); // 5 Minute timeout + txAck.setTx(currentMsg); + const message = await transport.call(Messages.MessageType.MESSAGETYPE_TXACK, txAck, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); // 5 Minute timeout responseType = message.message_enum; response = message.proto; continue; @@ -518,13 +508,9 @@ export async function btcSignMessage( sign.setMessage(toUTF8Array(msg.message)); sign.setCoinName(msg.coin || "Bitcoin"); sign.setScriptType(translateInputScriptType(msg.scriptType ?? core.BTCInputScriptType.SpendAddress)); - const event = await transport.call( - Messages.MessageType.MESSAGETYPE_SIGNMESSAGE, - sign, - { - msgTimeout: core.LONG_TIMEOUT, - } - ); + const event = await transport.call(Messages.MessageType.MESSAGETYPE_SIGNMESSAGE, sign, { + msgTimeout: core.LONG_TIMEOUT, + }); const messageSignature = event.proto as Messages.MessageSignature; const address = messageSignature.getAddress(); if (!address) throw new Error("btcSignMessage failed"); diff --git a/packages/hdwallet-keepkey/src/bnbencoding.ts b/packages/hdwallet-keepkey/src/bnbencoding.ts index c03d39cef..728dec180 100644 --- a/packages/hdwallet-keepkey/src/bnbencoding.ts +++ b/packages/hdwallet-keepkey/src/bnbencoding.ts @@ -3,44 +3,6 @@ import * as bnbSdk from "bnb-javascript-sdk-nobroadcast"; import CryptoJS from "crypto-js"; import TinySecP256K1 from "tiny-secp256k1"; -export function encodeBnbTx(unsignedTx: core.BinanceTx, publicKey: Buffer, signature: Buffer) { - const { account_number, chain_id, sequence, source } = unsignedTx; - const msg = unsignedTx.msgs[0]; - - const amountToInt = (x: any) => Number(x); - const msgNormalizer = (x: any) => ({ - address: bnbSdk.crypto.decodeAddress(x.address), - coins: x.coins.map((y: any) => ({ - // In particular, these keys are backwards because we can't have nice things. - denom: y.denom, - amount: amountToInt(y.amount), - })), - }); - const baseMsg = { - inputs: msg.inputs.map(msgNormalizer), - outputs: msg.outputs.map(msgNormalizer), - aminoPrefix: "2A2C87FA", - }; - - const tx = new bnbSdk.Transaction( - Object.assign({}, unsignedTx, { - chainId: chain_id, - accountNumber: Number(account_number), - source: Number(source ?? 0), - sequence: Number(sequence), - // A bug in the binance SDK makes this field required, even though it shouldn't be. - baseMsg: { getMsg: () => baseMsg, getBaseMsg: () => baseMsg, getSignMsg: () => baseMsg }, - }) - ); - - const ecPubKey = bnbSdk.crypto.getPublicKey(Buffer.from(publicKey).toString("hex")); - tx.addSignature(ecPubKey, signature); - - const serializedTx = Buffer.from(tx.serialize(), "hex"); - if (!validateBnbTx(serializedTx, chain_id)) throw new Error("serialized tx did not validate"); - return serializedTx; -} - export function decodeBnbTx(txBytes: Buffer, chainId: string) { const txDecoded = bnbSdk.amino.decoder.unMarshalBinaryLengthPrefixed(txBytes, { aminoPrefix: "f0625dee", @@ -110,3 +72,41 @@ export function validateBnbTx(txBytes: Buffer, chainId: string) { const { signBytesHash, pubKey, signature } = decodeBnbTx(txBytes, chainId); return TinySecP256K1.verify(Buffer.from(signBytesHash, "hex"), pubKey, signature); } + +export function encodeBnbTx(unsignedTx: core.BinanceTx, publicKey: Buffer, signature: Buffer) { + const { account_number, chain_id, sequence, source } = unsignedTx; + const msg = unsignedTx.msgs[0]; + + const amountToInt = (x: any) => Number(x); + const msgNormalizer = (x: any) => ({ + address: bnbSdk.crypto.decodeAddress(x.address), + coins: x.coins.map((y: any) => ({ + // In particular, these keys are backwards because we can't have nice things. + denom: y.denom, + amount: amountToInt(y.amount), + })), + }); + const baseMsg = { + inputs: msg.inputs.map(msgNormalizer), + outputs: msg.outputs.map(msgNormalizer), + aminoPrefix: "2A2C87FA", + }; + + const tx = new bnbSdk.Transaction( + Object.assign({}, unsignedTx, { + chainId: chain_id, + accountNumber: Number(account_number), + source: Number(source ?? 0), + sequence: Number(sequence), + // A bug in the binance SDK makes this field required, even though it shouldn't be. + baseMsg: { getMsg: () => baseMsg, getBaseMsg: () => baseMsg, getSignMsg: () => baseMsg }, + }) + ); + + const ecPubKey = bnbSdk.crypto.getPublicKey(Buffer.from(publicKey).toString("hex")); + tx.addSignature(ecPubKey, signature); + + const serializedTx = Buffer.from(tx.serialize(), "hex"); + if (!validateBnbTx(serializedTx, chain_id)) throw new Error("serialized tx did not validate"); + return serializedTx; +} diff --git a/packages/hdwallet-keepkey/src/cosmos.ts b/packages/hdwallet-keepkey/src/cosmos.ts index 72fe239f5..beec0cc62 100644 --- a/packages/hdwallet-keepkey/src/cosmos.ts +++ b/packages/hdwallet-keepkey/src/cosmos.ts @@ -1,5 +1,5 @@ -import * as CosmosMessages from "@keepkey/device-protocol/lib/messages-cosmos_pb"; import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; +import * as CosmosMessages from "@keepkey/device-protocol/lib/messages-cosmos_pb"; import * as core from "@shapeshiftoss/hdwallet-core"; import _ from "lodash"; @@ -25,16 +25,12 @@ export async function cosmosSignTx(transport: Transport, msg: core.CosmosSignTx) if (msg.tx.memo !== undefined) signTx.setMemo(msg.tx.memo); signTx.setMsgCount(1); - let resp = await transport.call( - Messages.MessageType.MESSAGETYPE_COSMOSSIGNTX, - signTx, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true - } - ); + let resp = await transport.call(Messages.MessageType.MESSAGETYPE_COSMOSSIGNTX, signTx, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); - for (let m of msg.tx.msg) { + for (const m of msg.tx.msg) { if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_COSMOSMSGREQUEST) { throw new Error(`cosmos: unexpected response ${resp.message_type}`); } @@ -64,7 +60,7 @@ export async function cosmosSignTx(transport: Transport, msg: core.CosmosSignTx) resp = await transport.call(Messages.MessageType.MESSAGETYPE_COSMOSMSGACK, ack, { msgTimeout: core.LONG_TIMEOUT, - omitLock: true + omitLock: true, }); } @@ -90,7 +86,10 @@ export async function cosmosSignTx(transport: Transport, msg: core.CosmosSignTx) }); } -export async function cosmosGetAddress(transport: Transport, msg: CosmosMessages.CosmosGetAddress.AsObject): Promise { +export async function cosmosGetAddress( + transport: Transport, + msg: CosmosMessages.CosmosGetAddress.AsObject +): Promise { const getAddr = new CosmosMessages.CosmosGetAddress(); getAddr.setAddressNList(msg.addressNList); getAddr.setShowDisplay(msg.showDisplay !== false); diff --git a/packages/hdwallet-keepkey/src/eos.ts b/packages/hdwallet-keepkey/src/eos.ts index 6766d7aba..595efcd15 100644 --- a/packages/hdwallet-keepkey/src/eos.ts +++ b/packages/hdwallet-keepkey/src/eos.ts @@ -1,41 +1,41 @@ -import * as EosMessages from "@keepkey/device-protocol/lib/messages-eos_pb"; import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; +import * as EosMessages from "@keepkey/device-protocol/lib/messages-eos_pb"; import * as core from "@shapeshiftoss/hdwallet-core"; +import * as bs58 from "bs58"; +import createHash from "create-hash"; +import Long from "long"; import { Transport } from "./transport"; -const createHash = require("create-hash"); - +// eslint-disable-next-line @typescript-eslint/no-unused-vars function eosSigFormatter(r: Uint8Array, s: Uint8Array, v: number): string { - const base58 = require("bs58"); + const recoverId = 0x1f; - var recoverId = 0x1f; + let signature = "SIG_K1_"; - var signature: string = "SIG_K1_"; - - console.log("formatter logs"); - var keyBuffer = Buffer.alloc(65); - var rbuf = Buffer.from(r); - var sbuf = Buffer.from(s); + console.debug("formatter logs"); + const keyBuffer = Buffer.alloc(65); + const rbuf = Buffer.from(r); + const sbuf = Buffer.from(s); keyBuffer.writeUInt8(recoverId, 0); rbuf.copy(keyBuffer, 1); sbuf.copy(keyBuffer, 33); - console.log(keyBuffer); + console.debug(keyBuffer); const check = [keyBuffer]; - var keyType = "K1"; // we only sign using K1 curve + const keyType = "K1"; // we only sign using K1 curve check.push(Buffer.from(keyType)); - console.log(check); + console.debug(check); - console.log("hash"); - console.log(createHash("ripemd160").update(core.compatibleBufferConcat(check)).digest()); + console.debug("hash"); + console.debug(createHash("ripemd160").update(core.compatibleBufferConcat(check)).digest()); const chksum = createHash("ripemd160").update(core.compatibleBufferConcat(check)).digest().slice(0, 4); - console.log(chksum); - signature = signature.concat(base58.encode(core.compatibleBufferConcat([keyBuffer, chksum]))); + console.debug(chksum); + signature = signature.concat(bs58.encode(core.compatibleBufferConcat([keyBuffer, chksum]))); - console.log(signature); + console.debug(signature); return signature; } @@ -51,7 +51,6 @@ function charToSymbol(c: string): number { } function nameToNumber(name: string): string { - var Long = require("long"); let value = new Long(0, 0, true); let c = new Long(0, 0, true); @@ -72,9 +71,18 @@ function nameToNumber(name: string): string { return value.toString(); } +function symbolFromString(p: number, name: string): number { + let result = 0; + for (let i = 0; i < name.length; i++) { + result |= name.charCodeAt(i) << (8 * (i + 1)); + } + result |= p; + return result; +} + function assetToNumber(asset: string): [number, number] { - let assetSplit = asset.split(" "); // amount, symbol - let dot_pos = assetSplit[0].indexOf("."); + const assetSplit = asset.split(" "); // amount, symbol + const dot_pos = assetSplit[0].indexOf("."); let fract_part = 0; let int_part = 0; let precision_digit = 0; @@ -86,7 +94,7 @@ function assetToNumber(asset: string): [number, number] { precision_digit = 0; } - let sym = symbolFromString(precision_digit, assetSplit[1]); + const sym = symbolFromString(precision_digit, assetSplit[1]); //parse amount if (dot_pos != -1) { int_part = parseInt(assetSplit[0].slice(0, dot_pos)); @@ -103,15 +111,6 @@ function assetToNumber(asset: string): [number, number] { return [amount, sym]; } -function symbolFromString(p: number, name: string): number { - let result = 0; - for (var i = 0; i < name.length; i++) { - result |= name.charCodeAt(i) << (8 * (i + 1)); - } - result |= p; - return result; -} - export function eosGetAccountPaths(msg: core.EosGetAccountPaths): Array { return [ { @@ -162,40 +161,40 @@ export async function eosSignTx(transport: Transport, msg: core.EosToSignTx): Pr signTx.setHeader(txHeader); signTx.setNumActions(msg.tx.actions.length); - console.log("tx header"); - console.log(txHeader); + console.debug("tx header"); + console.debug(txHeader); resp = await transport.call(Messages.MessageType.MESSAGETYPE_EOSSIGNTX, signTx, { msgTimeout: core.LONG_TIMEOUT, - omitLock: true + omitLock: true, }); if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_EOSTXACTIONREQUEST) { throw new Error(`eos: unexpected response ${resp.message_type}`); } // parse the common block of the action - let actCommon = new EosMessages.EosActionCommon(); + const actCommon = new EosMessages.EosActionCommon(); actCommon.setAccount(nameToNumber(msg.tx.actions[0].account).toString()); actCommon.setName(nameToNumber(msg.tx.actions[0].name).toString()); // interate through authorizations and add them for (let n = 0; n < msg.tx.actions[0].authorization.length; n++) { - let actPerm = new EosMessages.EosPermissionLevel(); + const actPerm = new EosMessages.EosPermissionLevel(); actPerm.setActor(nameToNumber(msg.tx.actions[0].authorization[n].actor).toString()); actPerm.setPermission(nameToNumber(msg.tx.actions[0].authorization[n].permission).toString()); actCommon.addAuthorization(actPerm); } - let actAck = new EosMessages.EosTxActionAck(); + const actAck = new EosMessages.EosTxActionAck(); actAck.setCommon(actCommon); // parse the various action types here. switch (msg.tx.actions[0].name) { case "transfer": { // build the transfer action - let actType = new EosMessages.EosActionTransfer(); + const actType = new EosMessages.EosActionTransfer(); actType.setSender(nameToNumber(msg.tx.actions[0].data.from).toString()); actType.setReceiver(nameToNumber(msg.tx.actions[0].data.to).toString()); - let actAsset = new EosMessages.EosAsset(); - let assetParse = assetToNumber(msg.tx.actions[0].data.quantity); + const actAsset = new EosMessages.EosAsset(); + const assetParse = assetToNumber(msg.tx.actions[0].data.quantity); actAsset.setAmount(assetParse[0].toString()); actAsset.setSymbol(assetParse[1].toString()); @@ -204,12 +203,10 @@ export async function eosSignTx(transport: Transport, msg: core.EosToSignTx): Pr actAck.setTransfer(actType); break; } - default: { - } } - console.log("action data"); - console.log(actAck); + console.debug("action data"); + console.debug(actAck); resp = await transport.call(Messages.MessageType.MESSAGETYPE_EOSTXACTIONACK, actAck, { msgTimeout: core.LONG_TIMEOUT, @@ -229,7 +226,7 @@ export async function eosSignTx(transport: Transport, msg: core.EosToSignTx): Pr const signatureS = signedTx.getSignatureS_asU8(); const signatureV = signedTx.getSignatureV(); if (signatureV === undefined) throw new Error("missing signatureV"); - var sig = { + const sig = { signatureV, signatureR, signatureS, diff --git a/packages/hdwallet-keepkey/src/ethereum.ts b/packages/hdwallet-keepkey/src/ethereum.ts index fbe89b2f5..3045d2c8e 100644 --- a/packages/hdwallet-keepkey/src/ethereum.ts +++ b/packages/hdwallet-keepkey/src/ethereum.ts @@ -1,15 +1,16 @@ +import Common from "@ethereumjs/common"; +import { FeeMarketEIP1559Transaction, Transaction } from "@ethereumjs/tx"; import * as Exchange from "@keepkey/device-protocol/lib/exchange_pb"; import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; import * as Types from "@keepkey/device-protocol/lib/types_pb"; import * as core from "@shapeshiftoss/hdwallet-core"; -import Common from "@ethereumjs/common"; -import { FeeMarketEIP1559Transaction, Transaction } from "@ethereumjs/tx"; import * as eip55 from "eip55"; -import { toUTF8Array, translateInputScriptType } from "./utils"; import { Transport } from "./transport"; +import { toUTF8Array, translateInputScriptType } from "./utils"; -export async function ethSupportsNetwork(chain_id: number): Promise { +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export async function ethSupportsNetwork(chainId: number): Promise { return true; } @@ -104,14 +105,10 @@ export async function ethSignTx(transport: Transport, msg: core.ETHSignTx): Prom } let response: Messages.EthereumTxRequest; - let nextResponse = await transport.call( - Messages.MessageType.MESSAGETYPE_ETHEREUMSIGNTX, - est, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); + let nextResponse = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMSIGNTX, est, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); response = nextResponse.proto as Messages.EthereumTxRequest; try { const esa: Messages.EthereumTxAck = new Messages.EthereumTxAck(); @@ -122,14 +119,10 @@ export async function ethSignTx(transport: Transport, msg: core.ETHSignTx): Prom dataRemaining = dataRemaining.slice(dataLength, dataRemaining.length); esa.setDataChunk(dataChunk); - nextResponse = await transport.call( - Messages.MessageType.MESSAGETYPE_ETHEREUMTXACK, - esa, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); + nextResponse = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMTXACK, esa, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); response = nextResponse.proto as Messages.EthereumTxRequest; } } catch (error) { @@ -179,16 +172,13 @@ export async function ethGetAddress(transport: Transport, msg: core.ETHGetAddres const getAddr = new Messages.EthereumGetAddress(); getAddr.setAddressNList(msg.addressNList); getAddr.setShowDisplay(msg.showDisplay !== false); - const response = await transport.call( - Messages.MessageType.MESSAGETYPE_ETHEREUMGETADDRESS, - getAddr, - { - msgTimeout: core.LONG_TIMEOUT, - } - ); + const response = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMGETADDRESS, getAddr, { + msgTimeout: core.LONG_TIMEOUT, + }); const ethAddress = response.proto as Messages.EthereumAddress; let address: string; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (ethAddress.hasAddressStr()) address = ethAddress.getAddressStr()!; else if (ethAddress.hasAddress()) address = "0x" + core.toHexString(ethAddress.getAddress_asU8()); else throw new Error("Unable to obtain ETH address from device."); @@ -200,13 +190,9 @@ export async function ethSignMessage(transport: Transport, msg: core.ETHSignMess const m = new Messages.EthereumSignMessage(); m.setAddressNList(msg.addressNList); m.setMessage(toUTF8Array(msg.message)); - const response = await transport.call( - Messages.MessageType.MESSAGETYPE_ETHEREUMSIGNMESSAGE, - m, - { - msgTimeout: core.LONG_TIMEOUT, - } - ); + const response = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMSIGNMESSAGE, m, { + msgTimeout: core.LONG_TIMEOUT, + }); const sig = response.proto as Messages.EthereumMessageSignature; return { address: eip55.encode("0x" + core.toHexString(sig.getAddress_asU8())), // FIXME: this should be done in the firmware @@ -219,15 +205,11 @@ export async function ethVerifyMessage(transport: Transport, msg: core.ETHVerify m.setAddress(core.arrayify(msg.address)); m.setSignature(core.arrayify(msg.signature)); m.setMessage(toUTF8Array(msg.message)); - let event: core.Event + let event: core.Event; try { - event = await transport.call( - Messages.MessageType.MESSAGETYPE_ETHEREUMVERIFYMESSAGE, - m, - { + event = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMVERIFYMESSAGE, m, { msgTimeout: core.LONG_TIMEOUT, - } - ); + }); } catch (e) { if (core.isIndexable(e) && e.message_enum === Messages.MessageType.MESSAGETYPE_FAILURE) { return false; diff --git a/packages/hdwallet-keepkey/src/keepkey.ts b/packages/hdwallet-keepkey/src/keepkey.ts index 9eb0cef79..19714f3d7 100644 --- a/packages/hdwallet-keepkey/src/keepkey.ts +++ b/packages/hdwallet-keepkey/src/keepkey.ts @@ -1,6 +1,6 @@ -import * as core from "@shapeshiftoss/hdwallet-core"; import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; import * as Types from "@keepkey/device-protocol/lib/types_pb"; +import * as core from "@shapeshiftoss/hdwallet-core"; import _ from "lodash"; import semver from "semver"; @@ -20,8 +20,8 @@ export function isKeepKey(wallet: core.HDWallet): wallet is KeepKeyHDWallet { } function describeETHPath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Ethereum", isKnown: false, @@ -39,7 +39,7 @@ function describeETHPath(path: core.BIP32Path): core.PathDescription { if (path[4] != 0) return unknown; - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Ethereum Account #${index}`, accountIdx: index, @@ -55,8 +55,8 @@ function describeUTXOPath( coin: core.Coin, scriptType?: core.BTCInputScriptType ): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin, scriptType, @@ -72,7 +72,7 @@ function describeUTXOPath( if ((path[0] & 0x80000000) >>> 0 !== 0x80000000) return unknown; - let purpose = path[0] & 0x7fffffff; + const purpose = path[0] & 0x7fffffff; if (![44, 49, 84].includes(purpose)) return unknown; @@ -82,9 +82,9 @@ function describeUTXOPath( if (purpose === 84 && scriptType !== core.BTCInputScriptType.SpendWitness) return unknown; - let wholeAccount = path.length === 3; + const wholeAccount = path.length === 3; - let script = scriptType + const script = scriptType ? ( { [core.BTCInputScriptType.SpendAddress]: ["Legacy"], @@ -135,9 +135,9 @@ function describeUTXOPath( break; } - let attr = attributes.length ? ` (${attributes.join(", ")})` : ""; + const attr = attributes.length ? ` (${attributes.join(", ")})` : ""; - let accountIdx = path[2] & 0x7fffffff; + const accountIdx = path[2] & 0x7fffffff; if (wholeAccount) { return { @@ -150,8 +150,8 @@ function describeUTXOPath( isPrefork, }; } else { - let change = path[3] === 1 ? "Change " : ""; - let addressIdx = path[4]; + const change = path[3] === 1 ? "Change " : ""; + const addressIdx = path[4]; return { coin, verbose: `${coin} Account #${accountIdx}, ${change}Address #${addressIdx}${attr}`, @@ -167,8 +167,8 @@ function describeUTXOPath( } function describeCosmosPath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Atom", isKnown: false, @@ -194,7 +194,7 @@ function describeCosmosPath(path: core.BIP32Path): core.PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Cosmos Account #${index}`, accountIdx: index, @@ -206,8 +206,8 @@ function describeCosmosPath(path: core.BIP32Path): core.PathDescription { } function describeThorchainPath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Rune", isKnown: false, @@ -233,7 +233,7 @@ function describeThorchainPath(path: core.BIP32Path): core.PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `THORChain Account #${index}`, accountIdx: index, @@ -245,8 +245,8 @@ function describeThorchainPath(path: core.BIP32Path): core.PathDescription { } function describeEosPath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Eos", isKnown: false, @@ -272,7 +272,7 @@ function describeEosPath(path: core.BIP32Path): core.PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Eos Account #${index}`, accountIdx: index, @@ -284,8 +284,8 @@ function describeEosPath(path: core.BIP32Path): core.PathDescription { } function describeRipplePath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Ripple", isKnown: false, @@ -311,7 +311,7 @@ function describeRipplePath(path: core.BIP32Path): core.PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Ripple Account #${index}`, accountIdx: index, @@ -323,8 +323,8 @@ function describeRipplePath(path: core.BIP32Path): core.PathDescription { } function describeBinancePath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Binance", isKnown: false, @@ -350,7 +350,7 @@ function describeBinancePath(path: core.BIP32Path): core.PathDescription { return unknown; } - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Binance Account #${index}`, accountIdx: index, @@ -464,6 +464,7 @@ export class KeepKeyHDWalletInfo return false; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { return true; } @@ -494,12 +495,12 @@ export class KeepKeyHDWalletInfo } public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - let description = describeUTXOPath(msg.addressNList, msg.coin, msg.scriptType); + const description = describeUTXOPath(msg.addressNList, msg.coin, msg.scriptType); if (!description.isKnown) { return undefined; } - let addressNList = msg.addressNList; + const addressNList = msg.addressNList; if ( addressNList[0] === 0x80000000 + 44 || @@ -517,8 +518,8 @@ export class KeepKeyHDWalletInfo } public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - let addressNList = msg.hardenedPath.concat(msg.relPath); - let description = describeETHPath(addressNList); + const addressNList = msg.hardenedPath.concat(msg.relPath); + const description = describeETHPath(addressNList); if (!description.isKnown) { return undefined; } @@ -537,12 +538,12 @@ export class KeepKeyHDWalletInfo } public cosmosNextAccountPath(msg: core.CosmosAccountPath): core.CosmosAccountPath | undefined { - let description = describeCosmosPath(msg.addressNList); + const description = describeCosmosPath(msg.addressNList); if (!description.isKnown) { return undefined; } - let addressNList = msg.addressNList; + const addressNList = msg.addressNList; addressNList[2] += 1; return { @@ -552,12 +553,12 @@ export class KeepKeyHDWalletInfo } public thorchainNextAccountPath(msg: core.ThorchainAccountPath): core.ThorchainAccountPath | undefined { - let description = describeThorchainPath(msg.addressNList); + const description = describeThorchainPath(msg.addressNList); if (!description.isKnown) { return undefined; } - let addressNList = msg.addressNList; + const addressNList = msg.addressNList; addressNList[2] += 1; return { @@ -567,11 +568,11 @@ export class KeepKeyHDWalletInfo } public rippleNextAccountPath(msg: core.RippleAccountPath): core.RippleAccountPath | undefined { - let description = describeRipplePath(msg.addressNList); + const description = describeRipplePath(msg.addressNList); if (!description.isKnown) { return undefined; } - let addressNList = msg.addressNList; + const addressNList = msg.addressNList; addressNList[2] += 1; return { @@ -581,12 +582,12 @@ export class KeepKeyHDWalletInfo } public binanceNextAccountPath(msg: core.BinanceAccountPath): core.BinanceAccountPath | undefined { - let description = describeBinancePath(msg.addressNList); + const description = describeBinancePath(msg.addressNList); if (!description.isKnown) { return undefined; } - let addressNList = msg.addressNList; + const addressNList = msg.addressNList; addressNList[2] += 1; return { @@ -596,12 +597,12 @@ export class KeepKeyHDWalletInfo } public eosNextAccountPath(msg: core.EosAccountPath): core.EosAccountPath | undefined { - let description = describeEosPath(msg.addressNList); + const description = describeEosPath(msg.addressNList); if (!description.isKnown) { return undefined; } - let addressNList = msg.addressNList; + const addressNList = msg.addressNList; addressNList[2] += 1; return { @@ -703,13 +704,9 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW GPK.setEcdsaCurveName(curve || "secp256k1"); GPK.setScriptType(translateInputScriptType(scriptType || core.BTCInputScriptType.SpendAddress)); - const event = await this.transport.call( - Messages.MessageType.MESSAGETYPE_GETPUBLICKEY, - GPK, - { - msgTimeout: showDisplay ? core.LONG_TIMEOUT : core.DEFAULT_TIMEOUT, - } - ); + const event = await this.transport.call(Messages.MessageType.MESSAGETYPE_GETPUBLICKEY, GPK, { + msgTimeout: showDisplay ? core.LONG_TIMEOUT : core.DEFAULT_TIMEOUT, + }); const publicKey = event.proto as Messages.PublicKey; publicKeys.push({ xpub: core.mustBeDefined(publicKey.getXpub()) }); @@ -723,13 +720,9 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW ping.setButtonProtection(msg.button || false); ping.setPinProtection(msg.pin || false); ping.setPassphraseProtection(msg.passphrase || false); - const event = await this.transport.call( - Messages.MessageType.MESSAGETYPE_PING, - ping, - { - msgTimeout: msg.button || msg.pin || msg.passphrase ? core.LONG_TIMEOUT : core.DEFAULT_TIMEOUT, - } - ); + const event = await this.transport.call(Messages.MessageType.MESSAGETYPE_PING, ping, { + msgTimeout: msg.button || msg.pin || msg.passphrase ? core.LONG_TIMEOUT : core.DEFAULT_TIMEOUT, + }); const message = event.proto as Messages.Success; return { msg: core.mustBeDefined(message.getMessage()) }; } @@ -781,7 +774,7 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW } public async press(isYes: boolean): Promise { - let decision = new Messages.DebugLinkDecision(); + const decision = new Messages.DebugLinkDecision(); decision.setYesNo(isYes); await this.transport.call(Messages.MessageType.MESSAGETYPE_DEBUGLINKDECISION, decision, { @@ -806,6 +799,7 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW return false; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { return true; } @@ -821,35 +815,21 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW public async sendPin(pin: string): Promise { const matrixAck = new Messages.PinMatrixAck(); matrixAck.setPin(pin); - console.assert( - undefined === - (await this.transport.call( - Messages.MessageType.MESSAGETYPE_PINMATRIXACK, - matrixAck, - { - msgTimeout: core.DEFAULT_TIMEOUT, - omitLock: true, - noWait: true, - } - )) - ); + await this.transport.call(Messages.MessageType.MESSAGETYPE_PINMATRIXACK, matrixAck, { + msgTimeout: core.DEFAULT_TIMEOUT, + omitLock: true, + noWait: true, + }); } public async sendPassphrase(passphrase: string): Promise { const passphraseAck = new Messages.PassphraseAck(); passphraseAck.setPassphrase(passphrase); - console.assert( - undefined === - (await this.transport.call( - Messages.MessageType.MESSAGETYPE_PASSPHRASEACK, - passphraseAck, - { - msgTimeout: core.DEFAULT_TIMEOUT, - omitLock: true, - noWait: true, - } - )) - ); + await this.transport.call(Messages.MessageType.MESSAGETYPE_PASSPHRASEACK, passphraseAck, { + msgTimeout: core.DEFAULT_TIMEOUT, + omitLock: true, + noWait: true, + }); } public async sendCharacter(character: string): Promise { @@ -864,6 +844,7 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW await this.sendCharacterProto("", false, true); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendWord(word: string): Promise { throw new Error("Not Yet Implemented :("); } @@ -877,18 +858,11 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW } else if (_done) { characterAck.setDone(_done); } - console.assert( - undefined === - (await this.transport.call( - Messages.MessageType.MESSAGETYPE_CHARACTERACK, - characterAck, - { - msgTimeout: core.DEFAULT_TIMEOUT, - omitLock: true, - noWait: true, - } - )) - ); + await this.transport.call(Messages.MessageType.MESSAGETYPE_CHARACTERACK, characterAck, { + msgTimeout: core.DEFAULT_TIMEOUT, + omitLock: true, + noWait: true, + }); } // ApplyPolicy enables or disables a named policy such as "ShapeShift" on the device @@ -958,10 +932,7 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW cipherKeyValue.setAskOnEncrypt(v.askOnEncrypt || false); cipherKeyValue.setAskOnDecrypt(v.askOnDecrypt || false); cipherKeyValue.setIv(v.iv || ""); - const response = await this.transport.call( - Messages.MessageType.MESSAGETYPE_CIPHERKEYVALUE, - cipherKeyValue, - ); + const response = await this.transport.call(Messages.MessageType.MESSAGETYPE_CIPHERKEYVALUE, cipherKeyValue); const ckv = response.message as Messages.CipheredKeyValue; return ckv.getValue(); } @@ -1029,7 +1000,7 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW } // GetFeatures returns the features and other device information such as the version, label, and supported coins - public async getFeatures(cached: boolean = false): Promise { + public async getFeatures(cached = false): Promise { if (cached && this.featuresCache) return this.featuresCache; const features = new Messages.GetFeatures(); const event = await this.transport.call(Messages.MessageType.MESSAGETYPE_GETFEATURES, features); @@ -1061,7 +1032,7 @@ export class KeepKeyHDWallet implements core.HDWallet, core.BTCWallet, core.ETHW // GetCoinTable returns an array of Types.CoinTypes, with start and end arguments for paging. // You cannot request more than 10 at a time. - public async getCoinTable(start: number = 0, end: number = start + 10): Promise { + public async getCoinTable(start = 0, end: number = start + 10): Promise { const getCoinTable = new Messages.GetCoinTable(); getCoinTable.setStart(start); getCoinTable.setEnd(end); diff --git a/packages/hdwallet-keepkey/src/ripple.ts b/packages/hdwallet-keepkey/src/ripple.ts index e472108f8..5b440fd51 100644 --- a/packages/hdwallet-keepkey/src/ripple.ts +++ b/packages/hdwallet-keepkey/src/ripple.ts @@ -1,5 +1,5 @@ -import * as RippleMessages from "@keepkey/device-protocol/lib/messages-ripple_pb"; import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; +import * as RippleMessages from "@keepkey/device-protocol/lib/messages-ripple_pb"; import * as core from "@shapeshiftoss/hdwallet-core"; import _ from "lodash"; @@ -27,18 +27,12 @@ export async function rippleSignTx(transport: Transport, msg: core.RippleSignTx) if (msg.payment.destinationTag !== undefined) payment.setDestinationTag(parseInt(msg.payment.destinationTag)); signTx.setPayment(payment); - let resp = await transport.call( - Messages.MessageType.MESSAGETYPE_RIPPLESIGNTX, - signTx, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); - - for (let m of msg.tx.value.msg) { - let ack; + const resp = await transport.call(Messages.MessageType.MESSAGETYPE_RIPPLESIGNTX, signTx, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); + for (const m of msg.tx.value.msg) { if (m.type === "ripple-sdk/MsgSend") { if (m.value.amount.length !== 1) { throw new Error("ripple: Multiple amounts per msg not supported"); diff --git a/packages/hdwallet-keepkey/src/thorchain.ts b/packages/hdwallet-keepkey/src/thorchain.ts index cf4679c0c..4a35932a7 100644 --- a/packages/hdwallet-keepkey/src/thorchain.ts +++ b/packages/hdwallet-keepkey/src/thorchain.ts @@ -1,5 +1,5 @@ -import * as ThorchainMessages from "@keepkey/device-protocol/lib/messages-thorchain_pb"; import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; +import * as ThorchainMessages from "@keepkey/device-protocol/lib/messages-thorchain_pb"; import * as core from "@shapeshiftoss/hdwallet-core"; import _ from "lodash"; @@ -25,16 +25,12 @@ export async function thorchainSignTx(transport: Transport, msg: core.ThorchainS if (msg.tx.memo !== undefined) signTx.setMemo(msg.tx.memo); signTx.setMsgCount(1); - let resp = await transport.call( - Messages.MessageType.MESSAGETYPE_THORCHAINSIGNTX, - signTx, - { - msgTimeout: core.LONG_TIMEOUT, - omitLock: true, - } - ); + let resp = await transport.call(Messages.MessageType.MESSAGETYPE_THORCHAINSIGNTX, signTx, { + msgTimeout: core.LONG_TIMEOUT, + omitLock: true, + }); - for (let m of msg.tx.msg) { + for (const m of msg.tx.msg) { if (resp.message_enum !== Messages.MessageType.MESSAGETYPE_THORCHAINMSGREQUEST) { throw new Error(`THORChain: unexpected response ${resp.message_type}`); } @@ -72,7 +68,7 @@ export async function thorchainSignTx(transport: Transport, msg: core.ThorchainS deposit.setAsset(m.value.coins[0].asset); deposit.setAmount(m.value.coins[0].amount); deposit.setMemo(m.value.memo); - deposit.setSigner(m.value.signer) + deposit.setSigner(m.value.signer); ack = new ThorchainMessages.ThorchainMsgAck(); ack.setDeposit(deposit); @@ -108,11 +104,14 @@ export async function thorchainSignTx(transport: Transport, msg: core.ThorchainS }); } -export async function thorchainGetAddress(transport: Transport, msg: ThorchainMessages.ThorchainGetAddress.AsObject): Promise { +export async function thorchainGetAddress( + transport: Transport, + msg: ThorchainMessages.ThorchainGetAddress.AsObject +): Promise { const getAddr = new ThorchainMessages.ThorchainGetAddress(); getAddr.setAddressNList(msg.addressNList); getAddr.setShowDisplay(msg.showDisplay !== false); - if (msg.testnet !== undefined) getAddr.setTestnet(msg.testnet) + if (msg.testnet !== undefined) getAddr.setTestnet(msg.testnet); const response = await transport.call(Messages.MessageType.MESSAGETYPE_THORCHAINGETADDRESS, getAddr, { msgTimeout: core.LONG_TIMEOUT, }); diff --git a/packages/hdwallet-keepkey/src/transport.ts b/packages/hdwallet-keepkey/src/transport.ts index a838c0c60..25ed9a2bb 100644 --- a/packages/hdwallet-keepkey/src/transport.ts +++ b/packages/hdwallet-keepkey/src/transport.ts @@ -1,9 +1,10 @@ -import * as Messages from "@keepkey/device-protocol/lib/messages_pb" -import * as Types from "@keepkey/device-protocol/lib/types_pb" +import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; +import * as Types from "@keepkey/device-protocol/lib/types_pb"; import * as core from "@shapeshiftoss/hdwallet-core"; +import * as crypto from "crypto"; import * as jspb from "google-protobuf"; -import { messageTypeRegistry, messageNameRegistry } from "./typeRegistry"; +import { messageNameRegistry, messageTypeRegistry } from "./typeRegistry"; import { SEGMENT_SIZE } from "./utils"; export interface TransportDelegate { @@ -19,8 +20,8 @@ export interface TransportDelegate { } export class Transport extends core.Transport { - debugLink: boolean = false; - userActionRequired: boolean = false; + debugLink = false; + userActionRequired = false; delegate: TransportDelegate; /// One per transport, unlike on Trezor, since the contention is @@ -53,8 +54,8 @@ export class Transport extends core.Transport { return this.delegate.connect(); } public async tryConnectDebugLink(): Promise { - let out = false - if (this.delegate.tryConnectDebugLink && await this.delegate.tryConnectDebugLink()) out = true; + let out = false; + if (this.delegate.tryConnectDebugLink && (await this.delegate.tryConnectDebugLink())) out = true; this.debugLink = out; return out; } @@ -93,7 +94,7 @@ export class Transport extends core.Transport { for (let offset = first.length; offset < buffer.length; ) { // Drop USB "?" reportId in the first byte - let next = (await this.delegate.readChunk(debugLink)).slice(1); + const next = (await this.delegate.readChunk(debugLink)).slice(1); buffer.set(next.slice(0, Math.min(next.length, buffer.length - offset)), offset); offset += next.length; } @@ -109,16 +110,14 @@ export class Transport extends core.Transport { if (typeof window !== "undefined" && window?.crypto) { return window.crypto.getRandomValues(new Uint8Array(length)); } - const { randomBytes } = require("crypto"); - return randomBytes(length); + return crypto.randomBytes(length); } public async getFirmwareHash(firmware: Uint8Array): Promise { if (typeof window !== "undefined" && window?.crypto) { return new Uint8Array(await window.crypto.subtle.digest({ name: "SHA-256" }, firmware)); } - const { createHash } = require("crypto"); - const hash = createHash("sha256"); + const hash = crypto.createHash("sha256"); hash.update(firmware); return hash.digest(); } @@ -147,7 +146,7 @@ export class Transport extends core.Transport { return this.callInProgress.main; } - public async handleCancellableResponse(messageType: any) { + public async handleCancellableResponse() { return this.readResponse(false); } @@ -157,7 +156,7 @@ export class Transport extends core.Transport { buf = await this.read(debugLink); } while (!buf); const [msgTypeEnum, msg] = this.fromMessageBuffer(buf); - let event = core.makeEvent({ + const event = core.makeEvent({ message_type: messageNameRegistry[msgTypeEnum], message_enum: msgTypeEnum, message: msg.toObject(), @@ -212,7 +211,7 @@ export class Transport extends core.Transport { }) ); this.userActionRequired = true; - return this.handleCancellableResponse(Messages.MessageType.MESSAGETYPE_PINMATRIXACK); + return this.handleCancellableResponse(); } if (msgTypeEnum === Messages.MessageType.MESSAGETYPE_PASSPHRASEREQUEST) { @@ -224,7 +223,7 @@ export class Transport extends core.Transport { }) ); this.userActionRequired = true; - return this.handleCancellableResponse(Messages.MessageType.MESSAGETYPE_PASSPHRASEACK); + return this.handleCancellableResponse(); } if (msgTypeEnum === Messages.MessageType.MESSAGETYPE_CHARACTERREQUEST) { @@ -236,7 +235,7 @@ export class Transport extends core.Transport { }) ); this.userActionRequired = true; - return this.handleCancellableResponse(Messages.MessageType.MESSAGETYPE_CHARACTERACK); + return this.handleCancellableResponse(); } if (msgTypeEnum === Messages.MessageType.MESSAGETYPE_WORDREQUEST) { @@ -248,7 +247,7 @@ export class Transport extends core.Transport { }) ); this.userActionRequired = true; - return this.handleCancellableResponse(Messages.MessageType.MESSAGETYPE_WORDACK); + return this.handleCancellableResponse(); } return event; @@ -258,30 +257,30 @@ export class Transport extends core.Transport { msgTypeEnum: number, msg: jspb.Message, options?: { - msgTimeout?: number, - omitLock?: boolean, - noWait?: false, - debugLink?: boolean, + msgTimeout?: number; + omitLock?: boolean; + noWait?: false; + debugLink?: boolean; } ): Promise; public async call( msgTypeEnum: number, msg: jspb.Message, options: { - msgTimeout?: number, - omitLock?: boolean, - noWait: true, - debugLink?: boolean, + msgTimeout?: number; + omitLock?: boolean; + noWait: true; + debugLink?: boolean; } ): Promise; public async call( msgTypeEnum: number, msg: jspb.Message, options?: { - msgTimeout?: number, - omitLock?: boolean, - noWait?: boolean, - debugLink?: boolean, + msgTimeout?: number; + omitLock?: boolean; + noWait?: boolean; + debugLink?: boolean; } ): Promise { options ??= {}; @@ -300,13 +299,15 @@ export class Transport extends core.Transport { const makePromise = async () => { if ( - ([ - Messages.MessageType.MESSAGETYPE_BUTTONACK, - Messages.MessageType.MESSAGETYPE_PASSPHRASEACK, - Messages.MessageType.MESSAGETYPE_CHARACTERACK, - Messages.MessageType.MESSAGETYPE_PINMATRIXACK, - Messages.MessageType.MESSAGETYPE_WORDACK, - ] as Array).includes(msgTypeEnum) + ( + [ + Messages.MessageType.MESSAGETYPE_BUTTONACK, + Messages.MessageType.MESSAGETYPE_PASSPHRASEACK, + Messages.MessageType.MESSAGETYPE_CHARACTERACK, + Messages.MessageType.MESSAGETYPE_PINMATRIXACK, + Messages.MessageType.MESSAGETYPE_WORDACK, + ] as Array + ).includes(msgTypeEnum) ) { this.userActionRequired = true; } @@ -332,7 +333,7 @@ export class Transport extends core.Transport { // See the comments in hdwallet-trezor-connect's call for why this weird // sequence. We've got a very similar issue here that needs pretty much // the same solution. - const lockKey = options?.debugLink ? "debug" : "main" + const lockKey = options?.debugLink ? "debug" : "main"; this.callInProgress[lockKey] = (async () => { await this.cancellable(this.callInProgress[lockKey]); @@ -352,7 +353,7 @@ export class Transport extends core.Transport { this.callInProgress = { main: undefined, debug: undefined }; const cancelMsg = new Messages.Cancel(); await this.call(Messages.MessageType.MESSAGETYPE_CANCEL, cancelMsg, { - noWait: this.userActionRequired + noWait: this.userActionRequired, }); } catch (e) { console.error("Cancel Pending Error", e); diff --git a/packages/hdwallet-keepkey/src/typeRegistry.ts b/packages/hdwallet-keepkey/src/typeRegistry.ts index 9728e6dd8..eb11f2b97 100644 --- a/packages/hdwallet-keepkey/src/typeRegistry.ts +++ b/packages/hdwallet-keepkey/src/typeRegistry.ts @@ -1,10 +1,10 @@ +import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; import * as BinanceMessages from "@keepkey/device-protocol/lib/messages-binance_pb"; import * as CosmosMessages from "@keepkey/device-protocol/lib/messages-cosmos_pb"; import * as EosMessages from "@keepkey/device-protocol/lib/messages-eos_pb"; import * as NanoMessages from "@keepkey/device-protocol/lib/messages-nano_pb"; import * as RippleMessages from "@keepkey/device-protocol/lib/messages-ripple_pb"; import * as ThorchainMessages from "@keepkey/device-protocol/lib/messages-thorchain_pb"; -import * as Messages from "@keepkey/device-protocol/lib/messages_pb"; import * as core from "@shapeshiftoss/hdwallet-core"; import * as jspb from "google-protobuf"; import _ from "lodash"; @@ -20,28 +20,19 @@ const AllMessages = ([] as Array<[string, core.Constructor]>) .concat(Object.entries(_.omit(EosMessages, "EosPublicKeyKind", "EosPublicKeyKindMap"))) .concat(Object.entries(ThorchainMessages)); -const upperCasedMessageClasses = AllMessages.reduce( - (registry, entry: [string, core.Constructor]) => { - registry[entry[0].toUpperCase()] = entry[1]; - return registry; - }, - {} as Record> -); +const upperCasedMessageClasses = AllMessages.reduce((registry, entry: [string, core.Constructor]) => { + registry[entry[0].toUpperCase()] = entry[1]; + return registry; +}, {} as Record>); // Map of message type enums to human readable message name -export const messageNameRegistry = Object.entries(Messages.MessageType).reduce( - (registry, entry: [string, number]) => { - registry[entry[1]] = entry[0].split("_")[1]; - return registry; - }, - {} as Record -); +export const messageNameRegistry = Object.entries(Messages.MessageType).reduce((registry, entry: [string, number]) => { + registry[entry[1]] = entry[0].split("_")[1]; + return registry; +}, {} as Record); // Map of message type enum to their protobuf constructor -export const messageTypeRegistry = Object.entries(Messages.MessageType).reduce( - (registry, entry: [string, number]) => { - registry[entry[1]] = upperCasedMessageClasses[entry[0].split("_")[1].toUpperCase()]; - return registry; - }, - {} as Record> -); +export const messageTypeRegistry = Object.entries(Messages.MessageType).reduce((registry, entry: [string, number]) => { + registry[entry[1]] = upperCasedMessageClasses[entry[0].split("_")[1].toUpperCase()]; + return registry; +}, {} as Record>); diff --git a/packages/hdwallet-keepkey/src/utils.ts b/packages/hdwallet-keepkey/src/utils.ts index 667d760d7..e6d249003 100644 --- a/packages/hdwallet-keepkey/src/utils.ts +++ b/packages/hdwallet-keepkey/src/utils.ts @@ -10,9 +10,9 @@ export function protoFieldToSetMethod(fieldName: string): string { // https://gist.github.com/joni/3760795/8f0c1a608b7f0c8b3978db68105c5b1d741d0446 export function toUTF8Array(str: string): Uint8Array { - var utf8: Array = []; - for (var i = 0; i < str.length; i++) { - var charcode = str.charCodeAt(i); + const utf8: Array = []; + for (let i = 0; i < str.length; i++) { + let charcode = str.charCodeAt(i); if (charcode < 0x80) utf8.push(charcode); else if (charcode < 0x800) { utf8.push(0xc0 | (charcode >> 6), 0x80 | (charcode & 0x3f)); diff --git a/packages/hdwallet-ledger-webhid/src/adapter.ts b/packages/hdwallet-ledger-webhid/src/adapter.ts index a7f9bed83..9b656b1da 100644 --- a/packages/hdwallet-ledger-webhid/src/adapter.ts +++ b/packages/hdwallet-ledger-webhid/src/adapter.ts @@ -1,14 +1,20 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as ledger from "@shapeshiftoss/hdwallet-ledger"; -import { LedgerWebHIDTransport, MOCK_SERIAL_NUMBER, getFirstLedgerDevice, getTransport, openTransport } from "./transport"; +import { + getFirstLedgerDevice, + getTransport, + LedgerWebHIDTransport, + MOCK_SERIAL_NUMBER, + openTransport, +} from "./transport"; const VENDOR_ID = 11415; const APP_NAVIGATION_DELAY = 3000; export class WebHIDLedgerAdapter { keyring: core.Keyring; - currentEventTimestamp: number = 0; + currentEventTimestamp = 0; constructor(keyring: core.Keyring) { this.keyring = keyring; @@ -52,8 +58,8 @@ export class WebHIDLedgerAdapter { try { await this.keyring.remove(MOCK_SERIAL_NUMBER); - } catch (e) { - console.error(e); + } catch (error) { + console.error(error); } finally { this.keyring.emit(["Ledger", e.device.productName ?? "", core.Events.DISCONNECT], MOCK_SERIAL_NUMBER); } diff --git a/packages/hdwallet-ledger-webhid/src/transport.ts b/packages/hdwallet-ledger-webhid/src/transport.ts index 20367ea64..73656a7cc 100644 --- a/packages/hdwallet-ledger-webhid/src/transport.ts +++ b/packages/hdwallet-ledger-webhid/src/transport.ts @@ -16,7 +16,7 @@ import { const RECORD_CONFORMANCE_MOCKS = false; -export const MOCK_SERIAL_NUMBER = "ledger-webhid-device" // WebHID devices do not have serialNumbers +export const MOCK_SERIAL_NUMBER = "ledger-webhid-device"; // WebHID devices do not have serialNumbers export async function getFirstLedgerDevice(): Promise { if (!(window && window.navigator.hid)) throw new core.WebHIDNotAvailable(); @@ -62,12 +62,12 @@ export function translateCoinAndMethod].bind(btc) + const methodInstance = btc[method as LedgerTransportMethodName<"Btc">].bind(btc); return methodInstance as LedgerTransportMethod; } case "Eth": { const eth = new Eth(transport); - const methodInstance = eth[method as LedgerTransportMethodName<"Eth">].bind(eth) + const methodInstance = eth[method as LedgerTransportMethodName<"Eth">].bind(eth); return methodInstance as LedgerTransportMethod; } case null: { @@ -139,7 +139,7 @@ export class LedgerWebHIDTransport extends ledger.LedgerTransport { if (RECORD_CONFORMANCE_MOCKS) { // May need a slight amount of cleanup on escaping `'`s. - console.log( + console.info( `this.memoize('${coin}', '${method}',\n JSON.parse('${JSON.stringify( args )}'),\n JSON.parse('${JSON.stringify(result)}'))` diff --git a/packages/hdwallet-ledger-webusb/src/adapter.ts b/packages/hdwallet-ledger-webusb/src/adapter.ts index 44118a456..ff68339a9 100644 --- a/packages/hdwallet-ledger-webusb/src/adapter.ts +++ b/packages/hdwallet-ledger-webusb/src/adapter.ts @@ -1,14 +1,14 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as ledger from "@shapeshiftoss/hdwallet-ledger"; -import { LedgerWebUsbTransport, getFirstLedgerDevice, getTransport, openTransport } from "./transport"; +import { getFirstLedgerDevice, getTransport, LedgerWebUsbTransport, openTransport } from "./transport"; const VENDOR_ID = 11415; const APP_NAVIGATION_DELAY = 3000; export class WebUSBLedgerAdapter { keyring: core.Keyring; - currentEventTimestamp: number = 0; + currentEventTimestamp = 0; constructor(keyring: core.Keyring) { this.keyring = keyring; @@ -30,7 +30,10 @@ export class WebUSBLedgerAdapter { try { await this.initialize(e.device); - this.keyring.emit([e.device.manufacturerName ?? "", e.device.productName ?? "", core.Events.CONNECT], e.device.serialNumber); + this.keyring.emit( + [e.device.manufacturerName ?? "", e.device.productName ?? "", core.Events.CONNECT], + e.device.serialNumber + ); } catch (error: any) { this.keyring.emit( [e.device.manufacturerName ?? "", e.device.productName ?? "", core.Events.FAILURE], @@ -52,10 +55,13 @@ export class WebUSBLedgerAdapter { try { if (e.device.serialNumber) await this.keyring.remove(e.device.serialNumber); - } catch (e) { - console.error(e); + } catch (error) { + console.error(error); } finally { - this.keyring.emit([e.device.manufacturerName ?? "", e.device.productName ?? "", core.Events.DISCONNECT], e.device.serialNumber); + this.keyring.emit( + [e.device.manufacturerName ?? "", e.device.productName ?? "", core.Events.DISCONNECT], + e.device.serialNumber + ); } }, APP_NAVIGATION_DELAY); } @@ -73,7 +79,9 @@ export class WebUSBLedgerAdapter { const ledgerTransport = await openTransport(device); - const wallet = ledger.create(new LedgerWebUsbTransport(device, ledgerTransport, this.keyring) as ledger.LedgerTransport); + const wallet = ledger.create( + new LedgerWebUsbTransport(device, ledgerTransport, this.keyring) as ledger.LedgerTransport + ); this.keyring.add(wallet, device.serialNumber); } diff --git a/packages/hdwallet-ledger-webusb/src/transport.ts b/packages/hdwallet-ledger-webusb/src/transport.ts index a6da1730e..fca830482 100644 --- a/packages/hdwallet-ledger-webusb/src/transport.ts +++ b/packages/hdwallet-ledger-webusb/src/transport.ts @@ -60,12 +60,12 @@ export function translateCoinAndMethod].bind(btc) + const methodInstance = btc[method as LedgerTransportMethodName<"Btc">].bind(btc); return methodInstance as LedgerTransportMethod; } case "Eth": { const eth = new Eth(transport); - const methodInstance = eth[method as LedgerTransportMethodName<"Eth">].bind(eth) + const methodInstance = eth[method as LedgerTransportMethodName<"Eth">].bind(eth); return methodInstance as LedgerTransportMethod; } case null: { @@ -136,7 +136,7 @@ export class LedgerWebUsbTransport extends ledger.LedgerTransport { if (RECORD_CONFORMANCE_MOCKS) { // May need a slight amount of cleanup on escaping `'`s. - console.log( + console.info( `this.memoize('${coin}', '${method}',\n JSON.parse('${JSON.stringify( args )}'),\n JSON.parse('${JSON.stringify(result)}'))` diff --git a/packages/hdwallet-ledger/src/bitcoin.ts b/packages/hdwallet-ledger/src/bitcoin.ts index 9aab59c2f..88816513b 100644 --- a/packages/hdwallet-ledger/src/bitcoin.ts +++ b/packages/hdwallet-ledger/src/bitcoin.ts @@ -1,3 +1,4 @@ +import { CreateTransactionArg } from "@ledgerhq/hw-app-btc/lib/createTransaction"; import { Transaction } from "@ledgerhq/hw-app-btc/lib/types"; import * as core from "@shapeshiftoss/hdwallet-core"; import Base64 from "base64-js"; @@ -7,8 +8,7 @@ import * as bitcoinMsg from "bitcoinjs-message"; import _ from "lodash"; import { LedgerTransport } from "./transport"; -import { createXpub, compressPublicKey, translateScriptType, networksUtil, handleError } from "./utils"; -import { CreateTransactionArg } from "@ledgerhq/hw-app-btc/lib/createTransaction"; +import { compressPublicKey, createXpub, handleError, networksUtil, translateScriptType } from "./utils"; export const supportedCoins = ["Testnet", "Bitcoin", "BitcoinCash", "Litecoin", "Dash", "DigiByte", "Dogecoin"]; @@ -53,7 +53,9 @@ export async function btcGetPublicKeys( const xpubs: Array = []; for (const getPublicKey of msg) { - let { addressNList, coin, scriptType } = getPublicKey; + let { scriptType } = getPublicKey; + const { addressNList, coin } = getPublicKey; + if (!coin) throw new Error("coin is required"); const parentBip32path: string = core.addressNListToBIP32(addressNList.slice(0, -1)).substring(2); // i.e. "44'/0'" @@ -142,13 +144,13 @@ export async function btcSignTx( transport: LedgerTransport, msg: core.BTCSignTxLedger ): Promise { - let supportsShapeShift = wallet.btcSupportsNativeShapeShift(); - let supportsSecureTransfer = await wallet.btcSupportsSecureTransfer(); - let slip44 = core.mustBeDefined(core.slip44ByCoin(msg.coin)); - let txBuilder = new bitcoin.TransactionBuilder(networksUtil[slip44].bitcoinjs as any); - let indexes: number[] = []; - let txs: Transaction[] = []; - let associatedKeysets: string[] = []; + const supportsShapeShift = wallet.btcSupportsNativeShapeShift(); + const supportsSecureTransfer = await wallet.btcSupportsSecureTransfer(); + const slip44 = core.mustBeDefined(core.slip44ByCoin(msg.coin)); + const txBuilder = new bitcoin.TransactionBuilder(networksUtil[slip44].bitcoinjs as any); + const indexes: number[] = []; + const txs: Transaction[] = []; + const associatedKeysets: string[] = []; let segwit = false; //bitcoinjs-lib @@ -180,10 +182,10 @@ export async function btcSignTx( txBuilder.addOutput(ret, 0); } - let unsignedHex = txBuilder.buildIncomplete().toHex(); - let splitTxRes = await transport.call("Btc", "splitTransaction", unsignedHex); + const unsignedHex = txBuilder.buildIncomplete().toHex(); + const splitTxRes = await transport.call("Btc", "splitTransaction", unsignedHex); handleError(splitTxRes, transport, "splitTransaction failed"); - let outputScriptRes = await transport.call("Btc", "serializeTransactionOutputs", splitTxRes.payload); + const outputScriptRes = await transport.call("Btc", "serializeTransactionOutputs", splitTxRes.payload); handleError(outputScriptRes, transport, "serializeTransactionOutputs failed"); const outputScriptHex = outputScriptRes.payload.toString("hex"); @@ -196,7 +198,7 @@ export async function btcSignTx( const keySet = core.addressNListToBIP32(msg.inputs[i].addressNList).replace(/^m\//, ""); - let vout = msg.inputs[i].vout; + const vout = msg.inputs[i].vout; const tx = await transport.call( "Btc", @@ -213,7 +215,7 @@ export async function btcSignTx( } if (txs.length !== indexes.length) throw new Error("tx/index array length mismatch"); - const inputs = _.zip(txs, indexes, [], []) as Array<[Transaction, number, undefined, undefined]> + const inputs = _.zip(txs, indexes, [], []) as Array<[Transaction, number, undefined, undefined]>; const txArgs: CreateTransactionArg = { inputs, @@ -221,14 +223,14 @@ export async function btcSignTx( outputScriptHex, additionals: msg.coin === "BitcoinCash" ? ["abc"] : [], segwit, - } + }; // "invalid data received" error from Ledger if not done this way: if (networksUtil[slip44].sigHash) { - txArgs.sigHashType = networksUtil[slip44].sigHash + txArgs.sigHashType = networksUtil[slip44].sigHash; } - let signedTx = await transport.call("Btc", "createPaymentTransactionNew", txArgs); + const signedTx = await transport.call("Btc", "createPaymentTransactionNew", txArgs); handleError(signedTx, transport, "Could not sign transaction with device"); return { @@ -310,6 +312,8 @@ export function btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array): boolean { + // TODO: There's no way this is correct. return true; } diff --git a/packages/hdwallet-ledger/src/ethereum.ts b/packages/hdwallet-ledger/src/ethereum.ts index b020a7c5a..a799a9664 100644 --- a/packages/hdwallet-ledger/src/ethereum.ts +++ b/packages/hdwallet-ledger/src/ethereum.ts @@ -1,11 +1,11 @@ -import * as core from "@shapeshiftoss/hdwallet-core"; import Common from "@ethereumjs/common"; -import EthereumTx from "ethereumjs-tx"; import { Transaction } from "@ethereumjs/tx"; +import * as core from "@shapeshiftoss/hdwallet-core"; +import EthereumTx from "ethereumjs-tx"; import * as ethereumUtil from "ethereumjs-util"; import { LedgerTransport } from "./transport"; -import { createXpub, compressPublicKey, networksUtil, handleError } from "./utils"; +import { compressPublicKey, createXpub, handleError, networksUtil } from "./utils"; export async function ethSupportsNetwork(chain_id: number): Promise { return chain_id === 1; @@ -27,7 +27,9 @@ export async function ethGetPublicKeys( const xpubs = []; for (const getPublicKey of msg) { - let { addressNList, coin, scriptType } = getPublicKey; + const { addressNList, coin } = getPublicKey; + let { scriptType } = getPublicKey; + if (!scriptType) scriptType = core.BTCInputScriptType.SpendAddress; // Only get public keys for ETH account paths @@ -73,7 +75,7 @@ export async function ethGetPublicKeys( export async function ethSignTx(transport: LedgerTransport, msg: core.ETHSignTx): Promise { const bip32path = core.addressNListToBIP32(msg.addressNList); - const common = new Common({chain: "mainnet", hardfork: "london"}); + const common = new Common({ chain: "mainnet", hardfork: "london" }); const txParams = { to: msg.to, value: msg.value, @@ -87,19 +89,22 @@ export async function ethSignTx(transport: LedgerTransport, msg: core.ETHSignTx) s: "0x00", }; - let utx = new EthereumTx(txParams); + const utx = new EthereumTx(txParams); const res = await transport.call("Eth", "signTransaction", bip32path, utx.serialize().toString("hex")); handleError(res, transport, "Could not sign ETH tx with Ledger"); const { v, r, s } = res.payload; - const tx = Transaction.fromTxData({ - ...txParams, - v: "0x" + v, - r: "0x" + r, - s: "0x" + s, - }, { common }); + const tx = Transaction.fromTxData( + { + ...txParams, + v: "0x" + v, + r: "0x" + r, + s: "0x" + s, + }, + { common } + ); return { v: parseInt(v, 16), @@ -148,7 +153,9 @@ export async function ethSignMessage( const res = await transport.call("Eth", "signPersonalMessage", bip32path, Buffer.from(msg.message).toString("hex")); handleError(res, transport, "Could not sign ETH message with Ledger"); - let { v, r, s } = res.payload; + let { v } = res.payload; + const { r, s } = res.payload; + v = v - 27; const vStr = v.toString(16).padStart(2, "0"); const addressRes = await transport.call("Eth", "getAddress", bip32path, false); diff --git a/packages/hdwallet-ledger/src/ledger.ts b/packages/hdwallet-ledger/src/ledger.ts index 1a834c704..77aa91070 100644 --- a/packages/hdwallet-ledger/src/ledger.ts +++ b/packages/hdwallet-ledger/src/ledger.ts @@ -11,8 +11,8 @@ export function isLedger(wallet: core.HDWallet): wallet is LedgerHDWallet { } function describeETHPath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Ethereum", isKnown: false, @@ -54,8 +54,8 @@ function describeETHPath(path: core.BIP32Path): core.PathDescription { } function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: core.BTCInputScriptType) { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin, scriptType, @@ -70,7 +70,7 @@ function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: co if ((path[0] & 0x80000000) >>> 0 !== 0x80000000) return unknown; - let purpose = path[0] & 0x7fffffff; + const purpose = path[0] & 0x7fffffff; if (![44, 49, 84].includes(purpose)) return unknown; @@ -83,16 +83,16 @@ function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: co const slip44 = core.slip44ByCoin(coin); if (slip44 === undefined || path[1] !== 0x80000000 + slip44) return unknown; - let wholeAccount = path.length === 3; + const wholeAccount = path.length === 3; let script = scriptType ? ( - { - [core.BTCInputScriptType.SpendAddress]: " (Legacy)", - [core.BTCInputScriptType.SpendP2SHWitness]: "", - [core.BTCInputScriptType.SpendWitness]: " (Segwit Native)", - } as Partial> - )[scriptType] + { + [core.BTCInputScriptType.SpendAddress]: " (Legacy)", + [core.BTCInputScriptType.SpendP2SHWitness]: "", + [core.BTCInputScriptType.SpendWitness]: " (Segwit Native)", + } as Partial> + )[scriptType] : undefined; switch (coin) { @@ -105,7 +105,7 @@ function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: co script = ""; } - let accountIdx = path[2] & 0x7fffffff; + const accountIdx = path[2] & 0x7fffffff; if (wholeAccount) { return { @@ -118,8 +118,8 @@ function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: co isPrefork: false, }; } else { - let change = path[3] == 1 ? "Change " : ""; - let addressIdx = path[4]; + const change = path[3] == 1 ? "Change " : ""; + const addressIdx = path[4]; return { verbose: `${coin} Account #${accountIdx}, ${change}Address #${addressIdx}${script}`, coin, @@ -186,6 +186,7 @@ export class LedgerHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInfo return eth.ethGetAccountPaths(msg); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { return false; } @@ -224,12 +225,12 @@ export class LedgerHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInfo } public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - let description = describeUTXOPath(msg.addressNList, msg.coin, msg.scriptType); + const description = describeUTXOPath(msg.addressNList, msg.coin, msg.scriptType); if (!description.isKnown) { return undefined; } - let addressNList = msg.addressNList; + const addressNList = msg.addressNList; if ( addressNList[0] === 0x80000000 + 44 || @@ -247,8 +248,8 @@ export class LedgerHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInfo } public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - let addressNList = msg.hardenedPath.concat(msg.relPath); - let description = describeETHPath(addressNList); + const addressNList = msg.hardenedPath.concat(msg.relPath); + const description = describeETHPath(addressNList); if (!description.isKnown) { return undefined; } @@ -291,7 +292,7 @@ export class LedgerHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa readonly _supportsBTC = true; readonly _supportsETH = true; - _isLedger: boolean = true; + _isLedger = true; transport: LedgerTransport; info: LedgerHDWalletInfo & core.HDWalletInfo; @@ -393,7 +394,7 @@ export class LedgerHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa payload: { name }, } = res; - const btcApps = new Set(btc.supportedCoins.map(x => coinToLedgerAppName(x)).filter(x => x !== undefined)) + const btcApps = new Set(btc.supportedCoins.map((x) => coinToLedgerAppName(x)).filter((x) => x !== undefined)); if (btcApps.has(name)) return btc.btcGetPublicKeys(this.transport, msg); switch (name) { @@ -404,6 +405,7 @@ export class LedgerHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa } } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { return false; } @@ -432,6 +434,7 @@ export class LedgerHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa return true; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async loadDevice(msg: core.LoadDevice): Promise { return; } @@ -445,26 +448,32 @@ export class LedgerHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa return; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async recover(msg: core.RecoverDevice): Promise { return; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async reset(msg: core.ResetDevice): Promise { return; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendCharacter(character: string): Promise { return; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendPassphrase(passphrase: string): Promise { return; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendPin(pin: string): Promise { return; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendWord(word: string): Promise { return; } diff --git a/packages/hdwallet-ledger/src/transport.ts b/packages/hdwallet-ledger/src/transport.ts index 61e9a7e86..80f0cb03a 100644 --- a/packages/hdwallet-ledger/src/transport.ts +++ b/packages/hdwallet-ledger/src/transport.ts @@ -1,10 +1,10 @@ -import * as core from "@shapeshiftoss/hdwallet-core"; -import type Transport from "@ledgerhq/hw-transport"; import type Btc from "@ledgerhq/hw-app-btc"; import type Eth from "@ledgerhq/hw-app-eth"; +import type Transport from "@ledgerhq/hw-transport"; import type getAppAndVersion from "@ledgerhq/live-common/lib/hw/getAppAndVersion"; import type getDeviceInfo from "@ledgerhq/live-common/lib/hw/getDeviceInfo"; import type openApp from "@ledgerhq/live-common/lib/hw/openApp"; +import * as core from "@shapeshiftoss/hdwallet-core"; type MethodsOnly = { [k in keyof T as T[k] extends (...args: any) => any ? k : never]: T[k]; @@ -47,12 +47,12 @@ type LedgerTransportMethodUnion = unknown extends T // Converts LedgerTransportMethodUnionInner to LedgerTransportMethod> type LedgerTransportMethodUnionInner = T extends LedgerTransportCoinType -? unknown extends U - ? LedgerTransportMethod> - : U extends LedgerTransportMethodName - ? LedgerTransportMethod - : never -: never; + ? unknown extends U + ? LedgerTransportMethod> + : U extends LedgerTransportMethodName + ? LedgerTransportMethod + : never + : never; export type LedgerTransportMethod = T extends LedgerTransportCoinType ? U extends LedgerTransportMethodName diff --git a/packages/hdwallet-ledger/src/utils.ts b/packages/hdwallet-ledger/src/utils.ts index c9d236dbb..a29c7f4e9 100644 --- a/packages/hdwallet-ledger/src/utils.ts +++ b/packages/hdwallet-ledger/src/utils.ts @@ -6,7 +6,11 @@ import _ from "lodash"; import { LedgerResponse } from "."; import { LedgerTransport } from "./transport"; -export function handleError>(result: T, transport?: LedgerTransport, message?: string): asserts result is T & {success: true} { +export function handleError>( + result: T, + transport?: LedgerTransport, + message?: string +): asserts result is T & { success: true } { if (result.success === true) return; if (result.payload && result.payload.error) { @@ -60,7 +64,7 @@ export function translateScriptType(scriptType: core.BTCInputScriptType): Addres [core.BTCInputScriptType.CashAddr]: "cashaddr", [core.BTCInputScriptType.SpendWitness]: "bech32", [core.BTCInputScriptType.SpendP2SHWitness]: "p2sh", - } + }; return core.mustBeDefined(scriptTypeMap[scriptType]); } @@ -69,55 +73,52 @@ export const compressPublicKey = (publicKey: Uint8Array) => { if (!(publicKey[0] === 0x04 && publicKey.length === 65)) throw new Error("Invalid public key format"); return core.compatibleBufferConcat([ - Buffer.from([((publicKey[64] & 0x01) === 0x00 ? 0x02 : 0x03)]), - publicKey.slice(1,33), + Buffer.from([(publicKey[64] & 0x01) === 0x00 ? 0x02 : 0x03]), + publicKey.slice(1, 33), ]); }; -export const createXpub = (depth: number, parentFp: number, childNum: number, chainCode: Uint8Array, publicKey: Uint8Array, network: number) => { +export const createXpub = ( + depth: number, + parentFp: number, + childNum: number, + chainCode: Uint8Array, + publicKey: Uint8Array, + network: number +) => { const header = new Uint8Array(4 + 1 + 4 + 4).buffer; const headerView = new DataView(header); headerView.setUint32(0, network); headerView.setUint8(4, depth); headerView.setUint32(5, parentFp); headerView.setUint32(9, childNum); - return bs58check.encode( - core.compatibleBufferConcat([ - new Uint8Array(header), - chainCode, - publicKey, - ]) - ); + return bs58check.encode(core.compatibleBufferConcat([new Uint8Array(header), chainCode, publicKey])); }; type NetworkMagic = { - apiName: string, - unit: string, - name: string, - appName?: string, - satoshi?: number, + apiName: string; + unit: string; + name: string; + appName?: string; + satoshi?: number; bitcoinjs: { - bech32?: string, + bech32?: string; bip32: { - private?: number, - public: Partial>, - }, - messagePrefix: string, - pubKeyHash?: number, - scriptHash?: number, - wif?: number, - }, - sigHash?: number, - isSegwitSupported?: boolean, - handleFeePerByte?: boolean, - additionals?: string[], - areTransactionTimestamped?: boolean, + private?: number; + public: Partial>; + }; + messagePrefix: string; + pubKeyHash?: number; + scriptHash?: number; + wif?: number; + }; + sigHash?: number; + isSegwitSupported?: boolean; + handleFeePerByte?: boolean; + additionals?: string[]; + areTransactionTimestamped?: boolean; }; -export function coinToLedgerAppName(coin: core.Coin): string | undefined { - return _.get(networksUtil[core.mustBeDefined(core.slip44ByCoin(coin))], "appName") -} - export const networksUtil: Record = { 0: { apiName: "btc", @@ -589,3 +590,7 @@ export const networksUtil: Record = { }, }, }; + +export function coinToLedgerAppName(coin: core.Coin): string | undefined { + return _.get(networksUtil[core.mustBeDefined(core.slip44ByCoin(coin))], "appName"); +} diff --git a/packages/hdwallet-metamask/src/adapter.ts b/packages/hdwallet-metamask/src/adapter.ts index 56696a8ac..9e9ea6461 100644 --- a/packages/hdwallet-metamask/src/adapter.ts +++ b/packages/hdwallet-metamask/src/adapter.ts @@ -1,7 +1,8 @@ +import detectEthereumProvider from "@metamask/detect-provider"; +import MetaMaskOnboarding from "@metamask/onboarding"; import * as core from "@shapeshiftoss/hdwallet-core"; + import { MetaMaskHDWallet } from "./metamask"; -import MetaMaskOnboarding from "@metamask/onboarding"; -import detectEthereumProvider from "@metamask/detect-provider"; export class MetaMaskAdapter { keyring: core.Keyring; diff --git a/packages/hdwallet-metamask/src/ethereum.ts b/packages/hdwallet-metamask/src/ethereum.ts index 23cec077e..a95f11e64 100644 --- a/packages/hdwallet-metamask/src/ethereum.ts +++ b/packages/hdwallet-metamask/src/ethereum.ts @@ -2,8 +2,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { ETHSignedMessage } from "@shapeshiftoss/hdwallet-core"; export function describeETHPath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Ethereum", isKnown: false, @@ -21,7 +21,7 @@ export function describeETHPath(path: core.BIP32Path): core.PathDescription { if (path[4] !== 0) return unknown; - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Ethereum Account #${index}`, accountIdx: index, @@ -31,6 +31,7 @@ export function describeETHPath(path: core.BIP32Path): core.PathDescription { }; } +// eslint-disable-next-line @typescript-eslint/no-unused-vars export async function ethVerifyMessage(msg: core.ETHVerifyMessage, ethereum: any): Promise { console.error("Method ethVerifyMessage unsupported for MetaMask wallet!"); return null; @@ -49,6 +50,7 @@ export function ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { console.error("Method ethSignTx unsupported for MetaMask wallet!"); return null; diff --git a/packages/hdwallet-metamask/src/metamask.ts b/packages/hdwallet-metamask/src/metamask.ts index 73440a9c3..71822c09f 100644 --- a/packages/hdwallet-metamask/src/metamask.ts +++ b/packages/hdwallet-metamask/src/metamask.ts @@ -1,13 +1,15 @@ +import detectEthereumProvider from "@metamask/detect-provider"; import * as core from "@shapeshiftoss/hdwallet-core"; -import * as eth from "./ethereum"; import _ from "lodash"; -import detectEthereumProvider from "@metamask/detect-provider"; + +import * as eth from "./ethereum"; class MetaMaskTransport extends core.Transport { public async getDeviceID() { return "metamask:0"; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public call(...args: any[]): Promise { return Promise.resolve(); } @@ -17,7 +19,87 @@ export function isMetaMask(wallet: core.HDWallet): wallet is MetaMaskHDWallet { return _.isObject(wallet) && (wallet as any)._isMetaMask; } -type HasNonTrivialConstructor = T extends { new (): any } ? never : T; +export class MetaMaskHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo { + readonly _supportsBTCInfo = false; + readonly _supportsETHInfo = true; + readonly _supportsCosmosInfo = false; + readonly _supportsBinanceInfo = false; + readonly _supportsRippleInfo = false; + readonly _supportsEosInfo = false; + readonly _supportsFioInfo = false; + readonly _supportsThorchainInfo = false; + readonly _supportsSecretInfo = false; + readonly _supportsKavaInfo = false; + readonly _supportsTerraInfo = false; + + public getVendor(): string { + return "MetaMask"; + } + + public hasOnDevicePinEntry(): boolean { + return false; + } + + public hasOnDevicePassphrase(): boolean { + return true; + } + + public hasOnDeviceDisplay(): boolean { + return true; + } + + public hasOnDeviceRecovery(): boolean { + return true; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { + return false; + } + + public supportsOfflineSigning(): boolean { + return false; + } + + public supportsBroadcast(): boolean { + return true; + } + + public describePath(msg: core.DescribePath): core.PathDescription { + switch (msg.coin) { + case "Ethereum": + return eth.describeETHPath(msg.path); + default: + throw new Error("Unsupported path"); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { + // TODO: What do we do here? + return undefined; + } + + public async ethSupportsNetwork(chainId = 1): Promise { + return chainId === 1; + } + + public async ethSupportsSecureTransfer(): Promise { + return false; + } + + public ethSupportsNativeShapeShift(): boolean { + return false; + } + + public async ethSupportsEIP1559(): Promise { + return false; + } + + public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { + return eth.ethGetAccountPaths(msg); + } +} export class MetaMaskHDWallet implements core.HDWallet, core.ETHWallet { readonly _supportsETH = true; @@ -124,21 +206,25 @@ export class MetaMaskHDWallet implements core.HDWallet, core.ETHWallet { return Promise.resolve({ msg: msg.msg }); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public sendPin(pin: string): Promise { // no concept of pin in MetaMask return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public sendPassphrase(passphrase: string): Promise { // cannot send passphrase to MetaMask. Could show the widget? return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public sendCharacter(charater: string): Promise { // no concept of sendCharacter in MetaMask return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public sendWord(word: string): Promise { // no concept of sendWord in MetaMask return Promise.resolve(); @@ -153,15 +239,18 @@ export class MetaMaskHDWallet implements core.HDWallet, core.ETHWallet { return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public reset(msg: core.ResetDevice): Promise { return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public recover(msg: core.RecoverDevice): Promise { // no concept of recover in MetaMask return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public loadDevice(msg: core.LoadDevice): Promise { // TODO: Does MetaMask allow this to be done programatically? return Promise.resolve(); @@ -171,6 +260,7 @@ export class MetaMaskHDWallet implements core.HDWallet, core.ETHWallet { return this.info.describePath(msg); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async getPublicKeys(msg: Array): Promise> { // Ethereum public keys are not exposed by the RPC API return []; @@ -184,7 +274,7 @@ export class MetaMaskHDWallet implements core.HDWallet, core.ETHWallet { return Promise.resolve(); } - public async ethSupportsNetwork(chainId: number = 1): Promise { + public async ethSupportsNetwork(chainId = 1): Promise { return chainId === 1; } @@ -208,6 +298,8 @@ export class MetaMaskHDWallet implements core.HDWallet, core.ETHWallet { return this.info.ethNextAccountPath(msg); } + // TODO: Respect msg.addressNList! + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async ethGetAddress(msg: core.ETHGetAddress): Promise { if (this.ethAddress) { return this.ethAddress; @@ -250,87 +342,6 @@ export class MetaMaskHDWallet implements core.HDWallet, core.ETHWallet { } } -export class MetaMaskHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo { - readonly _supportsBTCInfo = false; - readonly _supportsETHInfo = true; - readonly _supportsCosmosInfo = false; - readonly _supportsBinanceInfo = false; - readonly _supportsRippleInfo = false; - readonly _supportsEosInfo = false; - readonly _supportsFioInfo = false; - readonly _supportsThorchainInfo = false; - readonly _supportsSecretInfo = false; - readonly _supportsKavaInfo = false; - readonly _supportsTerraInfo = false; - - public getVendor(): string { - return "MetaMask"; - } - - public hasOnDevicePinEntry(): boolean { - return false; - } - - public hasOnDevicePassphrase(): boolean { - return true; - } - - public hasOnDeviceDisplay(): boolean { - return true; - } - - public hasOnDeviceRecovery(): boolean { - return true; - } - - public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { - // It doesn't... yet? - return false; - } - - public supportsOfflineSigning(): boolean { - return false; - } - - public supportsBroadcast(): boolean { - return true; - } - - public describePath(msg: core.DescribePath): core.PathDescription { - switch (msg.coin) { - case "Ethereum": - return eth.describeETHPath(msg.path); - default: - throw new Error("Unsupported path"); - } - } - - public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - // TODO: What do we do here? - return undefined; - } - - public async ethSupportsNetwork(chainId: number = 1): Promise { - return chainId === 1; - } - - public async ethSupportsSecureTransfer(): Promise { - return false; - } - - public ethSupportsNativeShapeShift(): boolean { - return false; - } - - public async ethSupportsEIP1559(): Promise { - return false; - } - - public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { - return eth.ethGetAccountPaths(msg); - } -} - export function info() { return new MetaMaskHDWalletInfo(); } diff --git a/packages/hdwallet-native-vault/src/argonBenchmark.test.ts b/packages/hdwallet-native-vault/src/argonBenchmark.test.ts index e471b1bd2..1a4470de7 100644 --- a/packages/hdwallet-native-vault/src/argonBenchmark.test.ts +++ b/packages/hdwallet-native-vault/src/argonBenchmark.test.ts @@ -1,10 +1,10 @@ -import { argonBenchmark, customNow } from "./argonBenchmark" -import { deterministicGetRandomValues } from "./deterministicGetRandomValues.test" -import { setPerformance, setCrypto } from "./util" +import { argonBenchmark, customNow } from "./argonBenchmark"; +import { deterministicGetRandomValues } from "./deterministicGetRandomValues.test"; +import { setCrypto, setPerformance } from "./util"; -const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); -const performance = require("perf_hooks").performance +const performance = require("perf_hooks").performance; jest.setTimeout(30 * 1000); @@ -15,20 +15,20 @@ describe("argonBenchmark", () => { subtle: realCrypto.subtle, getRandomValues: await deterministicGetRandomValues(realCrypto), }); - setPerformance(performance) + setPerformance(performance); }); describe("customNow", () => { it("should work", async () => { - const now = customNow(performance.now.bind(performance), 10, 0.05) - const start = now() - await delay(100) - const duration = now() - start - expect(duration).toBeGreaterThan(50) + const now = customNow(performance.now.bind(performance), 10, 0.05); + const start = now(); + await delay(100); + const duration = now() - start; + expect(duration).toBeGreaterThan(50); // This is performance-dependent // expect(duration).toBeLessThan(200) - }) - }) + }); + }); describe("argonBenchmark", () => { it("should run correctly", async () => { @@ -36,21 +36,21 @@ describe("argonBenchmark", () => { measureError: true, // jitter: 0.10, // roundMs: 100, - now: performance.now.bind(performance) - }) - console.log(results) - expect(results).toBeTruthy() - expect(Number.isSafeInteger(results.iterations)).toBeTruthy() - expect(results.iterations).toBeGreaterThanOrEqual(1) - expect(results.error).toBeGreaterThanOrEqual(0) - expect(results.bits).toBeGreaterThanOrEqual(0) + now: performance.now.bind(performance), + }); + console.info(results); + expect(results).toBeTruthy(); + expect(Number.isSafeInteger(results.iterations)).toBeTruthy(); + expect(results.iterations).toBeGreaterThanOrEqual(1); + expect(results.error).toBeGreaterThanOrEqual(0); + expect(results.bits).toBeGreaterThanOrEqual(0); // These are performance-dependent // expect(results.bits).toBeGreaterThan(5) // expect(results.bits).toBeLessThan(15) - expect(results.durations.overall).toBeGreaterThanOrEqual(0) - expect(results.durations.setup).toBeGreaterThanOrEqual(0) - expect(results.durations.warmup).toBeGreaterThanOrEqual(0) - expect(results.durations.msPerIteration).toBeGreaterThan(0) - }) - }) -}) + expect(results.durations.overall).toBeGreaterThanOrEqual(0); + expect(results.durations.setup).toBeGreaterThanOrEqual(0); + expect(results.durations.warmup).toBeGreaterThanOrEqual(0); + expect(results.durations.msPerIteration).toBeGreaterThan(0); + }); + }); +}); diff --git a/packages/hdwallet-native-vault/src/argonBenchmark.ts b/packages/hdwallet-native-vault/src/argonBenchmark.ts index 78cde8021..bb5492565 100644 --- a/packages/hdwallet-native-vault/src/argonBenchmark.ts +++ b/packages/hdwallet-native-vault/src/argonBenchmark.ts @@ -1,11 +1,11 @@ -import { argon2id } from "hash-wasm" +import { argon2id } from "hash-wasm"; -import { crypto, performance } from "./util" +import { crypto, performance } from "./util"; async function argonBenchInner(memorySize: number, iterations: number, now: () => number) { - const password = await (await crypto).getRandomValues(new Uint8Array(32)) - const salt = await (await crypto).getRandomValues(new Uint8Array(32)) - const start = now() + const password = await (await crypto).getRandomValues(new Uint8Array(32)); + const salt = await (await crypto).getRandomValues(new Uint8Array(32)); + const start = now(); await argon2id({ password, salt, @@ -14,36 +14,40 @@ async function argonBenchInner(memorySize: number, iterations: number, now: () = iterations, hashLength: 32, outputType: "binary", - }) - return now() - start + }); + return now() - start; } export function customNow(now: () => number, roundMs: number, jitter: number): () => number { if (jitter) { - let lastRealNow = 0 - let lastNow = 0 - now = ((thisNow) => (() => { - const realNow = thisNow() - const nextNow = lastRealNow === 0 ? realNow : (() => { - const elapsed = (realNow - lastRealNow) * (1 + ((Math.random() - 0.5) * jitter)) - return lastRealNow + elapsed - })() - lastRealNow = realNow - lastNow = nextNow - return nextNow - }))(now) + let lastRealNow = 0; + now = ((thisNow) => () => { + const realNow = thisNow(); + const nextNow = + lastRealNow === 0 + ? realNow + : (() => { + const elapsed = (realNow - lastRealNow) * (1 + (Math.random() - 0.5) * jitter); + return lastRealNow + elapsed; + })(); + lastRealNow = realNow; + return nextNow; + })(now); } if (roundMs) { - now = ((thisNow) => (() => Math.round(thisNow() / roundMs) * roundMs))(now) + now = ( + (thisNow) => () => + Math.round(thisNow() / roundMs) * roundMs + )(now); } - return now + return now; } /** * This function benchmarks the current machine to determine how many iterations of single-theaded argon2id can be afforded in a given time budget. * The time budget is considered to include only the time spent actually iterating; setup time is proportional to the memory allocation and excluded, * though it usually only is a few tens of milliseconds extra. - * + * * Benchmarks in Node and the browser show that the error in the final value is typically less than 5%, with * @param memorySizeKib The amount of memory used by argon2id; must be at least 8. A higher value provides more defense against ASIC and GPU attacks * @param targetTimeMs Desired duration of the key derivation operation @@ -53,21 +57,25 @@ export function customNow(now: () => number, roundMs: number, jitter: number): ( * @param options.now Overrides the timestamp function used to measure durations. Must return a monotonically-increasing number of milliseconds. * @param options.roundMs Rounds timestamps to simulate results when a browser rounds performance.now() to prevent fingerprinting. * @param options.jitter Introduces random timestamp errors of the specified magnitude. Range 0-1, default 0. - * @returns + * @returns */ -export async function argonBenchmark(memorySizeKib: number, targetTimeMs: number, options: Partial<{ - fineTuning: number - measureError: boolean - now: () => number - roundMs: number - jitter: number -}> = {}) { - const preciseNow = options.now ?? (await performance).now.bind(await performance) +export async function argonBenchmark( + memorySizeKib: number, + targetTimeMs: number, + options: Partial<{ + fineTuning: number; + measureError: boolean; + now: () => number; + roundMs: number; + jitter: number; + }> = {} +) { + const preciseNow = options.now ?? (await performance).now.bind(await performance); const overallStart = preciseNow(); - let fineTuning = options.fineTuning ?? 0 - const measureError = options.measureError ?? false - const roundedNow = customNow(preciseNow, options.roundMs ?? 0, options.jitter ?? 0) + let fineTuning = options.fineTuning ?? 0; + const measureError = options.measureError ?? false; + const roundedNow = customNow(preciseNow, options.roundMs ?? 0, options.jitter ?? 0); // warm-up wasm module with the minimum-possible parameter values to ensure the module is actually loaded; should take // no more than 100ms in the absolute worst case @@ -75,15 +83,15 @@ export async function argonBenchmark(memorySizeKib: number, targetTimeMs: number // Calculate the minimum time a single iteration of the shortest kind takes; this is later used to calculate the bits // of added security. The low memory use ensures that the setup time is negligible. - const minMsPerIter = await (async ()=>{ + const minMsPerIter = await (async () => { let minDuration = 0; - let minMsPerIter = 0; + let out = 0; for (let i = 1; minDuration === 0; i *= 2) { - minDuration = await argonBenchInner(8, 1, roundedNow) - minMsPerIter = minDuration / i + minDuration = await argonBenchInner(8, 1, roundedNow); + out = minDuration / i; } - return minMsPerIter - })() + return out; + })(); let firstDuration = 0; let firstI = 0; @@ -91,53 +99,53 @@ export async function argonBenchmark(memorySizeKib: number, targetTimeMs: number let msPerIteration = 0; let setupDuration = 0; let i = 0.5; // after the initial i *= 2, this will always be an integer - while (duration < (targetTimeMs + setupDuration)) { + while (duration < targetTimeMs + setupDuration) { if (msPerIteration === 0) { // This ensures that a non-increasing performance.now() implementation won't loop infinitely - if (i * memorySizeKib > 2 ** 20) break + if (i * memorySizeKib > 2 ** 20) break; // This happen on the first loop, but also can happen with rounded timestamps - i *= 2 + i *= 2; } else { - const iStep = Math.round(((targetTimeMs + setupDuration) - duration) / msPerIteration) + const iStep = Math.round((targetTimeMs + setupDuration - duration) / msPerIteration); // We don't need fine-tuning if we're dead on - if (iStep == 0) break + if (iStep == 0) break; // Ensuring each step is at least i large sacrifices accuracy, but limits the benchmark time to no more than 4 // times the target duration. Additional fine-tuning steps can increase accuracy, but each one adds a possible // 2 * targetTimeMs to the worst-case. if (Math.abs(iStep) < 0) { - if (fineTuning <= 0) break - fineTuning-- + if (fineTuning <= 0) break; + fineTuning--; } - i = i + iStep + i = i + iStep; } - duration = await argonBenchInner(memorySizeKib, i, roundedNow) - msPerIteration = (duration - setupDuration) / i + duration = await argonBenchInner(memorySizeKib, i, roundedNow); + msPerIteration = (duration - setupDuration) / i; // We want the the duration of the first *measurable* run (i.e. duration > 0). (Duration can be 0 if timestamps are // rounded.) if (!firstDuration) { - firstDuration = duration - firstI = i + firstDuration = duration; + firstI = i; } - // As we test higher iteration counts, we get a more accurate msPerIteration calculation, leading to - setupDuration = Math.max(0, firstDuration - (msPerIteration * firstI)) + // As we test higher iteration counts, we get a more accurate msPerIteration calculation, leading to + setupDuration = Math.max(0, firstDuration - msPerIteration * firstI); } - if (msPerIteration === 0) throw new Error("benchmark runs look instantaneous -- is performance.now() working?") + if (msPerIteration === 0) throw new Error("benchmark runs look instantaneous -- is performance.now() working?"); // Allow no more than i/2 to be shaved off the final time; this limits the damage a significantly outlying final - // benchmark run can do - i -= Math.min((duration - (targetTimeMs + setupDuration)) / msPerIteration, i / 2) - i = Math.ceil(i) + // benchmark run can do + i -= Math.min((duration - (targetTimeMs + setupDuration)) / msPerIteration, i / 2); + i = Math.ceil(i); - let error: number | undefined = undefined + let error: number | undefined = undefined; if (measureError) { // bench with recommended iterations to determine precise error; this will obviously take extra time - const finalDuration = await argonBenchInner(memorySizeKib, i, preciseNow) - error = Math.abs(finalDuration - (targetTimeMs + setupDuration)) / (targetTimeMs + setupDuration) + const finalDuration = await argonBenchInner(memorySizeKib, i, preciseNow); + error = Math.abs(finalDuration - (targetTimeMs + setupDuration)) / (targetTimeMs + setupDuration); } - const bits = Math.log2(((i * msPerIteration) - setupDuration) / minMsPerIter) - const overallDuration = preciseNow() - overallStart + const bits = Math.log2((i * msPerIteration - setupDuration) / minMsPerIter); + const overallDuration = preciseNow() - overallStart; return { iterations: i, bits, @@ -151,10 +159,10 @@ export async function argonBenchmark(memorySizeKib: number, targetTimeMs: number options: { ...{ ...options, - now: undefined + now: undefined, }, memorySizeKib, targetTimeMs, - } + }, }; } diff --git a/packages/hdwallet-native-vault/src/deterministicGetRandomValues.test.ts b/packages/hdwallet-native-vault/src/deterministicGetRandomValues.test.ts index c04b00842..9f3e15bcb 100644 --- a/packages/hdwallet-native-vault/src/deterministicGetRandomValues.test.ts +++ b/packages/hdwallet-native-vault/src/deterministicGetRandomValues.test.ts @@ -1,5 +1,7 @@ import { AsyncCrypto } from "./types"; +// (This is also used in index.test.ts) +// eslint-disable-next-line jest/no-export export async function deterministicGetRandomValues(crypto: AsyncCrypto, seed: Uint8Array = new Uint8Array(32)) { const rngSeed = await crypto.subtle.importKey( "raw", diff --git a/packages/hdwallet-native-vault/src/index.test.ts b/packages/hdwallet-native-vault/src/index.test.ts index fbfeb526c..817bbb9b4 100644 --- a/packages/hdwallet-native-vault/src/index.test.ts +++ b/packages/hdwallet-native-vault/src/index.test.ts @@ -3,10 +3,10 @@ import * as idb from "idb-keyval"; // import * as jose from "jose"; import * as uuid from "uuid"; -import { Vault, GENERATE_MNEMONIC } from "."; +import { GENERATE_MNEMONIC, Vault } from "."; import { deterministicGetRandomValues } from "./deterministicGetRandomValues.test"; -import { MockVault } from './test/mockVault.skip'; -import { RawVault } from './rawVault'; +import { RawVault } from "./rawVault"; +import { MockVault } from "./test/mockVault.skip"; import { ISealableVaultFactory, IVault } from "./types"; import { keyStoreUUID, vaultStoreUUID } from "./util"; @@ -22,7 +22,10 @@ async function resetGetRandomValues() { } type ParametersExceptFirst = F extends (arg0: any, ...rest: infer R) => any ? R : never; -async function thereCanBeOnlyOne>(factory: U, ...args: ParametersExceptFirst): Promise { +async function thereCanBeOnlyOne>( + factory: U, + ...args: ParametersExceptFirst +): Promise { const ids = await factory.list(); if (ids.length === 0) throw new Error("can't find a vault"); if (ids.length > 1) throw new Error(`expected a single vault; found ${ids.length}: ${ids}`); @@ -30,7 +33,7 @@ async function thereCanBeOnlyOne void; -const preparedOnce = new Promise(resolve => prepareOnce = resolve).then(async () => { +const preparedOnce = new Promise((resolve) => (prepareOnce = resolve)).then(async () => { await resetGetRandomValues(); await RawVault.prepare({ crypto: { @@ -40,17 +43,19 @@ const preparedOnce = new Promise(resolve => prepareOnce = resolve).then(as }, }, performance: require("perf_hooks").performance, - }) - await RawVault.defaultArgonParams -}) + }); + await RawVault.defaultArgonParams; +}); +// eslint-disable-next-line @typescript-eslint/no-shadow function testVaultImpl(name: string, Vault: ISealableVaultFactory) { + // eslint-disable-next-line jest/valid-title describe(name, () => { beforeAll(async () => { prepareOnce(); await preparedOnce; await Vault.prepare(); - for (const id of await Vault.list()) await Vault.delete(id) + for (const id of await Vault.list()) await Vault.delete(id); }); beforeEach(async () => { @@ -75,16 +80,16 @@ function testVaultImpl(name: string, Vault: ISealableVaultFactory) { expect((await Vault.list()).length).toBe(1); expect(vault.meta.get("name")).toBe("default"); - console.log("keyStore", await idb.entries(keyStore)); - console.log("vaultStore", await idb.entries(vaultStore)); + console.debug("keyStore", await idb.entries(keyStore)); + console.debug("vaultStore", await idb.entries(vaultStore)); }); it("should open a vault", async () => { const vaultIDs = await Vault.list(); expect(vaultIDs.length).toBe(1); const vault = await Vault.open(vaultIDs[0], "foobar"); - // console.log(jose.decodeProtectedHeader((await idb.get(vaultIDs[0], vaultStore))!)); - // console.log("entries", vault.entries()); + // console.debug(jose.decodeProtectedHeader((await idb.get(vaultIDs[0], vaultStore))!)); + // console.debug("entries", vault.entries()); expect(await vault.get("foo")).toBe("bar"); expect(uuid.validate(vault.id)).toBe(true); expect(vault.meta.size).toBe(1); @@ -98,13 +103,14 @@ function testVaultImpl(name: string, Vault: ISealableVaultFactory) { const mnemonic = (await vault.get("#mnemonic")) as native.crypto.Isolation.Engines.Default.BIP39.Mnemonic; expect(mnemonic).toBeInstanceOf(native.crypto.Isolation.Engines.Default.BIP39.Mnemonic); expect( - await mnemonic.toSeed() - .then(x => x.toMasterKey()) - .then(x => x.getPublicKey()) - .then(x => Buffer.from(x).toString("hex")) + await mnemonic + .toSeed() + .then((x) => x.toMasterKey()) + .then((x) => x.getPublicKey()) + .then((x) => Buffer.from(x).toString("hex")) ).toMatchInlineSnapshot(`"03e3b30e8c21923752a408242e069941fedbaef7db7161f7e2c5f3fdafe7e25ddc"`); }); - + it("should retrieve the mnemonic", async () => { const vaultIDs = await Vault.list(); expect(vaultIDs.length).toBe(1); @@ -112,29 +118,30 @@ function testVaultImpl(name: string, Vault: ISealableVaultFactory) { const mnemonic = (await vault.get("#mnemonic")) as native.crypto.Isolation.Engines.Default.BIP39.Mnemonic; expect(mnemonic).toBeInstanceOf(native.crypto.Isolation.Engines.Default.BIP39.Mnemonic); expect( - await mnemonic.toSeed() - .then(x => x.toMasterKey()) - .then(x => x.getPublicKey()) - .then(x => Buffer.from(x).toString("hex")) + await mnemonic + .toSeed() + .then((x) => x.toMasterKey()) + .then((x) => x.getPublicKey()) + .then((x) => Buffer.from(x).toString("hex")) ).toMatchInlineSnapshot(`"03e3b30e8c21923752a408242e069941fedbaef7db7161f7e2c5f3fdafe7e25ddc"`); }); it("should store metadata", async () => { const vault = await thereCanBeOnlyOne(Vault, "foobar"); - vault.meta.set("bar", "baz") - expect(vault.meta.get("bar")).toBe("baz") - await vault.save() - }) + vault.meta.set("bar", "baz"); + expect(vault.meta.get("bar")).toBe("baz"); + await vault.save(); + }); it("should retreive metadata from the vault instance", async () => { const vault = await thereCanBeOnlyOne(Vault, "foobar"); - expect(vault.meta.get("bar")).toBe("baz") - }) + expect(vault.meta.get("bar")).toBe("baz"); + }); it("should retreive metadata with the static method", async () => { - const id = (await thereCanBeOnlyOne(Vault)).id - expect((await Vault.meta(id))?.get("bar")).toBe("baz") - }) + const id = (await thereCanBeOnlyOne(Vault)).id; + expect((await Vault.meta(id))?.get("bar")).toBe("baz"); + }); describe("ISealable", () => { it("should be unwrappable before being sealed", async () => { @@ -177,10 +184,11 @@ function testVaultImpl(name: string, Vault: ISealableVaultFactory) { const mnemonic = (await vault.get("#mnemonic")) as native.crypto.Isolation.Engines.Default.BIP39.Mnemonic; expect(mnemonic).toBeInstanceOf(native.crypto.Isolation.Engines.Default.BIP39.Mnemonic); expect( - await mnemonic.toSeed() - .then(x => x.toMasterKey()) - .then(x => x.getPublicKey()) - .then(x => Buffer.from(x).toString("hex")) + await mnemonic + .toSeed() + .then((x) => x.toMasterKey()) + .then((x) => x.getPublicKey()) + .then((x) => Buffer.from(x).toString("hex")) ).toMatchInlineSnapshot(`"02576bde4c55b05886e56eeeeff304006352f935b6dfc1c409f7eae521dbc5558e"`); const unwrappedMnemonic = (await vault.unwrap().get("#mnemonic")) as string; @@ -195,10 +203,11 @@ function testVaultImpl(name: string, Vault: ISealableVaultFactory) { const mnemonic = (await vault.get("#mnemonic")) as native.crypto.Isolation.Engines.Default.BIP39.Mnemonic; expect(mnemonic).toBeInstanceOf(native.crypto.Isolation.Engines.Default.BIP39.Mnemonic); expect( - await mnemonic.toSeed() - .then(x => x.toMasterKey()) - .then(x => x.getPublicKey()) - .then(x => Buffer.from(x).toString("hex")) + await mnemonic + .toSeed() + .then((x) => x.toMasterKey()) + .then((x) => x.getPublicKey()) + .then((x) => Buffer.from(x).toString("hex")) ).toMatchInlineSnapshot(`"02576bde4c55b05886e56eeeeff304006352f935b6dfc1c409f7eae521dbc5558e"`); expect(await vault.unwrap().get("#mnemonic")).toMatchInlineSnapshot( @@ -208,5 +217,5 @@ function testVaultImpl(name: string, Vault: ISealableVaultFactory) { }); } -testVaultImpl("Vault", Vault) -testVaultImpl("MockVault", MockVault) +testVaultImpl("Vault", Vault); +testVaultImpl("MockVault", MockVault); diff --git a/packages/hdwallet-native-vault/src/index.ts b/packages/hdwallet-native-vault/src/index.ts index c2313bcc9..1f150a6ac 100644 --- a/packages/hdwallet-native-vault/src/index.ts +++ b/packages/hdwallet-native-vault/src/index.ts @@ -1,16 +1,16 @@ +import { createMnemonic, crypto, entropyToMnemonic, GENERATE_MNEMONIC } from "./util"; import { Vault } from "./vault"; -import { GENERATE_MNEMONIC, crypto, createMnemonic, entropyToMnemonic } from "./util"; -export { argonBenchmark } from "./argonBenchmark" -export type { ISealableVaultFactory, IVault, IVaultFactory } from './types' -export { GENERATE_MNEMONIC } from './util' -export { Vault } from "./vault" +export { argonBenchmark } from "./argonBenchmark"; +export type { ISealableVaultFactory, IVault, IVaultFactory } from "./types"; +export { GENERATE_MNEMONIC } from "./util"; +export { Vault } from "./vault"; Vault.registerValueTransformer("#mnemonic", async (x: unknown) => { - if (x !== GENERATE_MNEMONIC) return x - const entropy = await (await crypto).getRandomValues(Buffer.alloc(16)) - return entropyToMnemonic(entropy) -}) + if (x !== GENERATE_MNEMONIC) return x; + const entropy = await (await crypto).getRandomValues(Buffer.alloc(16)); + return entropyToMnemonic(entropy); +}); Vault.registerValueWrapper("#mnemonic", async (x: unknown, addRevoker: (revoke: () => void) => void) => { if (typeof x !== "string") throw new TypeError("#mnemonic must be a string"); const out = await createMnemonic(x); diff --git a/packages/hdwallet-native-vault/src/mapVault.ts b/packages/hdwallet-native-vault/src/mapVault.ts index 1156cd609..db5cc41e8 100644 --- a/packages/hdwallet-native-vault/src/mapVault.ts +++ b/packages/hdwallet-native-vault/src/mapVault.ts @@ -1,8 +1,8 @@ import * as ta from "type-assertions"; -import { RawVault } from "./rawVault" -import { IVaultBackedBy, IVaultFactory, VaultPrepareParams } from "./types" -import { Revocable, encoder, decoder } from "./util" +import { RawVault } from "./rawVault"; +import { IVaultBackedBy, IVaultFactory, VaultPrepareParams } from "./types"; +import { decoder, encoder, Revocable } from "./util"; ta.assert>>(); @@ -10,7 +10,9 @@ export class MapVault extends Revocable(Map) implements Map>, IVaultBackedBy]>> { - static async prepare(params?: VaultPrepareParams) { return RawVault.prepare(params); } + static async prepare(params?: VaultPrepareParams) { + return RawVault.prepare(params); + } static async create(password?: string) { return await MapVault.open(undefined, password); } @@ -18,9 +20,15 @@ export class MapVault await MapVault.prepare(); return new MapVault(await RawVault.open(id, password)); } - static list() { return RawVault.list(); } - static meta(id: string) { return RawVault.meta(id); } - static delete(id: string) { return RawVault.delete(id); } + static list() { + return RawVault.list(); + } + static meta(id: string) { + return RawVault.meta(id); + } + static delete(id: string) { + return RawVault.delete(id); + } readonly #rawVault: RawVault; @@ -40,7 +48,7 @@ export class MapVault async setPassword(password: string): Promise { await this.#rawVault.setPassword(password); - return this + return this; } async load(deserializer: (_: Array<[string, unknown | Promise]>) => Promise) { @@ -68,7 +76,9 @@ export class MapVault } async entriesAsync(): Promise> { - return await Promise.all(Array.from(this.entries()).map(async ([k, v]): Promise<[typeof k, unknown]> => [k, await v])) + return await Promise.all( + Array.from(this.entries()).map(async ([k, v]): Promise<[typeof k, unknown]> => [k, await v]) + ); } } diff --git a/packages/hdwallet-native-vault/src/rawVault.ts b/packages/hdwallet-native-vault/src/rawVault.ts index a2cd8fd47..550b5ed55 100644 --- a/packages/hdwallet-native-vault/src/rawVault.ts +++ b/packages/hdwallet-native-vault/src/rawVault.ts @@ -2,12 +2,12 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { argon2id } from "hash-wasm"; import * as idb from "idb-keyval"; import * as jose from "jose"; -import * as uuid from "uuid"; import * as ta from "type-assertions"; -import { argonBenchmark } from "./argonBenchmark"; +import * as uuid from "uuid"; +import { argonBenchmark } from "./argonBenchmark"; import { ArgonParams, IVaultBackedBy, IVaultFactory, VaultPrepareParams } from "./types"; -import { Revocable, crypto, revocable, encoder, keyStoreUUID, vaultStoreUUID, setCrypto, setPerformance } from "./util"; +import { crypto, encoder, keyStoreUUID, Revocable, revocable, setCrypto, setPerformance, vaultStoreUUID } from "./util"; // This has to be outside the class so the static initializers for defaultArgonParams and #machineSeed can reference it. let resolvers: @@ -75,7 +75,7 @@ export class RawVault extends Revocable(Object.freeze(class {})) implements IVau iterations: 26, }; const argonBenchmarkResults = await argonBenchmark(out.memorySize, 1000, { measureError: true }); - console.log("argonBenchmarkResults:", argonBenchmarkResults); + console.debug("argonBenchmarkResults:", argonBenchmarkResults); await idb.set("argonBenchmarkResults", argonBenchmarkResults, await RawVault.#keyStore); out.iterations = argonBenchmarkResults.iterations; await idb.set("defaultArgonParams", out, await RawVault.#keyStore); @@ -92,6 +92,7 @@ export class RawVault extends Revocable(Object.freeze(class {})) implements IVau id: string, argonParams: ArgonParams, password: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars addRevoker: (revoke: () => void) => void ) { const idBuf = encoder.encode(id); @@ -154,6 +155,7 @@ export class RawVault extends Revocable(Object.freeze(class {})) implements IVau static async open(id?: string, password?: string) { await RawVault.prepare(); + // eslint-disable-next-line @typescript-eslint/no-shadow const factory = async (id: string, argonParams: Promise) => { const vaultRevoker = new (Revocable(class {}))(); const vault = revocable(new RawVault(id, argonParams), (x) => vaultRevoker.addRevoker(x)); @@ -217,12 +219,16 @@ export class RawVault extends Revocable(Object.freeze(class {})) implements IVau protected constructor(id: string, argonParams: Promise) { super(); this.id = id; - this.#argonParams = argonParams.then(x => Object.freeze(JSON.parse(JSON.stringify(x)))); + this.#argonParams = argonParams.then((x) => Object.freeze(JSON.parse(JSON.stringify(x)))); } async setPassword(password: string): Promise { - this.#key = await RawVault.#deriveVaultKey(await RawVault.#machineSeed, this.id, await this.#argonParams, password, (x) => - this.addRevoker(x) + this.#key = await RawVault.#deriveVaultKey( + await RawVault.#machineSeed, + this.id, + await this.#argonParams, + password, + (x) => this.addRevoker(x) ); return this; } diff --git a/packages/hdwallet-native-vault/src/test/mockVault.skip.ts b/packages/hdwallet-native-vault/src/test/mockVault.skip.ts index 848ffc144..1f9866d0a 100644 --- a/packages/hdwallet-native-vault/src/test/mockVault.skip.ts +++ b/packages/hdwallet-native-vault/src/test/mockVault.skip.ts @@ -5,124 +5,141 @@ import * as bip39 from "bip39"; import * as ta from "type-assertions"; import * as uuid from "uuid"; -import { ISealableVaultFactory, IVault, VaultPrepareParams } from "../types" -import { GENERATE_MNEMONIC, Revocable, crypto, setCrypto, shadowedMap } from "../util" +import { ISealableVaultFactory, IVault, VaultPrepareParams } from "../types"; +import { crypto, GENERATE_MNEMONIC, Revocable, setCrypto, shadowedMap } from "../util"; ta.assert>>(); type MockVaultData = { - password: string, - data: Array<[string, unknown]>, - meta: Array<[string, unknown]> -} + password: string; + data: Array<[string, unknown]>; + meta: Array<[string, unknown]>; +}; -export class MockVault - extends Revocable(Map) - implements IVault, Map> -{ - static readonly data = new Map() +export class MockVault extends Revocable(Map) implements IVault, Map> { + static readonly data = new Map(); static prepared = false; static async prepare(params?: VaultPrepareParams) { if (params) { - if (this.prepared) throw new Error("can't call prepare with a parameters object after vault is already prepared") - setCrypto(params.crypto ?? window.crypto) + if (this.prepared) throw new Error("can't call prepare with a parameters object after vault is already prepared"); + setCrypto(params.crypto ?? window.crypto); } - this.prepared = true + this.prepared = true; } static async create(password?: string, sealed?: boolean): Promise { return await MockVault.open(undefined, password, sealed); } static async open(id?: string, password?: string, sealed?: boolean): Promise { - await MockVault.prepare() + await MockVault.prepare(); id ??= uuid.v4({ random: await (await crypto).getRandomValues(new Uint8Array(16)), - }) - const out = new MockVault(id, password, sealed) + }); + const out = new MockVault(id, password, sealed); if (id !== undefined && password !== undefined) await out.load(); - return out + return out; + } + static async list() { + return Array.from(MockVault.data.keys()); + } + static async meta(id: string) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return MockVault.data.has(id) ? new Map(MockVault.data.get(id)!.meta) : undefined; + } + static async delete(id: string) { + MockVault.data.delete(id); } - static async list() { return Array.from(MockVault.data.keys()) } - static async meta(id: string) { return MockVault.data.has(id) ? new Map(MockVault.data.get(id)!.meta) : undefined; } - static async delete(id: string) { MockVault.data.delete(id); } - readonly id: string - readonly meta = new Map() + readonly id: string; + readonly meta = new Map(); - _password: string | undefined = undefined - readonly _secrets = new Map() + _password: string | undefined = undefined; + readonly _secrets = new Map(); - protected constructor(id: string, password?: string, sealed: boolean = true) { - super() - this.id = id - this._password = password - if (sealed) this.seal() + protected constructor(id: string, password?: string, sealed = true) { + super(); + this.id = id; + this._password = password; + if (sealed) this.seal(); } async setPassword(password: string): Promise { - this._password = password - return this + this._password = password; + return this; } async load() { - const data = MockVault.data.get(this.id) - if (!data) throw new Error('no such vault') - if (this._password !== data.password) throw new Error('bad password') - this.clear() - this._secrets.clear() - data.data.forEach(([k, v]) => this.set(k, v)) - this.meta.clear() - data.meta.forEach(([k, v]) => this.meta.set(k, v)) + const data = MockVault.data.get(this.id); + if (!data) throw new Error("no such vault"); + if (this._password !== data.password) throw new Error("bad password"); + this.clear(); + this._secrets.clear(); + data.data.forEach(([k, v]) => this.set(k, v)); + this.meta.clear(); + data.meta.forEach(([k, v]) => this.meta.set(k, v)); return this; } async save() { - if (!this._password) throw new Error("can't save without password") + if (!this._password) throw new Error("can't save without password"); const data = { password: this._password, data: await this._unwrap().entriesAsync(), - meta: Array.from(this.meta.entries()) - } - MockVault.data.set(this.id, data) + meta: Array.from(this.meta.entries()), + }; + MockVault.data.set(this.id, data); return this; } async entriesAsync(): Promise> { - return await Promise.all(Array.from(this.entries()).map(async ([k, v]: [string, Promise]) => [k, await v] as [string, unknown])) + return await Promise.all( + Array.from(this.entries()).map(async ([k, v]: [string, Promise]) => [k, await v] as [string, unknown]) + ); } set(key: string, value: unknown | Promise): this { - if (!key.startsWith('#')) { - super.set(key, Promise.resolve(value)) - return this + if (!key.startsWith("#")) { + super.set(key, Promise.resolve(value)); + return this; } - if (key === '#mnemonic') { + if (key === "#mnemonic") { value = Promise.resolve(value).then(async (x) => { - if (x !== GENERATE_MNEMONIC) return x - const entropy = await (await crypto).getRandomValues(Buffer.alloc(16)) - return bip39.entropyToMnemonic(entropy) - }) + if (x !== GENERATE_MNEMONIC) return x; + const entropy = await (await crypto).getRandomValues(Buffer.alloc(16)); + return bip39.entropyToMnemonic(entropy); + }); } - this._secrets.set(key, Promise.resolve(value)) - super.set(key, (async () => { - switch (key) { - case '#mnemonic': - return await native.crypto.Isolation.Engines.Default.BIP39.Mnemonic.create(await value as string) - default: return core.untouchable(key) - } - })()) - return this + this._secrets.set(key, Promise.resolve(value)); + super.set( + key, + (async () => { + switch (key) { + case "#mnemonic": + return await native.crypto.Isolation.Engines.Default.BIP39.Mnemonic.create((await value) as string); + default: + return core.untouchable(key); + } + })() + ); + return this; } - sealed = false - seal() { this.sealed = true } + sealed = false; + seal() { + this.sealed = true; + } unwrap() { - if (this.sealed) throw new Error("can't unwrap a sealed vault") - return this._unwrap() + if (this.sealed) throw new Error("can't unwrap a sealed vault"); + return this._unwrap(); } _unwrap() { - return shadowedMap(this, (key: string) => { - return key.startsWith('#') ? this._secrets.get(key) : this.get(key) - }, () => {}) + return shadowedMap( + this, + (key: string) => { + return key.startsWith("#") ? this._secrets.get(key) : this.get(key); + }, + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ); } } diff --git a/packages/hdwallet-native-vault/src/types.ts b/packages/hdwallet-native-vault/src/types.ts index df875274f..57d07265a 100644 --- a/packages/hdwallet-native-vault/src/types.ts +++ b/packages/hdwallet-native-vault/src/types.ts @@ -4,8 +4,23 @@ import type * as idb from "idb-keyval"; import type { Revocable } from "./util"; export type AsyncCrypto = Omit & { - getRandomValues(array: T): T | Promise; -} + getRandomValues< + T extends + | DataView + | Float32Array + | Float64Array + | Uint8ClampedArray + | Uint8Array + | Int8Array + | Int16Array + | Int32Array + | Uint16Array + | Uint32Array + | null + >( + array: T + ): T | Promise; +}; export type ArgonParams = Pick; diff --git a/packages/hdwallet-native-vault/src/util.ts b/packages/hdwallet-native-vault/src/util.ts index c6a111b53..1d310d2f7 100644 --- a/packages/hdwallet-native-vault/src/util.ts +++ b/packages/hdwallet-native-vault/src/util.ts @@ -6,32 +6,32 @@ import { TextDecoder, TextEncoder } from "web-encoding"; import { AsyncCrypto } from "./types"; const nativeEngines = (async () => { - return (await import("@shapeshiftoss/hdwallet-native")).crypto.Isolation.Engines -})() + return (await import("@shapeshiftoss/hdwallet-native")).crypto.Isolation.Engines; +})(); export async function createMnemonic(mnemonic: string) { - return (await nativeEngines).Default.BIP39.Mnemonic.create(mnemonic) + return (await nativeEngines).Default.BIP39.Mnemonic.create(mnemonic); } export const entropyToMnemonic = bip39.entropyToMnemonic.bind(bip39); -let cryptoResolver: ((x: AsyncCrypto) => void) | undefined +let cryptoResolver: ((x: AsyncCrypto) => void) | undefined; export function setCrypto(x: AsyncCrypto) { if (!x) throw new Error("crypto module is required"); if (!cryptoResolver) throw new Error("can only set crypto module once"); - cryptoResolver(x) - cryptoResolver = undefined + cryptoResolver(x); + cryptoResolver = undefined; } -export const crypto = new Promise(resolve => cryptoResolver = resolve) +export const crypto = new Promise((resolve) => (cryptoResolver = resolve)); -let performanceResolver: ((x: Performance) => void) | undefined +let performanceResolver: ((x: Performance) => void) | undefined; export function setPerformance(x: Performance) { if (!x) throw new Error("performance module is required"); if (!performanceResolver) throw new Error("can only set performance module once"); - performanceResolver(x) - performanceResolver = undefined + performanceResolver(x); + performanceResolver = undefined; } -export const performance = new Promise(resolve => performanceResolver = resolve) +export const performance = new Promise((resolve) => (performanceResolver = resolve)); export const uuidNamespace = uuid.v5("hdwallet-native-vault", uuid.NIL); export const keyStoreUUID = uuid.v5("keyStore", uuidNamespace); @@ -46,7 +46,11 @@ export const revocable = native.crypto.Isolation.Engines.Default.revocable; export const decoder = new TextDecoder(); export const encoder = new TextEncoder(); -export function shadowedMap>(map: T, get: (key: K) => undefined | V, addRevoker: (revoke: () => void) => void): T { +export function shadowedMap>( + map: T, + get: (key: K) => undefined | V, + addRevoker: (revoke: () => void) => void +): T { const self = map; const { proxy, revoke } = Proxy.revocable(self, { get(t, p, r) { diff --git a/packages/hdwallet-native-vault/src/vault.ts b/packages/hdwallet-native-vault/src/vault.ts index 9b9716034..443f4b169 100644 --- a/packages/hdwallet-native-vault/src/vault.ts +++ b/packages/hdwallet-native-vault/src/vault.ts @@ -4,8 +4,8 @@ import * as ta from "type-assertions"; import { MapVault } from "./mapVault"; import { RawVault } from "./rawVault"; -import { IVault, ISealableVaultFactory, VaultPrepareParams } from "./types"; -import { Revocable, crypto, decoder, encoder, revocable, shadowedMap } from "./util"; +import { ISealableVaultFactory, IVault, VaultPrepareParams } from "./types"; +import { crypto, decoder, encoder, Revocable, revocable, shadowedMap } from "./util"; export type ValueWrapper = (x: unknown, addRevoker: (revoke: () => void) => void) => Promise; export type ValueTransformer = (x: unknown, addRevoker: (revoke: () => void) => void) => Promise; @@ -17,10 +17,10 @@ export class Vault extends MapVault implements IVault { static async prepare(params?: VaultPrepareParams) { return MapVault.prepare(params); } - static async create(password?: string, sealed: boolean = true) { + static async create(password?: string, sealed = true) { return await Vault.open(undefined, password, sealed); } - static async open(id?: string, password?: string, sealed: boolean = true) { + static async open(id?: string, password?: string, sealed = true) { await Vault.prepare(); const out = new Vault(await RawVault.open(id, password)); @@ -152,7 +152,7 @@ export class Vault extends MapVault implements IVault { } #unwrap(addRevoker: (revoke: () => void) => void = (x) => this.addRevoker(x)) { - return shadowedMap(this, (x: string) => this.#getUnwrapped(x), addRevoker) + return shadowedMap(this, (x: string) => this.#getUnwrapped(x), addRevoker); } #sealed = false; @@ -160,7 +160,7 @@ export class Vault extends MapVault implements IVault { this.#sealed = true; } get sealed() { - return this.#sealed + return this.#sealed; } unwrap(addRevoker?: (revoke: () => void) => void) { diff --git a/packages/hdwallet-native/__mocks__/@shapeshiftoss/hdwallet-core.ts b/packages/hdwallet-native/__mocks__/@shapeshiftoss/hdwallet-core.ts index 8fe35d8c9..1fb3471c8 100644 --- a/packages/hdwallet-native/__mocks__/@shapeshiftoss/hdwallet-core.ts +++ b/packages/hdwallet-native/__mocks__/@shapeshiftoss/hdwallet-core.ts @@ -5,4 +5,4 @@ for (const key of Object.keys(Object.getOwnPropertyDescriptors(actual))) { mock[key] = actual[key]; } module.exports = mock as typeof actual; -export {} +export {}; diff --git a/packages/hdwallet-native/__mocks__/mswMock.ts b/packages/hdwallet-native/__mocks__/mswMock.ts index faabd33d9..ce1db9191 100644 --- a/packages/hdwallet-native/__mocks__/mswMock.ts +++ b/packages/hdwallet-native/__mocks__/mswMock.ts @@ -4,7 +4,10 @@ import { setupServer } from "msw/node"; export = function newMswMock(handlers: Record> = {}) { Object.values(handlers).forEach((x) => { Object.entries(x).forEach(([k, v]) => { - x[k] = Object.assign(jest.fn((...args: any[]) => (typeof v === "function" ? v(...args) : v)), v); + x[k] = Object.assign( + jest.fn((...args: any[]) => (typeof v === "function" ? v(...args) : v)), + v + ); }); }); @@ -35,7 +38,7 @@ export = function newMswMock(handlers: Record> = out = {}; } return res(ctx.status(status), ctx.json(out)); - }) + }); }) ) .reduce((a, x) => a.concat(x), []) diff --git a/packages/hdwallet-native/__mocks__/untouchableMock.ts b/packages/hdwallet-native/__mocks__/untouchableMock.ts index 8e03bef5c..c8f5a5397 100644 --- a/packages/hdwallet-native/__mocks__/untouchableMock.ts +++ b/packages/hdwallet-native/__mocks__/untouchableMock.ts @@ -1,4 +1,4 @@ -function mockTraps(mock: Function) { +function mockTraps(mock: () => unknown) { return new Proxy( {}, { @@ -16,9 +16,8 @@ function doMock(target: T) { mock, ] as const; } -export const mock = doMock; -export function bind(obj: Record, prop: string, ...args: any[]) { +export function bind(obj: Record unknown>, prop: string, ...args: any[]) { const mock = jest.fn(); const [objProxy, objMock] = doMock(obj); objMock.mockImplementation(() => mock()); @@ -30,13 +29,13 @@ export function bind(obj: Record, prop: string, ...args: any[] return [Function.prototype.bind.call(obj[prop], objProxy, ...argProxies), mock]; } -export function call(obj: Record, prop: string, ...args: any[]) { +export function call(obj: Record unknown>, prop: string, ...args: any[]) { expect(obj[prop]).toBeInstanceOf(Function); const [fn, mock] = bind(obj, prop, ...args); const out = fn(); if ((typeof out === "object" || typeof out === "function") && "then" in out && out.then instanceof Function) { return (async () => { - const outResolved = await out; + await out; // make sure the method under test has time to hit the tripwire expect(mock).not.toHaveBeenCalled(); return out; })(); @@ -44,3 +43,5 @@ export function call(obj: Record, prop: string, ...args: any[] expect(mock).not.toHaveBeenCalled(); return out; } + +export const mock = doMock; diff --git a/packages/hdwallet-native/src/adapter.ts b/packages/hdwallet-native/src/adapter.ts index c8cf75bad..66977de7d 100644 --- a/packages/hdwallet-native/src/adapter.ts +++ b/packages/hdwallet-native/src/adapter.ts @@ -1,17 +1,20 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import * as native from "./native"; import * as Isolation from "./crypto/isolation"; +import * as native from "./native"; export type NativeAdapterArgs = { deviceId: string; -} & ({ - mnemonic?: string | Isolation.Core.BIP39.Mnemonic; - masterKey?: never; -} | { - mnemonic?: never; - masterKey?: Isolation.Core.BIP32.Node; -}); +} & ( + | { + mnemonic?: string | Isolation.Core.BIP39.Mnemonic; + masterKey?: never; + } + | { + mnemonic?: never; + masterKey?: Isolation.Core.BIP32.Node; + } +); export class NativeAdapter { keyring: core.Keyring; diff --git a/packages/hdwallet-native/src/binance.test.ts b/packages/hdwallet-native/src/binance.test.ts index 376103c91..129051b5d 100644 --- a/packages/hdwallet-native/src/binance.test.ts +++ b/packages/hdwallet-native/src/binance.test.ts @@ -28,38 +28,8 @@ const mswMock = require("mswMock")({ validator_info: { address: "B7707D9F593C62E85BB9E1A2366D12A97CD5DFF2", pub_key: [ - 113, - 242, - 215, - 184, - 236, - 28, - 139, - 153, - 166, - 83, - 66, - 155, - 1, - 24, - 205, - 32, - 31, - 121, - 79, - 64, - 157, - 15, - 234, - 77, - 101, - 177, - 182, - 98, - 242, - 176, - 0, - 99, + 113, 242, 215, 184, 236, 28, 139, 153, 166, 83, 66, 155, 1, 24, 205, 32, 31, 121, 79, 64, 157, 15, 234, 77, + 101, 177, 182, 98, 242, 176, 0, 99, ], voting_power: 1000000000000, }, @@ -111,15 +81,15 @@ describe("NativeBinanceWallet", () => { }); it("should generate a correct binance address", async () => { - expect(await - wallet.binanceGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/714'/0'/0/0") }) - ).toBe("bnb1qzc0v2q7u6484czzal6ncuvqmg9fae8n2xe2c6"); + expect(await wallet.binanceGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/714'/0'/0/0") })).toBe( + "bnb1qzc0v2q7u6484czzal6ncuvqmg9fae8n2xe2c6" + ); }); it("should generate another correct binance address", async () => { - expect(await - wallet.binanceGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/714'/1337'/123/4") }) - ).toBe("bnb14awl92k30hyhu36wjy6pg0trx3zlqrpfgsf3xk"); + expect(await wallet.binanceGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/714'/1337'/123/4") })).toBe( + "bnb14awl92k30hyhu36wjy6pg0trx3zlqrpfgsf3xk" + ); }); it("should sign a transaction correctly", async () => { @@ -255,10 +225,7 @@ describe("NativeBinanceWallet", () => { }, }) ).rejects.toThrowError(); - expect(mswMock).toHaveBeenCalledWith( - "GET", - "https://dex.binance.org/api/v1/node-info" - ); + expect(mswMock).toHaveBeenCalledWith("GET", "https://dex.binance.org/api/v1/node-info"); mswMock.clear(); }); diff --git a/packages/hdwallet-native/src/binance.ts b/packages/hdwallet-native/src/binance.ts index dbd562c98..a37a1262e 100644 --- a/packages/hdwallet-native/src/binance.ts +++ b/packages/hdwallet-native/src/binance.ts @@ -4,11 +4,12 @@ import BigNumber from "bignumber.js"; import type * as bnbSdk from "bnb-javascript-sdk-nobroadcast"; import CryptoJS from "crypto-js"; +import * as Isolation from "./crypto/isolation"; import { NativeHDWalletBase } from "./native"; import * as util from "./util"; -import * as Isolation from "./crypto/isolation"; export function MixinNativeBinanceWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeBinanceWalletInfo extends Base implements core.BinanceWalletInfo { readonly _supportsBinanceInfo = true; @@ -33,6 +34,7 @@ export function MixinNativeBinanceWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeBinanceWallet extends Base { readonly _supportsBinance = true; @@ -68,6 +71,7 @@ export function MixinNativeBinanceWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "binance"); return this.createBinanceAddress(keyPair.publicKey.toString("hex"), msg.testnet ?? false); }); @@ -75,16 +79,19 @@ export function MixinNativeBinanceWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "binance"); - const tx = Object.assign({}, msg.tx) - if (!tx.data) tx.data = null - if (!tx.memo) tx.memo = "" - if (!tx.sequence) tx.sequence = "0" - if (!tx.source) tx.source = "0" + const tx = Object.assign({}, msg.tx); + if (!tx.data) tx.data = null; + if (!tx.memo) tx.memo = ""; + if (!tx.sequence) tx.sequence = "0"; + if (!tx.source) tx.source = "0"; - const bnbSdk = await import("bnb-javascript-sdk-nobroadcast") - const client = new bnbSdk.BncClient(msg.testnet ? "https://testnet-dex.binance.org" : "https://dex.binance.org"); // broadcast not used but available + const bnbSdk = await import("bnb-javascript-sdk-nobroadcast"); + const client = new bnbSdk.BncClient( + msg.testnet ? "https://testnet-dex.binance.org" : "https://dex.binance.org" + ); // broadcast not used but available await client.chooseNetwork(msg.testnet ? "testnet" : "mainnet"); const haveAccountNumber = !!msg.tx.account_number && Number.isInteger(Number(msg.tx.account_number)); if (haveAccountNumber) await client.setAccountNumber(Number(msg.tx.account_number)); @@ -106,9 +113,10 @@ export function MixinNativeBinanceWallet, status: number} = core.mustBeDefined(await client.getAccount(addressFrom)); - if (!(status === 200 && "account_number" in result && typeof result.account_number === "number")) throw new Error("unable to load account number"); + const { result, status }: { result: Record; status: number } = core.mustBeDefined( + await client.getAccount(addressFrom) + ); + if (!(status === 200 && "account_number" in result && typeof result.account_number === "number")) + throw new Error("unable to load account number"); tx.account_number = result.account_number.toString(); } // The Binance SDK takes amounts as decimal strings. const amount = new BigNumber(tx.msgs[0].inputs[0].coins[0].amount); if (!amount.isInteger()) throw new Error("amount must be an integer"); - if (!amount.isEqualTo(tx.msgs[0].outputs[0].coins[0].amount)) throw new Error("amount in input and output must be equal"); + if (!amount.isEqualTo(tx.msgs[0].outputs[0].coins[0].amount)) + throw new Error("amount in input and output must be equal"); const asset = tx.msgs[0].inputs[0].coins[0].denom; - if (asset !== tx.msgs[0].outputs[0].coins[0].denom) throw new Error("denomination in input and output must be the same"); + if (asset !== tx.msgs[0].outputs[0].coins[0].denom) + throw new Error("denomination in input and output must be the same"); const result = (await client.transfer( addressFrom, @@ -135,13 +148,13 @@ export function MixinNativeBinanceWallet & { - accountNumber: number, + )) as unknown as Omit & { + accountNumber: number; // msgs: core.BinanceTx["msgs"], signatures: Array<{ - pub_key: Buffer, - signature: string, - }>, + pub_key: Buffer; + signature: string; + }>; }; const serialized = result.serialize(); @@ -159,17 +172,21 @@ export function MixinNativeBinanceWallet { coin: "Bitcoin", scriptType: "p2wpkh", addressNList: core.bip32ToAddressNList("m/84'/0'/0'"), - } + }, ], ], [ @@ -404,7 +406,7 @@ describe("NativeBTCWallet", () => { }); it("should not sign a transaction without having the raw input transaction", async () => { - const input = _.cloneDeep(BIP44_BENCHMARK_TX); + const input = cloneDeep(BIP44_BENCHMARK_TX); delete (input.inputs[0] as any).hex; await expect(wallet.btcSignTx(input)).rejects.toThrowError("must provide prev rawTx"); }); @@ -468,7 +470,7 @@ describe("NativeBTCWallet", () => { }); it("should not sign a transaction with the wrong key", async () => { - const input = _.cloneDeep(BIP44_BENCHMARK_TX); + const input = cloneDeep(BIP44_BENCHMARK_TX); input.inputs[0].addressNList = core.bip32ToAddressNList("m/44'/0'/1337'/123/4"); await expect(wallet.btcSignTx(input as any)).rejects.toThrowError("Can not sign for this input"); diff --git a/packages/hdwallet-native/src/bitcoin.ts b/packages/hdwallet-native/src/bitcoin.ts index 582ec9815..182ea6bbd 100644 --- a/packages/hdwallet-native/src/bitcoin.ts +++ b/packages/hdwallet-native/src/bitcoin.ts @@ -1,10 +1,10 @@ +import * as bitcoin from "@shapeshiftoss/bitcoinjs-lib"; import * as core from "@shapeshiftoss/hdwallet-core"; import * as bchAddr from "bchaddrjs"; -import * as bitcoin from "@shapeshiftoss/bitcoinjs-lib"; import * as Isolation from "./crypto/isolation"; -import { getNetwork } from "./networks"; import { NativeHDWalletBase } from "./native"; +import { getNetwork } from "./networks"; import * as util from "./util"; const supportedCoins = ["bitcoin", "dash", "digibyte", "dogecoin", "litecoin", "bitcoincash", "testnet"]; @@ -17,7 +17,7 @@ type NonWitnessUtxo = Buffer; type WitnessUtxo = { script: Buffer; - amount: Number; + amount: number; }; type UtxoData = NonWitnessUtxo | WitnessUtxo; @@ -28,12 +28,13 @@ type ScriptData = { }; type BchInputData = { - sighashType?: number -} + sighashType?: number; +}; type InputData = UtxoData | ScriptData | BchInputData; export function MixinNativeBTCWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeBTCWalletInfo extends Base implements core.BTCWalletInfo { readonly _supportsBTCInfo = true; @@ -100,6 +101,7 @@ export function MixinNativeBTCWalletInfo): boolean { // TODO: support at some point return false; @@ -112,7 +114,7 @@ export function MixinNativeBTCWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeBTCWallet extends Base { readonly _supportsBTC = true; @@ -200,7 +203,7 @@ export function MixinNativeBTCWallet bitcoin.script.OPS[k] === opcode) != "OP_RETURN") { console.error("OP_RETURN output not found for transaction."); return false; @@ -218,6 +221,7 @@ export function MixinNativeBTCWallet { return this.needsMnemonic(!!this.#masterKey, async () => { const { addressNList, amount, hex, scriptType } = input; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, addressNList, coin, scriptType); const isSegwit = !!scriptType && segwit.includes(scriptType); @@ -237,7 +241,7 @@ export function MixinNativeBTCWallet { return this.needsMnemonic(!!this.#masterKey, async () => { const { addressNList, coin, scriptType } = msg; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, addressNList, coin, scriptType); const { address } = this.createPayment(keyPair.publicKey, scriptType, keyPair.network); if (!address) return null; @@ -278,46 +283,51 @@ export function MixinNativeBTCWallet { - try { - const inputData = await this.buildInput(coin, input); - - psbt.addInput({ - hash: input.txid, - index: input.vout, - ...inputData, - }); - } catch (e) { - throw new Error(`failed to add input: ${e}`); - } - })); - - await Promise.all(outputs.map(async (output) => { - try { - const { amount } = output; - - let address: string; - if (output.address !== undefined) { - address = output.address; - } else if (output.addressNList !== undefined) { - const keyPair = await util.getKeyPair(this.#masterKey!, output.addressNList, coin, output.scriptType); - const { publicKey, network } = keyPair; - const payment = this.createPayment(publicKey, output.scriptType, network); - if (!payment.address) throw new Error("could not get payment address"); - address = payment.address; - } else { - throw new Error("unsupported output type"); + await Promise.all( + inputs.map(async (input) => { + try { + const inputData = await this.buildInput(coin, input); + + psbt.addInput({ + hash: input.txid, + index: input.vout, + ...inputData, + }); + } catch (e) { + throw new Error(`failed to add input: ${e}`); } - - if (coin.toLowerCase() === "bitcoincash") { - address = bchAddr.toLegacyAddress(address); + }) + ); + + await Promise.all( + outputs.map(async (output) => { + try { + const { amount } = output; + + let address: string; + if (output.address !== undefined) { + address = output.address; + } else if (output.addressNList !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const keyPair = await util.getKeyPair(this.#masterKey!, output.addressNList, coin, output.scriptType); + const { publicKey, network } = keyPair; + const payment = this.createPayment(publicKey, output.scriptType, network); + if (!payment.address) throw new Error("could not get payment address"); + address = payment.address; + } else { + throw new Error("unsupported output type"); + } + + if (coin.toLowerCase() === "bitcoincash") { + address = bchAddr.toLegacyAddress(address); + } + + psbt.addOutput({ address, value: Number(amount) }); + } catch (e) { + throw new Error(`failed to add output: ${e}`); } - - psbt.addOutput({ address, value: Number(amount) }); - } catch (e) { - throw new Error(`failed to add output: ${e}`); - } - })); + }) + ); if (msg.opReturnData) { const data = Buffer.from(msg.opReturnData, "utf-8"); @@ -327,15 +337,18 @@ export function MixinNativeBTCWallet { - try { - const { addressNList, scriptType } = input; - const keyPair = await util.getKeyPair(this.#masterKey!, addressNList, coin, scriptType); - await psbt.signInputAsync(idx, keyPair); - } catch (e) { - throw new Error(`failed to sign input: ${e}`); - } - })); + await Promise.all( + inputs.map(async (input, idx) => { + try { + const { addressNList, scriptType } = input; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const keyPair = await util.getKeyPair(this.#masterKey!, addressNList, coin, scriptType); + await psbt.signInputAsync(idx, keyPair); + } catch (e) { + throw new Error(`failed to sign input: ${e}`); + } + }) + ); psbt.finalizeAllInputs(); @@ -362,10 +375,12 @@ export function MixinNativeBTCWallet { throw new Error("function not implemented"); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars async btcVerifyMessage(msg: core.BTCVerifyMessage): Promise { throw new Error("function not implemented"); } diff --git a/packages/hdwallet-native/src/cosmos.test.ts b/packages/hdwallet-native/src/cosmos.test.ts index 234d8425b..ee3aed22b 100644 --- a/packages/hdwallet-native/src/cosmos.test.ts +++ b/packages/hdwallet-native/src/cosmos.test.ts @@ -38,42 +38,44 @@ describe("NativeCosmosWallet", () => { }); it("should generate a correct cosmos address", async () => { - expect( - await wallet.cosmosGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0") }) - ).toBe("cosmos1knuunh0lmwyrkjmrj7sky49uxk3peyzhzsvqqf"); + expect(await wallet.cosmosGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0") })).toBe( + "cosmos1knuunh0lmwyrkjmrj7sky49uxk3peyzhzsvqqf" + ); }); it("should generate another correct cosmos address", async () => { - expect( - await wallet.cosmosGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/118'/1337'/123/4") }) - ).toBe("cosmos14k4dnrrmxdch6nkvvuugsywrgmvlwrqszjfxjt"); + expect(await wallet.cosmosGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/118'/1337'/123/4") })).toBe( + "cosmos14k4dnrrmxdch6nkvvuugsywrgmvlwrqszjfxjt" + ); }); it("should sign a transaction correctly", async () => { const signed = await wallet.cosmosSignTx({ addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), tx: { - msg: [{ - "type": "cosmos-sdk/MsgSend", - "value": { - "from_address": "cosmos1knuunh0lmwyrkjmrj7sky49uxk3peyzhzsvqqf", - "to_address": "cosmos1knuunh0lmwyrkjmrj7sky49uxk3peyzhzsvqqf", - "amount": [ - { - "denom": "uatom", - "amount": "1000" - } - ] - } - }], + msg: [ + { + type: "cosmos-sdk/MsgSend", + value: { + from_address: "cosmos1knuunh0lmwyrkjmrj7sky49uxk3peyzhzsvqqf", + to_address: "cosmos1knuunh0lmwyrkjmrj7sky49uxk3peyzhzsvqqf", + amount: [ + { + denom: "uatom", + amount: "1000", + }, + ], + }, + }, + ], fee: { - "amount": [ + amount: [ { - "amount": "100", - "denom": "uatom" - } + amount: "100", + denom: "uatom", + }, ], - "gas": "100000" + gas: "100000", }, signatures: null, memo: "foobar", diff --git a/packages/hdwallet-native/src/cosmos.ts b/packages/hdwallet-native/src/cosmos.ts index cfed17831..4a6fa3430 100644 --- a/packages/hdwallet-native/src/cosmos.ts +++ b/packages/hdwallet-native/src/cosmos.ts @@ -1,15 +1,16 @@ import * as core from "@shapeshiftoss/hdwallet-core"; +import * as protoTxBuilder from "@shapeshiftoss/proto-tx-builder"; import * as bech32 from "bech32"; import CryptoJS from "crypto-js"; -import * as protoTxBuilder from "@shapeshiftoss/proto-tx-builder"; +import * as Isolation from "./crypto/isolation"; import { NativeHDWalletBase } from "./native"; import * as util from "./util"; -import * as Isolation from "./crypto/isolation"; const ATOM_CHAIN = "cosmoshub-4"; export function MixinNativeCosmosWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeCosmosWalletInfo extends Base implements core.CosmosWalletInfo { readonly _supportsCosmosInfo = true; async cosmosSupportsNetwork(): Promise { @@ -25,7 +26,7 @@ export function MixinNativeCosmosWalletInfo { - const slip44 = core.slip44ByCoin("Atom") + const slip44 = core.slip44ByCoin("Atom"); return [ { addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0], @@ -33,6 +34,7 @@ export function MixinNativeCosmosWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeCosmosWallet extends Base { readonly _supportsCosmos = true; @@ -68,6 +71,7 @@ export function MixinNativeCosmosWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "cosmos"); return this.createCosmosAddress(keyPair.publicKey.toString("hex")); }); @@ -75,10 +79,11 @@ export function MixinNativeCosmosWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "cosmos"); - const adapter = await Isolation.Adapters.CosmosDirect.create(keyPair.node,"cosmos"); + const adapter = await Isolation.Adapters.CosmosDirect.create(keyPair.node, "cosmos"); const result = await protoTxBuilder.sign(msg.tx, adapter, msg.sequence, msg.account_number, ATOM_CHAIN); - return result + return result; }); } }; diff --git a/packages/hdwallet-native/src/crypto/CryptoHelper.test.ts b/packages/hdwallet-native/src/crypto/CryptoHelper.test.ts index 68a70bd49..92b8ae1db 100644 --- a/packages/hdwallet-native/src/crypto/CryptoHelper.test.ts +++ b/packages/hdwallet-native/src/crypto/CryptoHelper.test.ts @@ -4,14 +4,16 @@ import * as webcrypto from "@peculiar/webcrypto"; import * as core from "@shapeshiftoss/hdwallet-core"; -import CryptoHelper from "./CryptoHelper"; import { CipherString } from "./classes"; +import CryptoHelper from "./CryptoHelper"; import { WebCryptoEngine } from "./engines"; import * as utils from "./utils"; -const PLAINTEXT_STRING = "totally random secret data" -const ENCRYPTED_STRING = "2.A/tC/OC0U/KN3XuAuz2L36lydOyr5x367tPSGSrPkvQ=|AAAAAAAAAAAAAAAAAAAAAA==|ZqR8HTeOg4+8mzcty10jVFZ5MqFFbn5bwEaqlL0c/Mg=" -const ENCRYPTED_EMPTY_STRING = "2.7wpUO5ISHHdT1voBnjzyXQ==|AAAAAAAAAAAAAAAAAAAAAA==|02KQ8aQWWXUX7foOmf4T2W0XCFAk4OTKFQO+hRhlRcY="; +const PLAINTEXT_STRING = "totally random secret data"; +const ENCRYPTED_STRING = + "2.A/tC/OC0U/KN3XuAuz2L36lydOyr5x367tPSGSrPkvQ=|AAAAAAAAAAAAAAAAAAAAAA==|ZqR8HTeOg4+8mzcty10jVFZ5MqFFbn5bwEaqlL0c/Mg="; +const ENCRYPTED_EMPTY_STRING = + "2.7wpUO5ISHHdT1voBnjzyXQ==|AAAAAAAAAAAAAAAAAAAAAA==|02KQ8aQWWXUX7foOmf4T2W0XCFAk4OTKFQO+hRhlRcY="; const BAD_ARGS = [undefined, null, "encrypteddatastring", [1, 2, 3, 4, 5, 6], {}]; @@ -23,8 +25,7 @@ describe("CryptoHelpers", () => { describe("constructor", () => { it("should require a CryptoEngine instances", () => { - // @ts-ignore - expect(() => new CryptoHelper()).toThrow("Missing cryptography engine"); + expect(() => new CryptoHelper(undefined as any)).toThrow("Missing cryptography engine"); }); it("should return a new instance", () => { @@ -85,7 +86,12 @@ describe("CryptoHelpers", () => { it("should work with Uint8Arrays", async () => { const key = await helper.makeKey("password", "email"); const encrypted = await helper.aesEncrypt(utils.fromUtf8ToArray(PLAINTEXT_STRING), key); - const decrypted = await helper.aesDecrypt(new Uint8Array(encrypted.data), new Uint8Array(encrypted.iv), new Uint8Array(encrypted.mac), key); + const decrypted = await helper.aesDecrypt( + new Uint8Array(encrypted.data), + new Uint8Array(encrypted.iv), + new Uint8Array(encrypted.mac), + key + ); expect(utils.fromBufferToUtf8(decrypted)).toEqual(PLAINTEXT_STRING); }); }); @@ -93,14 +99,14 @@ describe("CryptoHelpers", () => { describe("aesDecrypt", () => { it("should decrypt data with an hmac signature", async () => { const key = await helper.makeKey("password", "email"); - const encrypted = (new CipherString(ENCRYPTED_STRING)).toEncryptedObject(key); + const encrypted = new CipherString(ENCRYPTED_STRING).toEncryptedObject(key); const decrypted = await helper.aesDecrypt(encrypted.data, encrypted.iv, encrypted.mac, encrypted.key); expect(utils.fromBufferToUtf8(decrypted)).toEqual(PLAINTEXT_STRING); }); it("should fail if the data is incorrect", async () => { const key = await helper.makeKey("password", "email"); - const encrypted = (new CipherString(ENCRYPTED_STRING)).toEncryptedObject(key); + const encrypted = new CipherString(ENCRYPTED_STRING).toEncryptedObject(key); const data = new Uint8Array(encrypted.data.byteLength).fill(0x80); await expect(helper.aesDecrypt(data, encrypted.iv, encrypted.mac, encrypted.key)).rejects.toThrow( "HMAC signature is not valid" @@ -109,7 +115,7 @@ describe("CryptoHelpers", () => { it("should fail if the iv is incorrect", async () => { const key = await helper.makeKey("password", "email"); - const encrypted = (new CipherString(ENCRYPTED_STRING)).toEncryptedObject(key); + const encrypted = new CipherString(ENCRYPTED_STRING).toEncryptedObject(key); const iv = new Uint8Array(encrypted.iv.byteLength).fill(0x80); await expect(helper.aesDecrypt(encrypted.data, iv, encrypted.mac, encrypted.key)).rejects.toThrow( "HMAC signature is not valid" @@ -118,7 +124,7 @@ describe("CryptoHelpers", () => { it("should fail if the mac is incorrect", async () => { const key = await helper.makeKey("password", "email"); - const encrypted = (new CipherString(ENCRYPTED_STRING)).toEncryptedObject(key); + const encrypted = new CipherString(ENCRYPTED_STRING).toEncryptedObject(key); const mac = new Uint8Array(encrypted.mac.byteLength).fill(0x80); await expect(helper.aesDecrypt(encrypted.data, encrypted.iv, mac, encrypted.key)).rejects.toThrow( "HMAC signature is not valid" @@ -126,8 +132,8 @@ describe("CryptoHelpers", () => { }); it("should fail if the key is incorrect", async () => { - let key = await helper.makeKey("password2", "email"); - const encrypted = (new CipherString(ENCRYPTED_STRING)).toEncryptedObject(key); + const key = await helper.makeKey("password2", "email"); + const encrypted = new CipherString(ENCRYPTED_STRING).toEncryptedObject(key); await expect(helper.aesDecrypt(encrypted.data, encrypted.iv, encrypted.mac, encrypted.key)).rejects.toThrow( "HMAC signature is not valid" ); @@ -140,7 +146,6 @@ describe("CryptoHelpers", () => { ["key", 3], ])("should throw an error if %s is not the correct type", async (name, position) => { const dummyArg = new Uint8Array(32).fill(0); - let i = 0; for (const value of BAD_ARGS) { const args = new Array(position).fill(dummyArg); args.push(value); @@ -156,9 +161,9 @@ describe("CryptoHelpers", () => { it("should return false if the hmac results are different sizes", async () => { let i = 0; - const mock = jest.spyOn(engine, "hmac").mockImplementation(async (value, key) => { + const mock = jest.spyOn(engine, "hmac").mockImplementation(async () => { return core.toArrayBuffer(new Uint8Array(++i * 16)); - }) + }); const result = await helper.compare(new Uint8Array(32), new Uint8Array(32)); mock.mockRestore(); await expect(result).toBe(false); @@ -180,9 +185,12 @@ describe("CryptoHelpers", () => { } ); - it.each([[undefined], [null], [""], [[1, 2, 3, 4, 5, 6]], [{}]])("should require an email (%o)", async (param: any) => { - await expect(helper.makeKey("mypassword", param)).rejects.toThrow("email"); - }); + it.each([[undefined], [null], [""], [[1, 2, 3, 4, 5, 6]], [{}]])( + "should require an email (%o)", + async (param: any) => { + await expect(helper.makeKey("mypassword", param)).rejects.toThrow("email"); + } + ); }); describe("deviceId", () => { diff --git a/packages/hdwallet-native/src/crypto/CryptoHelper.ts b/packages/hdwallet-native/src/crypto/CryptoHelper.ts index 3a90d2443..46d77595b 100644 --- a/packages/hdwallet-native/src/crypto/CryptoHelper.ts +++ b/packages/hdwallet-native/src/crypto/CryptoHelper.ts @@ -55,7 +55,10 @@ export default class CryptoHelper { const macData = new Uint8Array(iv.byteLength + encData.byteLength); macData.set(new Uint8Array(iv), 0); macData.set(new Uint8Array(encData), iv.byteLength); - const mac = await this.#engine.hmac(macData.buffer.slice(macData.byteOffset, macData.byteOffset + macData.byteLength), key.macKey); + const mac = await this.#engine.hmac( + macData.buffer.slice(macData.byteOffset, macData.byteOffset + macData.byteLength), + key.macKey + ); return new EncryptedObject({ key, iv, data: encData, mac }); } @@ -74,17 +77,14 @@ export default class CryptoHelper { if (iv instanceof Uint8Array) iv = core.toArrayBuffer(iv); if (mac == null || !(mac instanceof ArrayBuffer || mac instanceof Uint8Array)) throw new Error("Required parameter [mac] is not of type ArrayBuffer or Uint8Array"); - if (mac instanceof Uint8Array) mac = core.toArrayBuffer(mac) + if (mac instanceof Uint8Array) mac = core.toArrayBuffer(mac); if (key == null || key.encKey == null || key.macKey == null) throw new Error("Required parameter [key] is not of type SymmetricCryptoKey"); const macData = new Uint8Array(iv.byteLength + data.byteLength); macData.set(new Uint8Array(iv), 0); macData.set(new Uint8Array(data), iv.byteLength); - const computedMac = await this.#engine.hmac( - core.toArrayBuffer(macData), - key.macKey - ); + const computedMac = await this.#engine.hmac(core.toArrayBuffer(macData), key.macKey); const macsMatch = await this.compare(mac, computedMac); if (!macsMatch) throw new Error("HMAC signature is not valid or data has been tampered with"); @@ -107,9 +107,7 @@ export default class CryptoHelper { t.set(info, previousT.length); t.set([i + 1], t.length - 1); - previousT = new Uint8Array( - await this.#engine.hmac(core.toArrayBuffer(t), prk) - ); + previousT = new Uint8Array(await this.#engine.hmac(core.toArrayBuffer(t), prk)); okm.set(previousT, i * hashLen); } @@ -155,7 +153,7 @@ export default class CryptoHelper { } // use entropyToMnemonic to generate mnemonic so we can utilize provided randomBytes function - async generateMnemonic(strength: number = 128): Promise { + async generateMnemonic(strength = 128): Promise { const entropy = await this.#engine.randomBytes(strength / 8); return bip39.entropyToMnemonic(Buffer.from(entropy)); } diff --git a/packages/hdwallet-native/src/crypto/EncryptedWallet.test.ts b/packages/hdwallet-native/src/crypto/EncryptedWallet.test.ts index 32cb50e88..8457ce7cd 100644 --- a/packages/hdwallet-native/src/crypto/EncryptedWallet.test.ts +++ b/packages/hdwallet-native/src/crypto/EncryptedWallet.test.ts @@ -29,8 +29,9 @@ describe("EncryptedWallet", () => { }); it("should require a crypto engine", () => { - // @ts-ignore - expect(() => new EncryptedWallet()).toThrow("Missing"); + expect( + () => new EncryptedWallet(undefined as unknown as ConstructorParameters[0]) + ).toThrow("Missing"); }); }); @@ -48,8 +49,7 @@ describe("EncryptedWallet", () => { "should not generate a password hash and encrypted mnemonic if no password is provided (%p)", async (pw) => { const wallet = new EncryptedWallet(engine); - // @ts-ignore - await expect(wallet.init("email", pw)).rejects.toThrow("Invalid password"); + await expect(wallet.init("email", pw as string)).rejects.toThrow("Invalid password"); } ); @@ -57,8 +57,7 @@ describe("EncryptedWallet", () => { "should not generate a password hash and encrypted mnemonic if no email is provided (%p)", async (email) => { const wallet = new EncryptedWallet(engine); - // @ts-ignore - await expect(wallet.init(email, "password")).rejects.toThrow("Invalid email"); + await expect(wallet.init(email as string, "password")).rejects.toThrow("Invalid email"); } ); }); diff --git a/packages/hdwallet-native/src/crypto/EncryptedWallet.ts b/packages/hdwallet-native/src/crypto/EncryptedWallet.ts index 0f5823c4f..e29ace059 100644 --- a/packages/hdwallet-native/src/crypto/EncryptedWallet.ts +++ b/packages/hdwallet-native/src/crypto/EncryptedWallet.ts @@ -1,9 +1,9 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as bip39 from "bip39"; +import { CipherString, SymmetricCryptoKey } from "./classes"; import CryptoHelper from "./CryptoHelper"; import { CryptoEngine } from "./engines"; -import { CipherString, SymmetricCryptoKey } from "./classes"; import * as utils from "./utils"; export class EncryptedWallet { @@ -51,14 +51,6 @@ export class EncryptedWallet { return this.#encryptedWallet; } - /** - * Get an ID based on the mnemonic - * Calling `decrypt` will set this value after decryption is successful - */ - get deviceId() { - return this.#deviceId; - } - /** * Set the encrypted wallet by providing a string representation * @throws {Error} throws if `wallet` is not a valid encrypted wallet string @@ -68,6 +60,14 @@ export class EncryptedWallet { this.#encryptedWallet = new CipherString(wallet).encryptedString; } + /** + * Get an ID based on the mnemonic + * Calling `decrypt` will set this value after decryption is successful + */ + get deviceId() { + return this.#deviceId; + } + /** * Initialize the wallet with and email and password * @@ -97,14 +97,16 @@ export class EncryptedWallet { */ async createWallet(mnemonic?: string) { if (!this.isInitialized) throw new Error("Wallet is not initialized"); - mnemonic = mnemonic ?? await this.#helper.generateMnemonic(); + mnemonic = mnemonic ?? (await this.#helper.generateMnemonic()); if (!bip39.validateMnemonic(mnemonic)) { throw new Error("Invalid mnemonic"); } if (!this.#key) throw new Error("Wallet does not contain a key"); - this.#encryptedWallet = (await this.#helper.aesEncrypt(core.toArrayBuffer(utils.fromUtf8ToArray(mnemonic)), this.#key)).toString(); + this.#encryptedWallet = ( + await this.#helper.aesEncrypt(core.toArrayBuffer(utils.fromUtf8ToArray(mnemonic)), this.#key) + ).toString(); return this; } diff --git a/packages/hdwallet-native/src/crypto/classes/cipherString.test.ts b/packages/hdwallet-native/src/crypto/classes/cipherString.test.ts index 82bb0b7b6..32bc06105 100644 --- a/packages/hdwallet-native/src/crypto/classes/cipherString.test.ts +++ b/packages/hdwallet-native/src/crypto/classes/cipherString.test.ts @@ -9,7 +9,8 @@ describe("CipherString", () => { ); it("should throw an error if an invalid encryption type is specified", () => { - const string = "999.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=|AAAAAAAAAAAAAAAAAAAAAA==|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + const string = + "999.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=|AAAAAAAAAAAAAAAAAAAAAA==|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; expect(() => new CipherString(string)).toThrow("Unsupported encryption method"); }); diff --git a/packages/hdwallet-native/src/crypto/classes/cipherString.ts b/packages/hdwallet-native/src/crypto/classes/cipherString.ts index 8cc3d62ba..c9c004bf6 100644 --- a/packages/hdwallet-native/src/crypto/classes/cipherString.ts +++ b/packages/hdwallet-native/src/crypto/classes/cipherString.ts @@ -1,7 +1,7 @@ /* Copied from portis: packages/portis-crypto/src/models/cipherString.ts */ -import { fromBufferToB64, fromB64ToArray } from "../utils"; +import { fromB64ToArray, fromBufferToB64 } from "../utils"; import { EncryptedObject } from "./encryptedObject"; import { EncryptionType } from "./encryptionType"; import { SymmetricCryptoKey } from "./symmetricCryptoKey"; @@ -56,6 +56,6 @@ export class CipherString { iv: fromB64ToArray(this.iv), mac: fromB64ToArray(this.mac), key, - }) + }); } } diff --git a/packages/hdwallet-native/src/crypto/classes/encryptedObject.ts b/packages/hdwallet-native/src/crypto/classes/encryptedObject.ts index 3c2154140..c70c25016 100644 --- a/packages/hdwallet-native/src/crypto/classes/encryptedObject.ts +++ b/packages/hdwallet-native/src/crypto/classes/encryptedObject.ts @@ -10,7 +10,17 @@ export class EncryptedObject { data: ArrayBuffer; mac: ArrayBuffer; - constructor({key, iv, data, mac}: {key: SymmetricCryptoKey, iv: ArrayBuffer, data: ArrayBuffer, mac: ArrayBuffer}) { + constructor({ + key, + iv, + data, + mac, + }: { + key: SymmetricCryptoKey; + iv: ArrayBuffer; + data: ArrayBuffer; + mac: ArrayBuffer; + }) { this.key = key; this.iv = iv; this.data = data; diff --git a/packages/hdwallet-native/src/crypto/classes/encryptionType.ts b/packages/hdwallet-native/src/crypto/classes/encryptionType.ts index 5743517f2..78949438d 100644 --- a/packages/hdwallet-native/src/crypto/classes/encryptionType.ts +++ b/packages/hdwallet-native/src/crypto/classes/encryptionType.ts @@ -1,7 +1,6 @@ /* Copied from portis: packages/portis-crypto/src/enums/encryptionType.ts */ -/* eslint-disable @typescript-eslint/camelcase */ export enum EncryptionType { AesCbc256_HmacSha256_B64 = 2, } diff --git a/packages/hdwallet-native/src/crypto/classes/symmetricCryptoKey.test.ts b/packages/hdwallet-native/src/crypto/classes/symmetricCryptoKey.test.ts index 451431c88..ce04dc3d1 100644 --- a/packages/hdwallet-native/src/crypto/classes/symmetricCryptoKey.test.ts +++ b/packages/hdwallet-native/src/crypto/classes/symmetricCryptoKey.test.ts @@ -14,17 +14,15 @@ describe("symmetricCryptoKey", () => { ["encKey", [encKey]], ["macKey", [encKey, encKey]], ])("should require a parameter %s", (name: string, params: ArrayBuffer[]) => { - // @ts-ignore - expect(() => new SymmetricCryptoKey(...params)).toThrow("Required parameter"); + expect(() => new (SymmetricCryptoKey as any)(...params)).toThrow("Required parameter"); }); it.each([ ["key", [key64, encKey, macKey]], ["encKey", [key, key64, macKey]], ["macKey", [key, encKey, key64]], - ])("should throw an error if %s is not 32 bytes", (name, params) => { - // @ts-ignore - expect(() => new SymmetricCryptoKey(...params)).toThrow("Keys must be 32 bytes"); + ])("should throw an error if %s is not 32 bytes", (name: string, params: ArrayBuffer[]) => { + expect(() => new (SymmetricCryptoKey as any)(...params)).toThrow("Keys must be 32 bytes"); }); it("should return an instance", () => { diff --git a/packages/hdwallet-native/src/crypto/engines/web-crypto.test.ts b/packages/hdwallet-native/src/crypto/engines/web-crypto.test.ts index 16f91edfc..f0d425b2a 100644 --- a/packages/hdwallet-native/src/crypto/engines/web-crypto.test.ts +++ b/packages/hdwallet-native/src/crypto/engines/web-crypto.test.ts @@ -20,7 +20,6 @@ describe("WebCryptoEngine JavaScript", () => { const data = core.toArrayBuffer(utils.fromUtf8ToArray("test all seed phrase words for to see this to work maybe")); const key = core.toArrayBuffer(utils.fromUtf8ToArray("12345678901234561234567890123456")); const iv = core.toArrayBuffer(utils.fromUtf8ToArray("1234567890123456")); - const engine = new WebCryptoEngine(); const encrypted = await engine.encrypt(data, key, iv); @@ -71,7 +70,9 @@ describe("WebCryptoEngine JavaScript", () => { const iv = utils.fromB64ToArray("rnvfQhmCO27xxEk33ayinw=="); const encrypted = await engine.encrypt(mnemonic, key.encKey, iv); - expect(utils.fromBufferToB64(encrypted)).toEqual("FC2M6J3aqlavEne0Sl72Xyh3XB2RzxmNpy/zKNqu1ys+3Xe7pxyRQd+GRsLcf/Rf"); + expect(utils.fromBufferToB64(encrypted)).toEqual( + "FC2M6J3aqlavEne0Sl72Xyh3XB2RzxmNpy/zKNqu1ys+3Xe7pxyRQd+GRsLcf/Rf" + ); }); it("should decrypt a wallet with a password and email", async () => { @@ -90,7 +91,7 @@ describe("WebCryptoEngine JavaScript", () => { // Make sure we're not just returning an empty array of 0s // It's very unlikely that a random set of 32 bytes will result in all 0s const typedArray = new Uint8Array(bytes); - const sum = typedArray.reduce((sum, value) => sum + value, 0); + const sum = typedArray.reduce((acc, value) => acc + value, 0); expect(sum).toBeGreaterThan(0); }); diff --git a/packages/hdwallet-native/src/crypto/engines/web-crypto.ts b/packages/hdwallet-native/src/crypto/engines/web-crypto.ts index 655eaa6b4..4e0dde187 100644 --- a/packages/hdwallet-native/src/crypto/engines/web-crypto.ts +++ b/packages/hdwallet-native/src/crypto/engines/web-crypto.ts @@ -29,7 +29,11 @@ export class WebCryptoEngine implements CryptoEngine { return globalThis.crypto.subtle.sign(signingAlgorithm, impKey, value); } - public async pbkdf2(password: ArrayBuffer, salt: ArrayBuffer, options: Partial & Pick) { + public async pbkdf2( + password: ArrayBuffer, + salt: ArrayBuffer, + options: Partial & Pick + ) { const pbkdf2Params: Pbkdf2Params = { name: "PBKDF2", salt: new Uint8Array(salt), diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/binance.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/binance.ts index cbf1500c5..ff8981980 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/binance.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/binance.ts @@ -1,9 +1,10 @@ -import type { Transaction, BncClient } from "bnb-javascript-sdk-nobroadcast"; -import { BIP32, SecP256K1 } from "../core" +import type { BncClient, Transaction } from "bnb-javascript-sdk-nobroadcast"; + +import { BIP32, SecP256K1 } from "../core"; type SigningDelegate = Parameters[0]; -const crypto = (async () => (await import("bnb-javascript-sdk-nobroadcast")).crypto)() +const crypto = (async () => (await import("bnb-javascript-sdk-nobroadcast")).crypto)(); export default { async create(keyPair: BIP32.Node): Promise { @@ -14,5 +15,5 @@ export default { tx.addSignature(pubKey, sig); return tx; }; - } + }, }; diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/bip32.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/bip32.ts index ef6e64140..86d9c4849 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/bip32.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/bip32.ts @@ -1,14 +1,14 @@ +import type { crypto as btccrypto, Network, SignerAsync } from "@shapeshiftoss/bitcoinjs-lib"; import * as bip32 from "bip32"; import bs58check from "bs58check"; -import type { crypto as btccrypto, Network, SignerAsync } from "@shapeshiftoss/bitcoinjs-lib"; -import { BIP32, SecP256K1, IsolationError } from "../core"; +import { BIP32, IsolationError, SecP256K1 } from "../core"; import { ECPairAdapter } from "./bitcoin"; -let btccryptoInstance: typeof btccrypto | undefined +let btccryptoInstance: typeof btccrypto | undefined; const btccryptoReady = (async () => { - btccryptoInstance = (await import("@shapeshiftoss/bitcoinjs-lib")).crypto -})() + btccryptoInstance = (await import("@shapeshiftoss/bitcoinjs-lib")).crypto; +})(); export type BIP32InterfaceAsync = Omit & Pick & { @@ -27,7 +27,13 @@ export class BIP32Adapter extends ECPairAdapter implements BIP32InterfaceAsync { _identifier?: Buffer; _base58?: string; - protected constructor(node: BIP32.Node, chainCode: BIP32.ChainCode, publicKey: SecP256K1.CurvePoint, networkOrParent?: BIP32Adapter | Network, index?: number) { + protected constructor( + node: BIP32.Node, + chainCode: BIP32.ChainCode, + publicKey: SecP256K1.CurvePoint, + networkOrParent?: BIP32Adapter | Network, + index?: number + ) { super(node, publicKey, networkOrParent instanceof BIP32Adapter ? networkOrParent.network : networkOrParent); this.node = node; this._chainCode = chainCode; @@ -36,9 +42,19 @@ export class BIP32Adapter extends ECPairAdapter implements BIP32InterfaceAsync { if (networkOrParent instanceof BIP32Adapter) this._parent = networkOrParent; } - static async create(isolatedNode: BIP32.Node, networkOrParent?: BIP32Adapter | Network, index?: number): Promise { - await btccryptoReady - return new BIP32Adapter(isolatedNode, await isolatedNode.getChainCode(), await isolatedNode.getPublicKey(), networkOrParent, index); + static async create( + isolatedNode: BIP32.Node, + networkOrParent?: BIP32Adapter | Network, + index?: number + ): Promise { + await btccryptoReady; + return new BIP32Adapter( + isolatedNode, + await isolatedNode.getChainCode(), + await isolatedNode.getPublicKey(), + networkOrParent, + index + ); } get depth(): number { @@ -52,6 +68,7 @@ export class BIP32Adapter extends ECPairAdapter implements BIP32InterfaceAsync { } get identifier() { return (this._identifier = + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this._identifier ?? btccryptoInstance!.hash160(Buffer.from(SecP256K1.CompressedPoint.from(this.publicKey)))); } get fingerprint() { @@ -71,8 +88,7 @@ export class BIP32Adapter extends ECPairAdapter implements BIP32InterfaceAsync { } get publicKey() { - return Buffer.from(SecP256K1.CompressedPoint.from(this._publicKey)) as Buffer & - SecP256K1.CompressedPoint; + return Buffer.from(SecP256K1.CompressedPoint.from(this._publicKey)) as Buffer & SecP256K1.CompressedPoint; } getPublicKey() { return this.publicKey; diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/bitcoin.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/bitcoin.ts index 119c903a9..92af09781 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/bitcoin.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/bitcoin.ts @@ -1,82 +1,90 @@ -import type { ECPairInterface, Network, SignerAsync, crypto as bcrypto, networks } from "@shapeshiftoss/bitcoinjs-lib"; -import { SecP256K1, IsolationError } from "../core" +import type { crypto as bcrypto, ECPairInterface, Network, networks, SignerAsync } from "@shapeshiftoss/bitcoinjs-lib"; + +import { IsolationError, SecP256K1 } from "../core"; import { assertType, ByteArray } from "../types"; export type ECPairInterfaceAsync = Omit & Pick; -let networksInstance: typeof networks | undefined +let networksInstance: typeof networks | undefined; const networksReady = (async () => { - networksInstance = (await import("@shapeshiftoss/bitcoinjs-lib")).networks -})() + networksInstance = (await import("@shapeshiftoss/bitcoinjs-lib")).networks; +})(); export class ECPairAdapter implements SignerAsync, ECPairInterfaceAsync { - protected readonly _isolatedKey: SecP256K1.ECDSAKey; - readonly _publicKey: SecP256K1.CurvePoint; - readonly _network: Network | undefined; - compressed: boolean = false; - lowR: boolean = false; + protected readonly _isolatedKey: SecP256K1.ECDSAKey; + readonly _publicKey: SecP256K1.CurvePoint; + readonly _network: Network | undefined; + compressed = false; + lowR = false; - protected constructor(isolatedKey: SecP256K1.ECDSAKey, publicKey: SecP256K1.CurvePoint, network?: Network) { - this._isolatedKey = isolatedKey; - this._publicKey = publicKey; - this._network = network; - } + protected constructor(isolatedKey: SecP256K1.ECDSAKey, publicKey: SecP256K1.CurvePoint, network?: Network) { + this._isolatedKey = isolatedKey; + this._publicKey = publicKey; + this._network = network; + } - static async create(isolatedKey: SecP256K1.ECDSAKey, network?: Network): Promise { - await networksReady - return new ECPairAdapter(isolatedKey, await isolatedKey.getPublicKey(), network); - } + static async create(isolatedKey: SecP256K1.ECDSAKey, network?: Network): Promise { + await networksReady; + return new ECPairAdapter(isolatedKey, await isolatedKey.getPublicKey(), network); + } - get network() { - return this._network ?? networksInstance!.bitcoin; - } + get network() { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this._network ?? networksInstance!.bitcoin; + } - get ecdsaSign() { - return this._isolatedKey.ecdsaSign.bind(this._isolatedKey); - } + get ecdsaSign() { + return this._isolatedKey.ecdsaSign.bind(this._isolatedKey); + } - get ecdh() { - const isolatedKey = this._isolatedKey as object as Record; - if (!("ecdh" in isolatedKey && typeof isolatedKey.ecdh === "function")) return undefined; - return isolatedKey.ecdh.bind(isolatedKey); - } + get ecdh() { + const isolatedKey = this._isolatedKey as object as Record; + if (!("ecdh" in isolatedKey && typeof isolatedKey.ecdh === "function")) return undefined; + return isolatedKey.ecdh.bind(isolatedKey); + } - get ecdhRaw() { - const isolatedKey = this._isolatedKey as object as Record; - if (!("ecdhRaw" in isolatedKey && typeof isolatedKey.ecdhRaw === "function")) return undefined; - return isolatedKey.ecdhRaw.bind(isolatedKey); - } + get ecdhRaw() { + const isolatedKey = this._isolatedKey as object as Record; + if (!("ecdhRaw" in isolatedKey && typeof isolatedKey.ecdhRaw === "function")) return undefined; + return isolatedKey.ecdhRaw.bind(isolatedKey); + } - async sign(hash: bcrypto.NonDigest | bcrypto.Digest<"hash256">, lowR?: boolean): Promise { - assertType(ByteArray(), hash); + async sign(hash: bcrypto.NonDigest | bcrypto.Digest<"hash256">, lowR?: boolean): Promise { + assertType(ByteArray(), hash); - lowR = lowR ?? this.lowR; - const sig = await(async () => { - if (!hash.algorithm) { - assertType(ByteArray(32), hash); - return !lowR - ? await this._isolatedKey.ecdsaSign(null, hash) - : await SecP256K1.Signature.signCanonically(this._isolatedKey, null, hash); - } else { - return !lowR - ? await this._isolatedKey.ecdsaSign(hash.algorithm, hash.preimage) - : await SecP256K1.Signature.signCanonically(this._isolatedKey, hash.algorithm, hash.preimage); - } - })(); - return Buffer.from(sig); - } - get publicKey() { return this.getPublicKey(); } - getPublicKey() { - const publicKey = this._publicKey; - const key = (this.compressed ? SecP256K1.CompressedPoint.from(publicKey) : SecP256K1.UncompressedPoint.from(publicKey)); - return Buffer.from(key) as Buffer & SecP256K1.CurvePoint; - } + lowR = lowR ?? this.lowR; + const sig = await (async () => { + if (!hash.algorithm) { + assertType(ByteArray(32), hash); + return !lowR + ? await this._isolatedKey.ecdsaSign(null, hash) + : await SecP256K1.Signature.signCanonically(this._isolatedKey, null, hash); + } else { + return !lowR + ? await this._isolatedKey.ecdsaSign(hash.algorithm, hash.preimage) + : await SecP256K1.Signature.signCanonically(this._isolatedKey, hash.algorithm, hash.preimage); + } + })(); + return Buffer.from(sig); + } + get publicKey() { + return this.getPublicKey(); + } + getPublicKey() { + const publicKey = this._publicKey; + const key = this.compressed + ? SecP256K1.CompressedPoint.from(publicKey) + : SecP256K1.UncompressedPoint.from(publicKey); + return Buffer.from(key) as Buffer & SecP256K1.CurvePoint; + } - toWIF(): never { throw new IsolationError("WIF"); } - verify(hash: Uint8Array, signature: Uint8Array) { - SecP256K1.Signature.assert(signature); - return SecP256K1.Signature.verify(signature, null, hash, this._publicKey); - } + toWIF(): never { + throw new IsolationError("WIF"); + } + verify(hash: Uint8Array, signature: Uint8Array) { + SecP256K1.Signature.assert(signature); + return SecP256K1.Signature.verify(signature, null, hash, this._publicKey); + } } export default ECPairAdapter; diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/cosmos.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/cosmos.ts index 256dcb492..bef1385b2 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/cosmos.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/cosmos.ts @@ -1,26 +1,27 @@ +import * as Isolation from "../"; import { SecP256K1 } from "../core"; export class WalletAdapter { - protected readonly _isolatedKey: SecP256K1.ECDSAKey; - readonly _publicKey: SecP256K1.CurvePoint; + protected readonly _isolatedKey: SecP256K1.ECDSAKey | Isolation.Adapters.BIP32; + readonly _publicKey: SecP256K1.CurvePoint; - protected constructor(isolatedKey: SecP256K1.ECDSAKey, publicKey: SecP256K1.CurvePoint) { - this._isolatedKey = isolatedKey; - this._publicKey = publicKey; - } + protected constructor(isolatedKey: SecP256K1.ECDSAKey | Isolation.Adapters.BIP32, publicKey: SecP256K1.CurvePoint) { + this._isolatedKey = isolatedKey; + this._publicKey = publicKey; + } - static async create(isolatedKey: SecP256K1.ECDSAKey): Promise { - return new WalletAdapter(isolatedKey, await isolatedKey.getPublicKey()) - } + static async create(isolatedKey: SecP256K1.ECDSAKey | Isolation.Adapters.BIP32): Promise { + return new WalletAdapter(isolatedKey, await isolatedKey.getPublicKey()); + } - get publicKey(): string { - return Buffer.from(this._publicKey).toString("hex"); - } + get publicKey(): string { + return Buffer.from(this._publicKey).toString("hex"); + } - async sign(signMessage: string): Promise { - const signBuf = Buffer.from(signMessage.normalize("NFKD"), "utf8"); - return Buffer.from(await this._isolatedKey.ecdsaSign("sha256", signBuf)); - } + async sign(signMessage: string): Promise { + const signBuf = Buffer.from(signMessage.normalize("NFKD"), "utf8"); + return Buffer.from(await this._isolatedKey.ecdsaSign("sha256", signBuf)); + } } export default WalletAdapter; diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/cosmosDirect.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/cosmosDirect.ts index 15ae6fd52..f47865213 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/cosmosDirect.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/cosmosDirect.ts @@ -1,4 +1,4 @@ -import type { AccountData, OfflineDirectSigner, DirectSignResponse } from "@cosmjs/proto-signing"; +import type { AccountData, DirectSignResponse, OfflineDirectSigner } from "@cosmjs/proto-signing"; import * as bech32 from "bech32"; import type { SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx"; @@ -46,9 +46,9 @@ export class OfflineDirectSignerAdapter implements OfflineDirectSigner { signature: { pub_key: { type: "tendermint/PubKeySecp256k1", - value: Buffer.from(this._pubkey).toString("base64") + value: Buffer.from(this._pubkey).toString("base64"), }, - signature: Buffer.from(signatureBytes).toString("base64") + signature: Buffer.from(signatureBytes).toString("base64"), }, }; } diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts index 99b767c0f..8b8cfdad1 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts @@ -1,4 +1,4 @@ -import * as core from "@shapeshiftoss/hdwallet-core" +import * as core from "@shapeshiftoss/hdwallet-core"; import * as ethers from "ethers"; import { SecP256K1 } from "../core"; @@ -11,7 +11,7 @@ function ethSigFromRecoverableSig(x: SecP256K1.RecoverableSignature): ethers.Sig export class SignerAdapter extends ethers.Signer { protected readonly _isolatedKey: SecP256K1.ECDSAKey; - readonly provider?: ethers.providers.Provider + readonly provider?: ethers.providers.Provider; protected constructor(isolatedKey: SecP256K1.ECDSAKey, provider?: ethers.providers.Provider) { super(); @@ -20,15 +20,16 @@ export class SignerAdapter extends ethers.Signer { } static async create(isolatedKey: SecP256K1.ECDSAKey, provider?: ethers.providers.Provider): Promise { - return new SignerAdapter(isolatedKey, provider) + return new SignerAdapter(isolatedKey, provider); } // This throws (as allowed by ethers.Signer) to avoid having to return an object which is initialized asynchronously // from a synchronous function. Because all the (other) methods on SignerAdapter are async, one could construct a // wrapper that deferred its initialization and awaited it before calling through to a "real" method, but that's // a lot of complexity just to implement this one method we don't actually use. + // eslint-disable-next-line @typescript-eslint/no-unused-vars connect(_provider: ethers.providers.Provider): never { - throw new Error("changing providers on a SignerAdapter is unsupported") + throw new Error("changing providers on a SignerAdapter is unsupported"); } async getAddress(): Promise { @@ -36,7 +37,11 @@ export class SignerAdapter extends ethers.Signer { } async signDigest(digest: ethers.BytesLike): Promise { - const recoverableSig = await SecP256K1.RecoverableSignature.signCanonically(this._isolatedKey, null, digest instanceof Uint8Array ? digest : ethers.utils.arrayify(digest)); + const recoverableSig = await SecP256K1.RecoverableSignature.signCanonically( + this._isolatedKey, + null, + digest instanceof Uint8Array ? digest : ethers.utils.arrayify(digest) + ); const sig = SecP256K1.RecoverableSignature.sig(recoverableSig); const recoveryParam = SecP256K1.RecoverableSignature.recoveryParam(recoverableSig); return ethers.utils.splitSignature(core.compatibleBufferConcat([sig, Buffer.from([recoveryParam])])); @@ -45,7 +50,7 @@ export class SignerAdapter extends ethers.Signer { async signTransaction(transaction: ethers.utils.Deferrable): Promise { const tx = await ethers.utils.resolveProperties(transaction); if (tx.from != null) { - if (ethers.utils.getAddress(tx.from) !== await this.getAddress()) { + if (ethers.utils.getAddress(tx.from) !== (await this.getAddress())) { throw new Error("transaction from address mismatch"); } delete tx.from; @@ -53,7 +58,7 @@ export class SignerAdapter extends ethers.Signer { const unsignedTx: ethers.UnsignedTransaction = { ...tx, nonce: tx.nonce !== undefined ? ethers.BigNumber.from(tx.nonce).toNumber() : undefined, - } + }; const txBuf = ethers.utils.arrayify(ethers.utils.serializeTransaction(unsignedTx)); const rawSig = await SecP256K1.RecoverableSignature.signCanonically(this._isolatedKey, "keccak256", txBuf); @@ -65,7 +70,10 @@ export class SignerAdapter extends ethers.Signer { typeof messageData === "string" ? Buffer.from(messageData.normalize("NFKD"), "utf8") : Buffer.from(ethers.utils.arrayify(messageData)); - const messageBuf = core.compatibleBufferConcat([Buffer.from(`\x19Ethereum Signed Message:\n${messageDataBuf.length}`, "utf8"), messageDataBuf]); + const messageBuf = core.compatibleBufferConcat([ + Buffer.from(`\x19Ethereum Signed Message:\n${messageDataBuf.length}`, "utf8"), + messageDataBuf, + ]); const rawSig = await SecP256K1.RecoverableSignature.signCanonically(this._isolatedKey, "keccak256", messageBuf); return ethers.utils.joinSignature(ethSigFromRecoverableSig(rawSig)); } diff --git a/packages/hdwallet-native/src/crypto/isolation/adapters/fio.ts b/packages/hdwallet-native/src/crypto/isolation/adapters/fio.ts index 6e750a21b..5264306bb 100644 --- a/packages/hdwallet-native/src/crypto/isolation/adapters/fio.ts +++ b/packages/hdwallet-native/src/crypto/isolation/adapters/fio.ts @@ -1,46 +1,52 @@ import { ExternalPrivateKey as FIOExternalPrivateKey } from "@shapeshiftoss/fiojs"; -import * as core from "@shapeshiftoss/hdwallet-core" -import bs58 from "bs58" +import * as core from "@shapeshiftoss/hdwallet-core"; +import bs58 from "bs58"; import { SecP256K1 } from "../core"; import * as Digest from "../core/digest"; import { CurvePoint, RecoverableSignature } from "../core/secp256k1"; import { checkType } from "../types"; -function bs58FioEncode(raw: Uint8Array, keyType: string = ""): string { - const typeBuf = Buffer.from(keyType, "utf8"); - const checksum = Digest.Algorithms["ripemd160"](core.compatibleBufferConcat([raw, typeBuf])).slice(0, 4); - return bs58.encode(core.compatibleBufferConcat([raw, checksum])); +function bs58FioEncode(raw: Uint8Array, keyType = ""): string { + const typeBuf = Buffer.from(keyType, "utf8"); + const checksum = Digest.Algorithms["ripemd160"](core.compatibleBufferConcat([raw, typeBuf])).slice(0, 4); + return bs58.encode(core.compatibleBufferConcat([raw, checksum])); } type IsolatedKey = SecP256K1.ECDSAKey & SecP256K1.ECDHKey; export class ExternalSignerAdapter implements FIOExternalPrivateKey { - protected readonly _isolatedKey: IsolatedKey; - readonly _publicKey: CurvePoint; - - protected constructor(isolatedKey: IsolatedKey, publicKey: CurvePoint) { - this._isolatedKey = isolatedKey; - this._publicKey = publicKey; - } - - static async create(isolatedKey: IsolatedKey): Promise { - return new ExternalSignerAdapter(isolatedKey, await isolatedKey.getPublicKey()) - } - - get publicKey(): string { - const raw = SecP256K1.CompressedPoint.from(this._publicKey) - return `FIO${bs58FioEncode(raw)}`; - } - - async sign(signBuf: Uint8Array): Promise { - const sig = await SecP256K1.RecoverableSignature.signCanonically(this._isolatedKey, "sha256", signBuf); - const fioSigBuf = core.compatibleBufferConcat([Buffer.from([RecoverableSignature.recoveryParam(sig) + 4 + 27]), SecP256K1.RecoverableSignature.r(sig), SecP256K1.RecoverableSignature.s(sig)]); - return `SIG_K1_${bs58FioEncode(fioSigBuf, "K1")}`; - } - - async getSharedSecret(publicKey: any): Promise { - return Buffer.from(Digest.Algorithms["sha512"](await this._isolatedKey.ecdh(checkType(SecP256K1.CurvePoint, publicKey.toBuffer())))); - } + protected readonly _isolatedKey: IsolatedKey; + readonly _publicKey: CurvePoint; + + protected constructor(isolatedKey: IsolatedKey, publicKey: CurvePoint) { + this._isolatedKey = isolatedKey; + this._publicKey = publicKey; + } + + static async create(isolatedKey: IsolatedKey): Promise { + return new ExternalSignerAdapter(isolatedKey, await isolatedKey.getPublicKey()); + } + + get publicKey(): string { + const raw = SecP256K1.CompressedPoint.from(this._publicKey); + return `FIO${bs58FioEncode(raw)}`; + } + + async sign(signBuf: Uint8Array): Promise { + const sig = await SecP256K1.RecoverableSignature.signCanonically(this._isolatedKey, "sha256", signBuf); + const fioSigBuf = core.compatibleBufferConcat([ + Buffer.from([RecoverableSignature.recoveryParam(sig) + 4 + 27]), + SecP256K1.RecoverableSignature.r(sig), + SecP256K1.RecoverableSignature.s(sig), + ]); + return `SIG_K1_${bs58FioEncode(fioSigBuf, "K1")}`; + } + + async getSharedSecret(publicKey: any): Promise { + return Buffer.from( + Digest.Algorithms["sha512"](await this._isolatedKey.ecdh(checkType(SecP256K1.CurvePoint, publicKey.toBuffer()))) + ); + } } export default ExternalSignerAdapter; diff --git a/packages/hdwallet-native/src/crypto/isolation/core/bip32/index.ts b/packages/hdwallet-native/src/crypto/isolation/core/bip32/index.ts index 7ddc8a699..6ba900354 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/bip32/index.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/bip32/index.ts @@ -2,31 +2,29 @@ export * from "./types"; export * from "./interfaces"; import { Path } from "./types"; -import { Node } from "./interfaces"; interface Derivable { - derive(index: number): Promise + derive(index: number): Promise; } export async function derivePath(node: T, path: Path): Promise { - // This logic is copied (almost) wholesale from the bip32 package. - Path.assert(path); + // This logic is copied (almost) wholesale from the bip32 package. + Path.assert(path); - let splitPath = path.split('/'); - if (splitPath[0] === 'm') { - splitPath = splitPath.slice(1); + let splitPath = path.split("/"); + if (splitPath[0] === "m") { + splitPath = splitPath.slice(1); + } + const endIndex = splitPath.lastIndexOf(""); + if (endIndex >= 0) splitPath = splitPath.slice(0, endIndex); + return await splitPath.reduce(async (prevHd, indexStr) => { + let index; + if (indexStr.slice(-1) === `'`) { + index = parseInt(indexStr.slice(0, -1), 10); + return (await prevHd).derive(index + 0x80000000); + } else { + index = parseInt(indexStr, 10); + return (await prevHd).derive(index); } - const endIndex = splitPath.lastIndexOf(""); - if (endIndex >= 0) splitPath = splitPath.slice(0, endIndex); - return await splitPath.reduce(async (prevHd, indexStr) => { - let index; - if (indexStr.slice(-1) === `'`) { - index = parseInt(indexStr.slice(0, -1), 10); - return (await prevHd).derive(index + 0x80000000); - } - else { - index = parseInt(indexStr, 10); - return (await prevHd).derive(index); - } - }, Promise.resolve(node)); + }, Promise.resolve(node)); } diff --git a/packages/hdwallet-native/src/crypto/isolation/core/bip32/interfaces.ts b/packages/hdwallet-native/src/crypto/isolation/core/bip32/interfaces.ts index b00d6f2cb..4cb873000 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/bip32/interfaces.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/bip32/interfaces.ts @@ -1,19 +1,19 @@ import * as core from "@shapeshiftoss/hdwallet-core"; +import { Revocable } from ".."; import * as SecP256K1 from "../secp256k1"; import { ChainCode } from "."; -import { Revocable } from ".."; export interface Seed extends Partial { - toMasterKey(hmacKey?: string | Uint8Array): Promise; + toMasterKey(hmacKey?: string | Uint8Array): Promise; } export interface Node extends Partial, SecP256K1.ECDSAKey, Partial { - getPublicKey(): Promise; - getChainCode(): Promise; - derive(index: number): Promise; + getPublicKey(): Promise; + getChainCode(): Promise; + derive(index: number): Promise; } export function nodeSupportsECDH(x: T): x is T & SecP256K1.ECDHKey { - return core.isIndexable(x) && "ecdh" in x && typeof x.ecdh === "function"; + return core.isIndexable(x) && "ecdh" in x && typeof x.ecdh === "function"; } diff --git a/packages/hdwallet-native/src/crypto/isolation/core/bip32/types.ts b/packages/hdwallet-native/src/crypto/isolation/core/bip32/types.ts index 2feb4e14d..c6368160f 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/bip32/types.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/bip32/types.ts @@ -1,4 +1,5 @@ import { Static } from "funtypes"; + import { BoundedString, ByteArray } from "../../types"; const chainCodeBase = ByteArray(32); diff --git a/packages/hdwallet-native/src/crypto/isolation/core/bip39/interfaces.ts b/packages/hdwallet-native/src/crypto/isolation/core/bip39/interfaces.ts index 03cdddafd..db765accb 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/bip39/interfaces.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/bip39/interfaces.ts @@ -2,5 +2,5 @@ import { Revocable } from ".."; import * as BIP32 from "../bip32"; export interface Mnemonic extends Partial { - toSeed(passphrase?: string): Promise; + toSeed(passphrase?: string): Promise; } diff --git a/packages/hdwallet-native/src/crypto/isolation/core/digest/algorithms.ts b/packages/hdwallet-native/src/crypto/isolation/core/digest/algorithms.ts index 12a8874c5..010cb792d 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/digest/algorithms.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/digest/algorithms.ts @@ -1,50 +1,57 @@ import CryptoJS from "crypto-js"; -import { AlgorithmName, Algorithm } from "./types"; +import { Algorithm, AlgorithmName } from "./types"; export const AlgorithmLength = { - "sha1": 20, - "ripemd160": 20, - "hash160": 20, - "sha256": 32, - "hash256": 32, - "keccak256": 32, - "sha512": 64, + sha1: 20, + ripemd160: 20, + hash160: 20, + sha256: 32, + hash256: 32, + keccak256: 32, + sha512: 64, } as const; export function toWordArray(x: Uint8Array): CryptoJS.lib.WordArray { - // TODO: avoid this conversion - return CryptoJS.enc.Hex.parse(Buffer.from(x).toString("hex")); - // return (CryptoJS.lib.WordArray.create as unknown as (x: Uint8Array) => CryptoJS.lib.WordArray)(x); + // TODO: avoid this conversion + return CryptoJS.enc.Hex.parse(Buffer.from(x).toString("hex")); + // return (CryptoJS.lib.WordArray.create as unknown as (x: Uint8Array) => CryptoJS.lib.WordArray)(x); } export function fromWordArray(x: CryptoJS.lib.WordArray): Uint8Array { - // TODO: avoid this conversion - return Buffer.from(CryptoJS.enc.Hex.stringify(x), "hex"); - // return Buffer.alloc(x.sigBytes).map((_, i) => (x.words[i >>> 2] >>> (32 - (((i + 1) & 0x03) << 3))) & 0xff); + // TODO: avoid this conversion + return Buffer.from(CryptoJS.enc.Hex.stringify(x), "hex"); + // return Buffer.alloc(x.sigBytes).map((_, i) => (x.words[i >>> 2] >>> (32 - (((i + 1) & 0x03) << 3))) & 0xff); } export function _initializeAlgorithms(register: (name: N, fn: Algorithm) => void) { - // Using an "any" return value overrides static type checking of the length of the digest. This - // is OK because there's no ambiguity as to what it should be and it will be checked at runtime. + // Using an "any" return value overrides static type checking of the length of the digest. This + // is OK because there's no ambiguity as to what it should be and it will be checked at runtime. - try { - const crypto = require("crypto"); - register("sha1", (x): any => crypto.createHash("sha1").update(x).digest()); - register("ripemd160", (x): any => crypto.createHash("ripemd160").update(x).digest()); - register("hash160", (x): any => crypto.createHash("ripemd160").update(crypto.createHash("sha256").update(x).digest()).digest()); - register("sha256", (x): any => crypto.createHash("sha256").update(x).digest()); - register("hash256", (x): any => crypto.createHash("sha256").update(crypto.createHash("sha256").update(x).digest()).digest()); - // register("keccak256", (x): any => crypto.createHash("sha3-256").update(x).digest()); - register("keccak256", (x): any => fromWordArray(CryptoJS.SHA3(toWordArray(x), {outputLength: 256}))); - register("sha512", (x): any => crypto.createHash("sha512").update(x).digest()); - } catch { - register("sha1", (x): any => fromWordArray(CryptoJS.SHA1(toWordArray(x)))); - register("ripemd160", (x): any => fromWordArray(CryptoJS.RIPEMD160(toWordArray(x)))); - register("hash160", (x): any => fromWordArray(CryptoJS.RIPEMD160(CryptoJS.SHA256(toWordArray(x))))); - register("sha256", (x): any => fromWordArray(CryptoJS.SHA256(toWordArray(x)))); - register("hash256", (x): any => fromWordArray(CryptoJS.SHA256(CryptoJS.SHA256(toWordArray(x))))); - register("keccak256", (x): any => fromWordArray(CryptoJS.SHA3(toWordArray(x), {outputLength: 256}))); - register("sha512", (x): any => fromWordArray(CryptoJS.SHA512(toWordArray(x)))); - } + try { + // (Can't use a dynamic import here, because the return is needed synchronously; can't use a static import, + // because we need to fall back tp CryptoJS in browsers) + // eslint-disable-next-line @typescript-eslint/no-var-requires + const crypto = require("crypto"); + register("sha1", (x): any => crypto.createHash("sha1").update(x).digest()); + register("ripemd160", (x): any => crypto.createHash("ripemd160").update(x).digest()); + register("hash160", (x): any => + crypto.createHash("ripemd160").update(crypto.createHash("sha256").update(x).digest()).digest() + ); + register("sha256", (x): any => crypto.createHash("sha256").update(x).digest()); + register("hash256", (x): any => + crypto.createHash("sha256").update(crypto.createHash("sha256").update(x).digest()).digest() + ); + // register("keccak256", (x): any => crypto.createHash("sha3-256").update(x).digest()); + register("keccak256", (x): any => fromWordArray(CryptoJS.SHA3(toWordArray(x), { outputLength: 256 }))); + register("sha512", (x): any => crypto.createHash("sha512").update(x).digest()); + } catch { + register("sha1", (x): any => fromWordArray(CryptoJS.SHA1(toWordArray(x)))); + register("ripemd160", (x): any => fromWordArray(CryptoJS.RIPEMD160(toWordArray(x)))); + register("hash160", (x): any => fromWordArray(CryptoJS.RIPEMD160(CryptoJS.SHA256(toWordArray(x))))); + register("sha256", (x): any => fromWordArray(CryptoJS.SHA256(toWordArray(x)))); + register("hash256", (x): any => fromWordArray(CryptoJS.SHA256(CryptoJS.SHA256(toWordArray(x))))); + register("keccak256", (x): any => fromWordArray(CryptoJS.SHA3(toWordArray(x), { outputLength: 256 }))); + register("sha512", (x): any => fromWordArray(CryptoJS.SHA512(toWordArray(x)))); + } } diff --git a/packages/hdwallet-native/src/crypto/isolation/core/digest/index.test.ts b/packages/hdwallet-native/src/crypto/isolation/core/digest/index.test.ts index a752b4e36..c60377704 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/digest/index.test.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/digest/index.test.ts @@ -1,5 +1,5 @@ import { Algorithms } from "."; -import { toWordArray, fromWordArray } from "./algorithms"; +import { fromWordArray, toWordArray } from "./algorithms"; describe("Digests", () => { it("converts to/from WordArrays correctly", () => { @@ -9,7 +9,7 @@ describe("Digests", () => { const bar = fromWordArray(foo); const output = Buffer.from(bar); expect(output.toString("hex")).toEqual(inHex); - }) + }); it.each([ ["SHA-1", "sha1", "616263", "a9993e364706816aba3e25717850c26c9cd0d89d"], ["RIPEMD160", "ripemd160", "616263", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"], @@ -17,12 +17,17 @@ describe("Digests", () => { ["SHA-256", "sha256", "616263", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"], ["HASH256", "hash256", "616263", "4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358"], ["Keccak-256", "keccak256", "616263", "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"], - ["SHA-512", "sha512", "616263", "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"], + [ + "SHA-512", + "sha512", + "616263", + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", + ], ] as const)("correctly implements %s", (_, algName, inHex, outHex) => { const input = Buffer.from(inHex, "hex"); console.time(algName); const output = (() => { - for (let i = 0; true; i++) { + for (let i = 0; ; i++) { const foo = Algorithms[algName](input); if (i === 1000) return foo; } diff --git a/packages/hdwallet-native/src/crypto/isolation/core/digest/types.ts b/packages/hdwallet-native/src/crypto/isolation/core/digest/types.ts index bc29a6c67..1381521c1 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/digest/types.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/digest/types.ts @@ -1,47 +1,59 @@ -import { Literal, Object as Obj, Never, Enum, Contract, Runtype } from "funtypes" -import { ByteArray, NonNegativeInteger} from "../../types"; -import { AlgorithmLength, _initializeAlgorithms } from "./algorithms"; +import { Contract, Enum, Literal, Never, Object as Obj, Runtype } from "funtypes"; -import { checkType } from "../../types" +import { ByteArray, NonNegativeInteger } from "../../types"; +import { checkType } from "../../types"; +import { _initializeAlgorithms, AlgorithmLength } from "./algorithms"; // These names come from the keys on the AlgorithmLength object. We'd prefer not // to repeat them here, which means we have to build the Enum object in a way that // type inference can't follow. Luckily, because the names are statically known, // we can assert the type even though it can't be inferred. -function algorithmNameBase(length?: L): Enum<{[N in AlgorithmName]: N}> { - return Enum(`AlgorithmName(${length ?? ""})`, Object.entries(AlgorithmLength) - .filter(x => length === undefined || length === x[1]) - .map(x => x[0]) - .reduce((a, x) => Object.assign(a, {[x]: x}), {}) - ) as any; -}; +function algorithmNameBase( + length?: L +): Enum<{ [N in AlgorithmName]: N }> { + return Enum( + `AlgorithmName(${length ?? ""})`, + Object.entries(AlgorithmLength) + .filter((x) => length === undefined || length === x[1]) + .map((x) => x[0]) + .reduce((a, x) => Object.assign(a, { [x]: x }), {}) + ) as any; +} // This can't be inline (or use generic type bounds) because it needs to distributed over the members of a union type. -type algorithmNameInner = K extends (keyof typeof AlgorithmLength) ? ((typeof AlgorithmLength[K]) extends L ? K : never) : never; +type algorithmNameInner = K extends keyof typeof AlgorithmLength + ? typeof AlgorithmLength[K] extends L + ? K + : never + : never; // The generic parameter is optional, and will restrict the type to algorithm names whose entries on AlgorithmLength match the specified length. -export type AlgorithmName = L extends undefined ? (keyof typeof AlgorithmLength) : algorithmNameInner<(keyof typeof AlgorithmLength), L>; +export type AlgorithmName = L extends undefined + ? keyof typeof AlgorithmLength + : algorithmNameInner; const algorithmNameStatic = { - forEach(callbackfn: (value: AlgorithmName, index: number, array: AlgorithmName[]) => void, thisarg?: any) { - (Object.keys(AlgorithmName().enumObject) as AlgorithmName[]).forEach(callbackfn, thisarg) - }, + forEach(callbackfn: (value: AlgorithmName, index: number, array: AlgorithmName[]) => void, thisarg?: any) { + (Object.keys(AlgorithmName().enumObject) as AlgorithmName[]).forEach(callbackfn, thisarg); + }, }; const algorithmName = Object.assign(algorithmNameBase, algorithmNameBase(), algorithmNameStatic); export const AlgorithmName: typeof algorithmName = algorithmName; function specificUnverifiedDigest(name: N): Runtype> { - return Obj({ - preimage: ByteArray(), - algorithm: Literal(name), - }).And(ByteArray(AlgorithmLength[name])) as any; + return Obj({ + preimage: ByteArray(), + algorithm: Literal(name), + }).And(ByteArray(AlgorithmLength[name])) as any; } function unverifiedDigestBase(name?: N): Runtype> { - if (name !== undefined) return specificUnverifiedDigest(name); - return (Object.keys(AlgorithmName.enumObject) as AlgorithmName[]).reduce((a: Runtype, x: AlgorithmName) => { - return a.Or(specificUnverifiedDigest(x)); - }, Never as Runtype); + if (name !== undefined) return specificUnverifiedDigest(name); + return (Object.keys(AlgorithmName.enumObject) as AlgorithmName[]).reduce((a: Runtype, x: AlgorithmName) => { + return a.Or(specificUnverifiedDigest(x)); + }, Never as Runtype); } // This can't be inline (or use generic type bounds) because it needs to distributed over the members of a union type. -type unverifiedDigestInner = N extends (keyof typeof AlgorithmLength) ? (ByteArray & {preimage: ByteArray, algorithm: N}) : never; +type unverifiedDigestInner = N extends keyof typeof AlgorithmLength + ? ByteArray & { preimage: ByteArray; algorithm: N } + : never; // The generic parameter is optional, and will restrict the type to digests with matching length/name combinations. type UnverifiedDigest = unverifiedDigestInner; const unverifiedDigestStatic = {}; @@ -51,20 +63,12 @@ const unverifiedDigest = Object.assign(unverifiedDigestBase, ByteArray, unverifi // actual algorithm functions we're wrapping should be able to use it. const UnverifiedDigest: typeof unverifiedDigest = unverifiedDigest; -function digestBase(name?: AlgorithmName) { - return UnverifiedDigest(name).withConstraint( - x => ByteArray.equal(x, Algorithms[x.algorithm](x.preimage)) || `expected ${x} to equal the ${x.algorithm} digest of ${x.preimage}`, - {name: `Digest(${name})`}, - ); -} export type Digest = UnverifiedDigest; const digestStatic = {}; -const digest = Object.assign(digestBase, UnverifiedDigest, digestStatic); -export const Digest: typeof digest = digest; // We use UnverifiedDigest instead of Digest in the contract because the result is implicitly trusted. -function algorithmBase(name: N){ - return Contract([ByteArray()], UnverifiedDigest(name)); +function algorithmBase(name: N) { + return Contract([ByteArray()], UnverifiedDigest(name)); } export type Algorithm = (_: ByteArray) => Digest; const algorithmStatic = {}; @@ -72,26 +76,38 @@ const algorithm = Object.assign(algorithmBase, algorithmStatic); // This isn't exported; only the registration function below should be using it. const Algorithm: typeof algorithm = algorithm; -export const Algorithms = (()=>{ - const algorithms = {} as { - [Property in keyof typeof AlgorithmLength]: Algorithm; - }; +export const Algorithms = (() => { + const algorithms = {} as { + [Property in keyof typeof AlgorithmLength]: Algorithm; + }; - _initializeAlgorithms((name: N, fn: Algorithm) => { - AlgorithmName.assert(name); - if (name in algorithms) throw new Error(`digest algorithm implementation already provided for ${name}`); - algorithms[name] = Algorithm(name).enforce((x: ByteArray) => { - const out = checkType(ByteArray(AlgorithmLength[name]), fn(x)) as Partial>; - out.preimage = x; - out.algorithm = name; - return checkType(UnverifiedDigest(name), out); - }) as Algorithm; - }); + _initializeAlgorithms((name: N, fn: Algorithm) => { + AlgorithmName.assert(name); + if (name in algorithms) throw new Error(`digest algorithm implementation already provided for ${name}`); + algorithms[name] = Algorithm(name).enforce((x: ByteArray) => { + const out = checkType(ByteArray(AlgorithmLength[name]), fn(x)) as Partial>; + out.preimage = x; + out.algorithm = name; + return checkType(UnverifiedDigest(name), out); + }) as Algorithm; + }); - Object.freeze(algorithms); - AlgorithmName.forEach(x => { - if (!algorithms[x]) throw new Error(`digest algorithm implementation missing for ${x}`) - }); + Object.freeze(algorithms); + AlgorithmName.forEach((x) => { + if (!algorithms[x]) throw new Error(`digest algorithm implementation missing for ${x}`); + }); - return algorithms; + return algorithms; })(); + +function digestBase(name?: AlgorithmName) { + return UnverifiedDigest(name).withConstraint( + (x) => + ByteArray.equal(x, Algorithms[x.algorithm](x.preimage)) || + `expected ${x} to equal the ${x.algorithm} digest of ${x.preimage}`, + { name: `Digest(${name})` } + ); +} + +const digest = Object.assign(digestBase, UnverifiedDigest, digestStatic); +export const Digest: typeof digest = digest; diff --git a/packages/hdwallet-native/src/crypto/isolation/core/index.ts b/packages/hdwallet-native/src/crypto/isolation/core/index.ts index 6b4276e8f..696c7fc9e 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/index.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/index.ts @@ -4,12 +4,12 @@ export * as Digest from "./digest"; export * as SecP256K1 from "./secp256k1"; export class IsolationError extends Error { - constructor(name: string) { - super(`this key is isolated -- no ${name} for you!`); - } + constructor(name: string) { + super(`this key is isolated -- no ${name} for you!`); + } } export interface Revocable { - revoke(): void - addRevoker(revoke: () => void): void + revoke(): void; + addRevoker(revoke: () => void): void; } diff --git a/packages/hdwallet-native/src/crypto/isolation/core/secp256k1/interfaces.ts b/packages/hdwallet-native/src/crypto/isolation/core/secp256k1/interfaces.ts index 3c3fb28c2..b0d635698 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/secp256k1/interfaces.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/secp256k1/interfaces.ts @@ -1,39 +1,58 @@ import { ByteArray, Uint32 } from "../../types"; -import { CurvePoint, RecoverableSignature, Signature } from "./types"; import * as Digest from "../digest"; +import { CurvePoint, RecoverableSignature, Signature } from "./types"; export interface ECDSAKey { - getPublicKey(): Promise; + getPublicKey(): Promise; - // Signatures MUST use a determinsitic nonce generation algorithm, which SHOULD be the one specified in RFC6979. The - // counter parameter is used to generate multiple distinct signatures over the same message, and SHOULD be included as - // additional entropy in the nonce generation algorithm (typically after convertion to a 32-byte big-endian integer). - // - // This can be used, for example, to find a signature whose r-value does not have the MSB set (i.e. a lowR signature), - // which can be encoded in DER format with one less byte. If an implementation does not support the use of the counter - // value, it MUST return undefined rather than perform a signing operation which ignores it. - ecdsaSign(digestAlgorithm: null, message: ByteArray<32>): Promise>; - ecdsaSign(digestAlgorithm: null, message: ByteArray<32>, counter: Uint32): Promise | undefined>; - ecdsaSign(digestAlgorithm: Digest.AlgorithmName<32>, message: Uint8Array): Promise>; - ecdsaSign(digestAlgorithm: Digest.AlgorithmName<32>, message: Uint8Array, counter: Uint32): Promise | undefined>; + // Signatures MUST use a determinsitic nonce generation algorithm, which SHOULD be the one specified in RFC6979. The + // counter parameter is used to generate multiple distinct signatures over the same message, and SHOULD be included as + // additional entropy in the nonce generation algorithm (typically after convertion to a 32-byte big-endian integer). + // + // This can be used, for example, to find a signature whose r-value does not have the MSB set (i.e. a lowR signature), + // which can be encoded in DER format with one less byte. If an implementation does not support the use of the counter + // value, it MUST return undefined rather than perform a signing operation which ignores it. + ecdsaSign(digestAlgorithm: null, message: ByteArray<32>): Promise>; + ecdsaSign( + digestAlgorithm: null, + message: ByteArray<32>, + counter: Uint32 + ): Promise | undefined>; + ecdsaSign(digestAlgorithm: Digest.AlgorithmName<32>, message: Uint8Array): Promise>; + ecdsaSign( + digestAlgorithm: Digest.AlgorithmName<32>, + message: Uint8Array, + counter: Uint32 + ): Promise | undefined>; } export interface ECDSARecoverableKey extends ECDSAKey { - ecdsaSignRecoverable(digestAlgorithm: null, message: ByteArray<32>): Promise>; - ecdsaSignRecoverable(digestAlgorithm: null, message: ByteArray<32>, counter: Uint32): Promise | undefined>; - ecdsaSignRecoverable(digestAlgorithm: Digest.AlgorithmName<32>, message: Uint8Array): Promise>; - ecdsaSignRecoverable(digestAlgorithm: Digest.AlgorithmName<32>, message: Uint8Array, counter: Uint32): Promise | undefined>; + ecdsaSignRecoverable(digestAlgorithm: null, message: ByteArray<32>): Promise>; + ecdsaSignRecoverable( + digestAlgorithm: null, + message: ByteArray<32>, + counter: Uint32 + ): Promise | undefined>; + ecdsaSignRecoverable( + digestAlgorithm: Digest.AlgorithmName<32>, + message: Uint8Array + ): Promise>; + ecdsaSignRecoverable( + digestAlgorithm: Digest.AlgorithmName<32>, + message: Uint8Array, + counter: Uint32 + ): Promise | undefined>; } export interface ECDHKey { - getPublicKey(): Promise; + getPublicKey(): Promise; - // Calculates a shared secret field element according to the ECDH key-agreement scheme specified in SEC 1 section 3.3.2, - // encoded as a 32-byte big-endian integer. The output of this function is not uniformly distributed, and is not safe to - // use as a cryptographic key by itself. - // - // A key derivation function is required to convert the output into a usable key; a plain, unkeyed cryptographic hash - // function such as SHA-256 is typically used for this purpose. - ecdh(publicKey: CurvePoint, digestAlgorithm?: Digest.AlgorithmName<32>): Promise>>; - ecdhRaw?(publicKey: CurvePoint): Promise>; + // Calculates a shared secret field element according to the ECDH key-agreement scheme specified in SEC 1 section 3.3.2, + // encoded as a 32-byte big-endian integer. The output of this function is not uniformly distributed, and is not safe to + // use as a cryptographic key by itself. + // + // A key derivation function is required to convert the output into a usable key; a plain, unkeyed cryptographic hash + // function such as SHA-256 is typically used for this purpose. + ecdh(publicKey: CurvePoint, digestAlgorithm?: Digest.AlgorithmName<32>): Promise>>; + ecdhRaw?(publicKey: CurvePoint): Promise>; } diff --git a/packages/hdwallet-native/src/crypto/isolation/core/secp256k1/types.ts b/packages/hdwallet-native/src/crypto/isolation/core/secp256k1/types.ts index 74fdab0d0..ad597f154 100644 --- a/packages/hdwallet-native/src/crypto/isolation/core/secp256k1/types.ts +++ b/packages/hdwallet-native/src/crypto/isolation/core/secp256k1/types.ts @@ -1,220 +1,316 @@ -import * as core from "@shapeshiftoss/hdwallet-core" +import * as core from "@shapeshiftoss/hdwallet-core"; import { Literal, Object as Obj, Static, Union } from "funtypes"; import * as tinyecc from "tiny-secp256k1"; +import { assertType, BigEndianInteger, ByteArray, checkType, safeBufferFrom, Uint32 } from "../../types"; import * as Digest from "../digest"; -import { BigEndianInteger, ByteArray, Uint32, checkType, safeBufferFrom, assertType } from "../../types"; import { ECDSAKey, ECDSARecoverableKey } from "./interfaces"; -const ethers = import("ethers") +const ethers = import("ethers"); -const fieldElementBase = BigEndianInteger(32).withConstraint( - x => tinyecc.isPrivate(safeBufferFrom(x)) || `expected ${x} to be within the order of the curve`, - {name: "FieldElement"}, +const _fieldElementBase = BigEndianInteger(32).withConstraint( + (x) => tinyecc.isPrivate(safeBufferFrom(x)) || `expected ${x} to be within the order of the curve`, + { name: "FieldElement" } ); -export type FieldElement = Static; -const fieldElementStatic = {}; -const fieldElement = Object.assign(fieldElementBase, BigEndianInteger, fieldElementStatic); -export const FieldElement: typeof fieldElement = fieldElement; - -const compressedPointBase = ByteArray(33).And(Obj({ - 0: Literal(0x02).Or(Literal(0x03)), -})).withConstraint( - p => FieldElement.test(p.slice(1)) || `expected ${p}.x to be within the order of the curve`, - {name: "CompressedPoint.x"}, -); -export type CompressedPoint = Static; -const compressedPointStatic = { - from: (p: CurvePoint): CompressedPoint => { - return (p.length === 33 ? p : CompressedPoint.fromUncompressed(checkType(UncompressedPoint, p))); - }, - fromUncompressed: (p: UncompressedPoint): CompressedPoint => { - const out = new Uint8Array(33); - out[0] = (UncompressedPoint.yIsOdd(p) ? 0x03 : 0x02); - out.set(UncompressedPoint.x(p), 1); - CompressedPoint.assert(out); - return out; +export type FieldElement = Static; +const _fieldElementStatic = {}; +const _fieldElement = Object.assign(_fieldElementBase, BigEndianInteger, _fieldElementStatic); +export const FieldElement: typeof _fieldElement = _fieldElement; + +const _uncompressedPointBase = ByteArray(65) + .And( + Obj({ + 0: Literal(0x04), + }) + ) + .withConstraint((p) => FieldElement.test(p.slice(1, 33)) || `expected ${p}.x to be within the order of the curve`, { + name: "UncompressedPoint.x", + }) + .withConstraint( + (p) => { + if (!FieldElement.test(p.slice(33, 65))) return `expected ${p}.y to be within the order of the curve`; + const pBuf = Buffer.from(p); + if (!ByteArray.equal(tinyecc.pointCompress(tinyecc.pointCompress(pBuf, true), false), pBuf)) + return `expected ${p} to be on the curve`; + return true; }, - x: (p: CompressedPoint): FieldElement => { return checkType(FieldElement, p.slice(1)); }, - yIsOdd: (p: CompressedPoint): boolean => { return p[0] === 0x03; } + { name: "UncompressedPoint.y" } + ); +export type UncompressedPoint = Static; + +const _compressedPointBase = ByteArray(33) + .And( + Obj({ + 0: Literal(0x02).Or(Literal(0x03)), + }) + ) + .withConstraint((p) => FieldElement.test(p.slice(1)) || `expected ${p}.x to be within the order of the curve`, { + name: "CompressedPoint.x", + }); +export type CompressedPoint = Static; + +const _uncompressedPointStatic = { + from: (p: CurvePoint): UncompressedPoint => { + return p.length === 65 ? p : UncompressedPoint.fromCompressed(checkType(CompressedPoint, p)); + }, + fromCompressed: (p: CompressedPoint): UncompressedPoint => { + return checkType(UncompressedPoint, tinyecc.pointCompress(Buffer.from(p), false)); + }, + x: (p: UncompressedPoint): FieldElement => { + return checkType(FieldElement, p.slice(1, 33)); + }, + y: (p: UncompressedPoint): FieldElement => { + return checkType(FieldElement, p.slice(33, 65)); + }, + yIsOdd: (p: UncompressedPoint): boolean => { + return FieldElement.isOdd(UncompressedPoint.y(p)); + }, }; -const compressedPoint = Object.assign(compressedPointBase, ByteArray, compressedPointStatic); -export const CompressedPoint: typeof compressedPoint = compressedPoint; - -const uncompressedPointBase = ByteArray(65).And(Obj({ - 0: Literal(0x04), -})).withConstraint( - p => FieldElement.test(p.slice(1, 33)) || `expected ${p}.x to be within the order of the curve`, - {name: "UncompressedPoint.x"}, -).withConstraint( - p => { - if (!FieldElement.test(p.slice(33, 65))) return `expected ${p}.y to be within the order of the curve`; - const pBuf = Buffer.from(p); - if (!ByteArray.equal(tinyecc.pointCompress(tinyecc.pointCompress(pBuf, true), false), pBuf)) return `expected ${p} to be on the curve`; - return true; - }, - {name: "UncompressedPoint.y"} -); -export type UncompressedPoint = Static; -const uncompressedPointStatic = { - from: (p: CurvePoint): UncompressedPoint => { - return (p.length === 65 ? p : UncompressedPoint.fromCompressed(checkType(CompressedPoint, p))); - }, - fromCompressed: (p: CompressedPoint): UncompressedPoint => { - return checkType(UncompressedPoint, tinyecc.pointCompress(Buffer.from(p), false)); - }, - x: (p: UncompressedPoint): FieldElement => { return checkType(FieldElement, p.slice(1, 33)); }, - y: (p: UncompressedPoint): FieldElement => { return checkType(FieldElement, p.slice(33, 65)); }, - yIsOdd: (p: UncompressedPoint): boolean => { return FieldElement.isOdd(UncompressedPoint.y(p)); }, +const _uncompressedPoint = Object.assign(_uncompressedPointBase, ByteArray, _uncompressedPointStatic); +export const UncompressedPoint: typeof _uncompressedPoint = _uncompressedPoint; + +const _compressedPointStatic = { + from: (p: CurvePoint): CompressedPoint => { + return p.length === 33 ? p : CompressedPoint.fromUncompressed(checkType(UncompressedPoint, p)); + }, + fromUncompressed: (p: UncompressedPoint): CompressedPoint => { + const out = new Uint8Array(33); + out[0] = UncompressedPoint.yIsOdd(p) ? 0x03 : 0x02; + out.set(UncompressedPoint.x(p), 1); + CompressedPoint.assert(out); + return out; + }, + x: (p: CompressedPoint): FieldElement => { + return checkType(FieldElement, p.slice(1)); + }, + yIsOdd: (p: CompressedPoint): boolean => { + return p[0] === 0x03; + }, }; -const uncompressedPoint = Object.assign(uncompressedPointBase, ByteArray, uncompressedPointStatic); -export const UncompressedPoint: typeof uncompressedPoint = uncompressedPoint; +const _compressedPoint = Object.assign(_compressedPointBase, ByteArray, _compressedPointStatic); +export const CompressedPoint: typeof _compressedPoint = _compressedPoint; -const curvePointBase = CompressedPoint.Or(UncompressedPoint); +const _curvePointBase = CompressedPoint.Or(UncompressedPoint); export type CurvePoint = CompressedPoint | UncompressedPoint; -const curvePointStatic = { - x: (p: CurvePoint): FieldElement => (p[0] === 0x04 ? UncompressedPoint.x(p) : CompressedPoint.x(p)), - yIsOdd: (p: CurvePoint): boolean => (p[0] === 0x04 ? UncompressedPoint.yIsOdd(p) : CompressedPoint.yIsOdd(p)), - // Equivalent to CompressedPoint.equal(CompressedPoint.from(lhs), CompressedPoint.from(rhs)), but avoids allocations - equal: (lhs: CurvePoint, rhs: CurvePoint) => CurvePoint.yIsOdd(lhs) === CurvePoint.yIsOdd(rhs) && FieldElement.equal(CurvePoint.x(lhs), CurvePoint.x(rhs)), +const _curvePointStatic = { + x: (p: CurvePoint): FieldElement => (p[0] === 0x04 ? UncompressedPoint.x(p) : CompressedPoint.x(p)), + yIsOdd: (p: CurvePoint): boolean => (p[0] === 0x04 ? UncompressedPoint.yIsOdd(p) : CompressedPoint.yIsOdd(p)), + // Equivalent to CompressedPoint.equal(CompressedPoint.from(lhs), CompressedPoint.from(rhs)), but avoids allocations + equal: (lhs: CurvePoint, rhs: CurvePoint) => + CurvePoint.yIsOdd(lhs) === CurvePoint.yIsOdd(rhs) && FieldElement.equal(CurvePoint.x(lhs), CurvePoint.x(rhs)), }; -const curvePoint = Object.assign(curvePointBase, curvePointStatic); -export const CurvePoint: typeof curvePoint = curvePoint; - -const recoveryParamBase = Union(Literal(0), Literal(1), Literal(2), Literal(3)); -export type RecoveryParam = Static; -const recoveryParamStatic = {}; -const recoveryParam = Object.assign(recoveryParamBase, recoveryParamStatic); -export const RecoveryParam: typeof recoveryParam = recoveryParam; - -const messageWithPreimageBase = ByteArray(32).And(Digest.Digest()); -export type MessageWithPreimage = Static; -const messageWithPreimageStatic = {}; -const messageWithPreimage = Object.assign(messageWithPreimageBase, ByteArray, messageWithPreimageStatic); -export const MessageWithPreimage: typeof messageWithPreimage = messageWithPreimage; - -const messageBase = MessageWithPreimage.Or(ByteArray()); -export type Message = Static; -const messageStatic = {}; -const message = Object.assign(messageBase, ByteArray, messageWithPreimageStatic, messageStatic); -export const Message: typeof message = message; - -const signatureBase = ByteArray(64).withConstraint( - x => FieldElement.test(x.slice(0, 32)) || `expected ${x}.r to be within the order of the curve`, - {name: "Signature.r"}, -).withConstraint( - x => FieldElement.test(x.slice(32, 64)) || `expected ${x}.s to be within the order of the curve`, - {name: "Signature.s"}, -); -export type Signature = Static; -const signatureStatic = { - r: (x: Signature): FieldElement => { return checkType(FieldElement, x.slice(0, 32)); }, - s: (x: Signature): FieldElement => { return checkType(FieldElement, x.slice(32, 64)); }, - isLowR: (x: Signature): boolean => { return !FieldElement.isHigh(Signature.r(x)); }, - isLowS: (x: Signature): boolean => { return !FieldElement.isHigh(Signature.s(x)); }, - isCanonical: (x: Signature): boolean => { return Signature.isLowR(x) && Signature.isLowS(x); }, - signCanonically: async (x: ECDSAKey, digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, counter?: Uint32): Promise => { - assertType(ByteArray(), message); - counter === undefined || Uint32.assert(counter); - for (let i = counter; i === undefined || i < (counter ?? 0) + 128; i = (i ?? -1) + 1) { - const sig = await (async () => { - if (digestAlgorithm === null) { - assertType(ByteArray(32), message); - return i === undefined ? await x.ecdsaSign(digestAlgorithm, message) : await x.ecdsaSign(digestAlgorithm, message, i); - } else { - return i === undefined ? await x.ecdsaSign(digestAlgorithm, message) : await x.ecdsaSign(digestAlgorithm, message, i); - } - })(); - if (sig === undefined) break; - //TODO: do integrated lowS correction - if (Signature.isCanonical(sig)) return sig; +const _curvePoint = Object.assign(_curvePointBase, _curvePointStatic); +export const CurvePoint: typeof _curvePoint = _curvePoint; + +const _recoveryParamBase = Union(Literal(0), Literal(1), Literal(2), Literal(3)); +export type RecoveryParam = Static; +const _recoveryParamStatic = {}; +const _recoveryParam = Object.assign(_recoveryParamBase, _recoveryParamStatic); +export const RecoveryParam: typeof _recoveryParam = _recoveryParam; + +const _messageWithPreimageBase = ByteArray(32).And(Digest.Digest()); +export type MessageWithPreimage = Static; +const _messageWithPreimageStatic = {}; +const _messageWithPreimage = Object.assign(_messageWithPreimageBase, ByteArray, _messageWithPreimageStatic); +export const MessageWithPreimage: typeof _messageWithPreimage = _messageWithPreimage; + +const _messageBase = MessageWithPreimage.Or(ByteArray()); +export type Message = Static; +const _messageStatic = {}; +const _message = Object.assign(_messageBase, ByteArray, _messageWithPreimageStatic, _messageStatic); +export const Message: typeof _message = _message; + +const _signatureBase = ByteArray(64) + .withConstraint((x) => FieldElement.test(x.slice(0, 32)) || `expected ${x}.r to be within the order of the curve`, { + name: "Signature.r", + }) + .withConstraint((x) => FieldElement.test(x.slice(32, 64)) || `expected ${x}.s to be within the order of the curve`, { + name: "Signature.s", + }); +export type Signature = Static; +const _signatureStatic = { + r: (x: Signature): FieldElement => { + return checkType(FieldElement, x.slice(0, 32)); + }, + s: (x: Signature): FieldElement => { + return checkType(FieldElement, x.slice(32, 64)); + }, + isLowR: (x: Signature): boolean => { + return !FieldElement.isHigh(Signature.r(x)); + }, + isLowS: (x: Signature): boolean => { + return !FieldElement.isHigh(Signature.s(x)); + }, + isCanonical: (x: Signature): boolean => { + return Signature.isLowR(x) && Signature.isLowS(x); + }, + signCanonically: async ( + x: ECDSAKey, + digestAlgorithm: Digest.AlgorithmName<32> | null, + message: Uint8Array, + counter?: Uint32 + ): Promise => { + assertType(ByteArray(), message); + counter === undefined || Uint32.assert(counter); + for (let i = counter; i === undefined || i < (counter ?? 0) + 128; i = (i ?? -1) + 1) { + const sig = await (async () => { + if (digestAlgorithm === null) { + assertType(ByteArray(32), message); + return i === undefined + ? await x.ecdsaSign(digestAlgorithm, message) + : await x.ecdsaSign(digestAlgorithm, message, i); + } else { + return i === undefined + ? await x.ecdsaSign(digestAlgorithm, message) + : await x.ecdsaSign(digestAlgorithm, message, i); } - // This is cryptographically impossible (2^-128 chance) if the key is implemented correctly. - throw new Error(`Unable to generate canonical signature with public key ${x} over message ${message}; is your key implementation broken?`); - }, - verify: (x: Signature, digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, publicKey: CurvePoint): boolean => { - const msgOrDigest = digestAlgorithm === null ? checkType(ByteArray(32), message) : Digest.Algorithms[digestAlgorithm](checkType(ByteArray(), message)); - return tinyecc.verify(Buffer.from(msgOrDigest), Buffer.from(publicKey), Buffer.from(x)); - }, + })(); + if (sig === undefined) break; + //TODO: do integrated lowS correction + if (Signature.isCanonical(sig)) return sig; + } + // This is cryptographically impossible (2^-128 chance) if the key is implemented correctly. + throw new Error( + `Unable to generate canonical signature with public key ${x} over message ${message}; is your key implementation broken?` + ); + }, + verify: ( + x: Signature, + digestAlgorithm: Digest.AlgorithmName<32> | null, + message: Uint8Array, + publicKey: CurvePoint + ): boolean => { + const msgOrDigest = + digestAlgorithm === null + ? checkType(ByteArray(32), message) + : Digest.Algorithms[digestAlgorithm](checkType(ByteArray(), message)); + return tinyecc.verify(Buffer.from(msgOrDigest), Buffer.from(publicKey), Buffer.from(x)); + }, }; -const signature = Object.assign(signatureBase, ByteArray, signatureStatic); -export const Signature: typeof signature = signature; - -const recoverableSignatureBase = ByteArray(65).And(Obj({ - 64: RecoveryParam, -})).withConstraint( - x => Signature.test(x.slice(0, 64)) || `expected ${x}.sig to be a valid signature`, - {name: "Signature"}, -); -export type RecoverableSignature = Static; -const recoverableSignatureStatic = { - from: (x: Signature, recoveryParam: RecoveryParam): RecoverableSignature => { - return checkType(RecoverableSignature, core.compatibleBufferConcat([x, new Uint8Array([recoveryParam])])); - }, - fromSignature: async (x: Signature, digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, publicKey: CurvePoint): Promise => { - for (let recoveryParam: RecoveryParam = 0; recoveryParam < 4; recoveryParam++) { - const out = RecoverableSignature.from(x, recoveryParam); - if (!CurvePoint.equal(publicKey, await RecoverableSignature.recoverPublicKey(out, digestAlgorithm, message))) continue; - return out; - } - throw new Error(`couldn't find recovery parameter producing public key ${publicKey} for signature ${x} over message ${message}`); - }, - sig: (x: RecoverableSignature): Signature => checkType(Signature, x.slice(0, 64)), - recoveryParam: (x: RecoverableSignature): RecoveryParam => checkType(RecoveryParam, x[64]), - isLowRecoveryParam: (x: RecoverableSignature) => [0, 1].includes(RecoverableSignature.recoveryParam(x)), - isCanonical: (x: RecoverableSignature): boolean => Signature.isCanonical(checkType(Signature, RecoverableSignature.sig(x))) && RecoverableSignature.isLowRecoveryParam(x), - signCanonically: async (x: ECDSAKey, digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, counter?: Uint32): Promise => { - const publicKey = await x.getPublicKey(); - assertType(ByteArray(), message); - counter === undefined || Uint32.assert(counter); - - const isIndexable = (x: unknown): x is Record => x !== null && ["object", "function"].includes(typeof x); - const isECDSARecoverableKey = (x: ECDSAKey): x is ECDSARecoverableKey => isIndexable(x) && "ecdsaSignRecoverable" in x && typeof x.ecdsaSignRecoverable === "function"; - - const ecdsaSignRecoverable = isECDSARecoverableKey(x) ? async (digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, counter?: Uint32) => { +const _signature = Object.assign(_signatureBase, ByteArray, _signatureStatic); +export const Signature: typeof _signature = _signature; + +const _recoverableSignatureBase = ByteArray(65) + .And( + Obj({ + 64: RecoveryParam, + }) + ) + .withConstraint((x) => Signature.test(x.slice(0, 64)) || `expected ${x}.sig to be a valid signature`, { + name: "Signature", + }); +export type RecoverableSignature = Static; +const _recoverableSignatureStatic = { + from: (x: Signature, recoveryParam: RecoveryParam): RecoverableSignature => { + return checkType(RecoverableSignature, core.compatibleBufferConcat([x, new Uint8Array([recoveryParam])])); + }, + fromSignature: async ( + x: Signature, + digestAlgorithm: Digest.AlgorithmName<32> | null, + message: Uint8Array, + publicKey: CurvePoint + ): Promise => { + for (let recoveryParam: RecoveryParam = 0; recoveryParam < 4; recoveryParam++) { + const out = RecoverableSignature.from(x, recoveryParam); + if (!CurvePoint.equal(publicKey, await RecoverableSignature.recoverPublicKey(out, digestAlgorithm, message))) + continue; + return out; + } + throw new Error( + `couldn't find recovery parameter producing public key ${publicKey} for signature ${x} over message ${message}` + ); + }, + sig: (x: RecoverableSignature): Signature => checkType(Signature, x.slice(0, 64)), + recoveryParam: (x: RecoverableSignature): RecoveryParam => checkType(RecoveryParam, x[64]), + isLowRecoveryParam: (x: RecoverableSignature) => [0, 1].includes(RecoverableSignature.recoveryParam(x)), + isCanonical: (x: RecoverableSignature): boolean => + Signature.isCanonical(checkType(Signature, RecoverableSignature.sig(x))) && + RecoverableSignature.isLowRecoveryParam(x), + signCanonically: async ( + x: ECDSAKey, + digestAlgorithm: Digest.AlgorithmName<32> | null, + message: Uint8Array, + counter?: Uint32 + ): Promise => { + const publicKey = await x.getPublicKey(); + assertType(ByteArray(), message); + counter === undefined || Uint32.assert(counter); + + // eslint-disable-next-line @typescript-eslint/no-shadow + const isIndexable = (x: unknown): x is Record => + x !== null && ["object", "function"].includes(typeof x); + // eslint-disable-next-line @typescript-eslint/no-shadow + const isECDSARecoverableKey = (x: ECDSAKey): x is ECDSARecoverableKey => + isIndexable(x) && "ecdsaSignRecoverable" in x && typeof x.ecdsaSignRecoverable === "function"; + + const ecdsaSignRecoverable = isECDSARecoverableKey(x) + ? // eslint-disable-next-line @typescript-eslint/no-shadow + async (digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, counter?: Uint32) => { if (digestAlgorithm === null) { assertType(ByteArray(32), message); - return counter === undefined ? await x.ecdsaSignRecoverable(digestAlgorithm, message) : await x.ecdsaSignRecoverable(digestAlgorithm, message, counter); + return counter === undefined + ? await x.ecdsaSignRecoverable(digestAlgorithm, message) + : await x.ecdsaSignRecoverable(digestAlgorithm, message, counter); } else { - return counter === undefined ? await x.ecdsaSignRecoverable(digestAlgorithm, message) : await x.ecdsaSignRecoverable(digestAlgorithm, message, counter); + return counter === undefined + ? await x.ecdsaSignRecoverable(digestAlgorithm, message) + : await x.ecdsaSignRecoverable(digestAlgorithm, message, counter); } - } : async (digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, counter?: Uint32) => { + } + : // eslint-disable-next-line @typescript-eslint/no-shadow + async (digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, counter?: Uint32) => { const sig = await Signature.signCanonically(x, digestAlgorithm, message, counter); if (sig === undefined) return undefined; return await RecoverableSignature.fromSignature(sig, digestAlgorithm, message, publicKey); }; - // Technically, this may waste cycles; if Signature.signCanonically grinds the counter to find a canonical signature which then - // ends up to have a non-canonical recovery parameter, those values will all be re-ground. However, signatures can have - // non-canonical recovery parameters only with negligible probability, so optimization for that case would be silly. - for (let i = counter; i === undefined || i < (counter ?? 0) + 128; i = (i ?? -1) + 1) { - const recoverableSig = await ecdsaSignRecoverable(digestAlgorithm, message, i); - if (recoverableSig === undefined) break; - //TODO: do integrated lowS correction - if (RecoverableSignature.isCanonical(recoverableSig)) return recoverableSig; - } - // This is cryptographically impossible (2^-128 chance) if the key is implemented correctly. - throw new Error(`Unable to generate canonical recoverable signature with public key ${Buffer.from(publicKey).toString("hex")} over message ${Buffer.from(message).toString("hex")}; is your key implementation broken?`); - }, - recoverPublicKey: async (x: RecoverableSignature, digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array): Promise => { - // TODO: do this better - const msgOrDigest = digestAlgorithm === null ? checkType(ByteArray(32), message) : Digest.Algorithms[digestAlgorithm](checkType(ByteArray(), message)); - const sig = RecoverableSignature.sig(x); - const recoveryParam = RecoverableSignature.recoveryParam(x); - const ethSig = core.compatibleBufferConcat([sig, Buffer.from([recoveryParam])]); - const ethRecovered = (await ethers).utils.recoverPublicKey(msgOrDigest, (await ethers).utils.splitSignature(ethSig)); - return checkType(UncompressedPoint, Buffer.from(ethRecovered.slice(2), "hex")); - }, - r: (x: RecoverableSignature): FieldElement => Signature.r(RecoverableSignature.sig(x)), - s: (x: RecoverableSignature): FieldElement => Signature.s(RecoverableSignature.sig(x)), - isLowR: (x: RecoverableSignature): boolean => Signature.isLowR(RecoverableSignature.sig(x)), - isLowS: (x: RecoverableSignature): boolean => Signature.isLowS(RecoverableSignature.sig(x)), - verify: (x: RecoverableSignature, digestAlgorithm: Digest.AlgorithmName<32> | null, message: Uint8Array, publicKey: CurvePoint): boolean => { - return Signature.verify(RecoverableSignature.sig(x), digestAlgorithm, message, publicKey); - }, + // Technically, this may waste cycles; if Signature.signCanonically grinds the counter to find a canonical signature which then + // ends up to have a non-canonical recovery parameter, those values will all be re-ground. However, signatures can have + // non-canonical recovery parameters only with negligible probability, so optimization for that case would be silly. + for (let i = counter; i === undefined || i < (counter ?? 0) + 128; i = (i ?? -1) + 1) { + const recoverableSig = await ecdsaSignRecoverable(digestAlgorithm, message, i); + if (recoverableSig === undefined) break; + //TODO: do integrated lowS correction + if (RecoverableSignature.isCanonical(recoverableSig)) return recoverableSig; + } + // This is cryptographically impossible (2^-128 chance) if the key is implemented correctly. + throw new Error( + `Unable to generate canonical recoverable signature with public key ${Buffer.from(publicKey).toString( + "hex" + )} over message ${Buffer.from(message).toString("hex")}; is your key implementation broken?` + ); + }, + recoverPublicKey: async ( + x: RecoverableSignature, + digestAlgorithm: Digest.AlgorithmName<32> | null, + message: Uint8Array + ): Promise => { + // TODO: do this better + const msgOrDigest = + digestAlgorithm === null + ? checkType(ByteArray(32), message) + : Digest.Algorithms[digestAlgorithm](checkType(ByteArray(), message)); + const sig = RecoverableSignature.sig(x); + const recoveryParam = RecoverableSignature.recoveryParam(x); + const ethSig = core.compatibleBufferConcat([sig, Buffer.from([recoveryParam])]); + const ethRecovered = (await ethers).utils.recoverPublicKey( + msgOrDigest, + (await ethers).utils.splitSignature(ethSig) + ); + return checkType(UncompressedPoint, Buffer.from(ethRecovered.slice(2), "hex")); + }, + r: (x: RecoverableSignature): FieldElement => Signature.r(RecoverableSignature.sig(x)), + s: (x: RecoverableSignature): FieldElement => Signature.s(RecoverableSignature.sig(x)), + isLowR: (x: RecoverableSignature): boolean => Signature.isLowR(RecoverableSignature.sig(x)), + isLowS: (x: RecoverableSignature): boolean => Signature.isLowS(RecoverableSignature.sig(x)), + verify: ( + x: RecoverableSignature, + digestAlgorithm: Digest.AlgorithmName<32> | null, + message: Uint8Array, + publicKey: CurvePoint + ): boolean => { + return Signature.verify(RecoverableSignature.sig(x), digestAlgorithm, message, publicKey); + }, }; -const recoverableSignature = Object.assign( - recoverableSignatureBase, - recoverableSignatureStatic -); -export const RecoverableSignature: typeof recoverableSignature = recoverableSignature; +const _recoverableSignature = Object.assign(_recoverableSignatureBase, _recoverableSignatureStatic); +export const RecoverableSignature: typeof _recoverableSignature = _recoverableSignature; diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts index e27893fe8..d6d8a1883 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip32.ts @@ -2,146 +2,184 @@ import * as bip32crypto from "bip32/src/crypto"; import * as tinyecc from "tiny-secp256k1"; import { TextEncoder } from "web-encoding"; -import { ByteArray, Uint32, checkType, safeBufferFrom, assertType } from "../../types"; import { BIP32, Digest, SecP256K1 } from "../../core"; -import { revocable, Revocable } from "./revocable"; +import { assertType, ByteArray, checkType, safeBufferFrom, Uint32 } from "../../types"; +import { Revocable, revocable } from "./revocable"; export * from "../../core/bip32"; -export class Seed extends Revocable(class {}) implements BIP32.Seed { - readonly #seed: Buffer; - - protected constructor(seed: Uint8Array) { - super() - this.#seed = safeBufferFrom(seed); - this.addRevoker(() => this.#seed.fill(0)); - } - - static async create(seed: Uint8Array): Promise { - const obj = new Seed(seed); - return revocable(obj, (x) => obj.addRevoker(x)); - } - - async toMasterKey(hmacKey?: string | Uint8Array): Promise { - if (hmacKey !== undefined && typeof hmacKey !== "string" && !(hmacKey instanceof Uint8Array)) throw new Error("bad hmacKey type"); - - // AFAIK all BIP32 implementations use the "Bitcoin seed" string for this derivation, even if they aren't otherwise Bitcoin-related - hmacKey = hmacKey ?? "Bitcoin seed"; - if (typeof hmacKey === "string") hmacKey = new TextEncoder().encode(hmacKey.normalize("NFKD")); - const I = safeBufferFrom(bip32crypto.hmacSHA512(safeBufferFrom(hmacKey), this.#seed)); - const IL = I.slice(0, 32); - const IR = I.slice(32, 64); - const out = await Node.create(IL, IR); - this.addRevoker(() => out.revoke?.()); - return out; - }; -} - export class Node extends Revocable(class {}) implements BIP32.Node, SecP256K1.ECDSARecoverableKey, SecP256K1.ECDHKey { - readonly #privateKey: Buffer & ByteArray<32>; - readonly chainCode: Buffer & BIP32.ChainCode; - #publicKey: SecP256K1.CompressedPoint | undefined; - - // When running tests, this will keep us aware of any codepaths that don't pass in the preimage - static requirePreimage = typeof expect === "function"; - - protected constructor(privateKey: Uint8Array, chainCode: Uint8Array) { - super() - // We avoid handing the private key to any non-platform code -- including our type-checking machinery. - if (privateKey.length !== 32) throw new Error("bad private key length"); - this.#privateKey = safeBufferFrom(privateKey) as Buffer & ByteArray<32>; - this.addRevoker(() => this.#privateKey.fill(0)); - this.chainCode = safeBufferFrom(checkType(BIP32.ChainCode, chainCode)) as Buffer & BIP32.ChainCode; - } - - static async create(privateKey: Uint8Array, chainCode: Uint8Array): Promise { - const obj = new Node(privateKey, chainCode); - return revocable(obj, (x) => obj.addRevoker(x)); - } - - async getPublicKey(): Promise { - this.#publicKey = this.#publicKey ?? checkType(SecP256K1.CompressedPoint, tinyecc.pointFromScalar(this.#privateKey, true)); - return this.#publicKey; - } - - async getChainCode() { return this.chainCode } - - async ecdsaSign(digestAlgorithm: null, msg: ByteArray<32>, counter?: Uint32): Promise - async ecdsaSign(digestAlgorithm: Digest.AlgorithmName<32>, msg: Uint8Array, counter?: Uint32): Promise - async ecdsaSign(digestAlgorithm: Digest.AlgorithmName<32> | null, msg: Uint8Array, counter?: Uint32): Promise { - const recoverableSig = await (async () =>{ - if (digestAlgorithm === null) { - assertType(ByteArray(32), msg); - return await this.ecdsaSignRecoverable(digestAlgorithm, msg, counter); - } else { - return await this.ecdsaSignRecoverable(digestAlgorithm, msg, counter); - } - })(); - return SecP256K1.RecoverableSignature.sig(recoverableSig) + readonly #privateKey: Buffer & ByteArray<32>; + readonly chainCode: Buffer & BIP32.ChainCode; + #publicKey: SecP256K1.CompressedPoint | undefined; + + // When running tests, this will keep us aware of any codepaths that don't pass in the preimage + static requirePreimage = typeof expect === "function"; + + protected constructor(privateKey: Uint8Array, chainCode: Uint8Array) { + super(); + // We avoid handing the private key to any non-platform code -- including our type-checking machinery. + if (privateKey.length !== 32) throw new Error("bad private key length"); + this.#privateKey = safeBufferFrom(privateKey) as Buffer & ByteArray<32>; + this.addRevoker(() => this.#privateKey.fill(0)); + this.chainCode = safeBufferFrom(checkType(BIP32.ChainCode, chainCode)) as Buffer & BIP32.ChainCode; + } + + static async create(privateKey: Uint8Array, chainCode: Uint8Array): Promise { + const obj = new Node(privateKey, chainCode); + return revocable(obj, (x) => obj.addRevoker(x)); + } + + async getPublicKey(): Promise { + this.#publicKey = + this.#publicKey ?? checkType(SecP256K1.CompressedPoint, tinyecc.pointFromScalar(this.#privateKey, true)); + return this.#publicKey; + } + + async getChainCode() { + return this.chainCode; + } + + async ecdsaSign(digestAlgorithm: null, msg: ByteArray<32>, counter?: Uint32): Promise; + async ecdsaSign( + digestAlgorithm: Digest.AlgorithmName<32>, + msg: Uint8Array, + counter?: Uint32 + ): Promise; + async ecdsaSign( + digestAlgorithm: Digest.AlgorithmName<32> | null, + msg: Uint8Array, + counter?: Uint32 + ): Promise { + const recoverableSig = await (async () => { + if (digestAlgorithm === null) { + assertType(ByteArray(32), msg); + return await this.ecdsaSignRecoverable(digestAlgorithm, msg, counter); + } else { + return await this.ecdsaSignRecoverable(digestAlgorithm, msg, counter); + } + })(); + return SecP256K1.RecoverableSignature.sig(recoverableSig); + } + + async ecdsaSignRecoverable( + digestAlgorithm: null, + msg: ByteArray<32>, + counter?: Uint32 + ): Promise; + async ecdsaSignRecoverable( + digestAlgorithm: Digest.AlgorithmName<32>, + msg: Uint8Array, + counter?: Uint32 + ): Promise; + async ecdsaSignRecoverable( + digestAlgorithm: Digest.AlgorithmName<32> | null, + msg: Uint8Array, + counter?: Uint32 + ): Promise { + counter === undefined || Uint32.assert(counter); + digestAlgorithm === null || Digest.AlgorithmName(32).assert(digestAlgorithm); + + if (Node.requirePreimage && digestAlgorithm === null) throw TypeError("preimage required"); + + const msgOrDigest = + digestAlgorithm === null + ? checkType(ByteArray(32), msg) + : Digest.Algorithms[digestAlgorithm](checkType(ByteArray(), msg)); + const entropy = counter === undefined ? undefined : Buffer.alloc(32); + entropy?.writeUInt32BE(counter ?? 0, 24); + return await SecP256K1.RecoverableSignature.fromSignature( + checkType( + SecP256K1.Signature, + ( + tinyecc as typeof tinyecc & { + signWithEntropy: (message: Buffer, privateKey: Buffer, entropy?: Buffer) => Buffer; + } + ).signWithEntropy(Buffer.from(msgOrDigest), this.#privateKey, entropy) + ), + null, + msgOrDigest, + await this.getPublicKey() + ); + } + + async derive(index: Uint32): Promise { + Uint32.assert(index); + + const serP = Buffer.alloc(37); + if (index < 0x80000000) { + serP.set(SecP256K1.CompressedPoint.from(await this.getPublicKey()), 0); + } else { + serP.set(this.#privateKey, 1); } + serP.writeUInt32BE(index, 33); + + const I = bip32crypto.hmacSHA512(this.chainCode, serP); + const IL = I.slice(0, 32); + const IR = I.slice(32, 64); + const ki = tinyecc.privateAdd(this.#privateKey, IL); + if (ki === null) throw new Error("ki is null; this should be cryptographically impossible"); + const out = await Node.create(ki, IR); + this.addRevoker(() => out.revoke?.()); + return out as this; + } + + async ecdh(publicKey: SecP256K1.CurvePoint, digestAlgorithm?: Digest.AlgorithmName<32>): Promise> { + SecP256K1.CurvePoint.assert(publicKey); + digestAlgorithm === undefined || Digest.AlgorithmName(32).assert(digestAlgorithm); + + return checkType(ByteArray(32), await this._ecdh(publicKey, digestAlgorithm)); + } + + async ecdhRaw(publicKey: SecP256K1.CurvePoint): Promise { + return checkType(SecP256K1.UncompressedPoint, await this._ecdh(publicKey, null)); + } + + private async _ecdh( + publicKey: SecP256K1.CurvePoint, + digestAlgorithm?: Digest.AlgorithmName<32> | null + ): Promise | SecP256K1.UncompressedPoint> { + SecP256K1.CurvePoint.assert(publicKey); + digestAlgorithm === undefined || digestAlgorithm === null || Digest.AlgorithmName(32).assert(digestAlgorithm); + + const sharedFieldElement = checkType( + SecP256K1.UncompressedPoint, + tinyecc.pointMultiply(Buffer.from(publicKey), this.#privateKey, false) + ); + if (digestAlgorithm === null) return sharedFieldElement; + + let out = SecP256K1.CurvePoint.x(sharedFieldElement); + if (digestAlgorithm !== undefined) out = Digest.Algorithms[digestAlgorithm](out); + return out; + } +} - async ecdsaSignRecoverable(digestAlgorithm: null, msg: ByteArray<32>, counter?: Uint32): Promise - async ecdsaSignRecoverable(digestAlgorithm: Digest.AlgorithmName<32>, msg: Uint8Array, counter?: Uint32): Promise - async ecdsaSignRecoverable(digestAlgorithm: Digest.AlgorithmName<32> | null, msg: Uint8Array, counter?: Uint32): Promise { - counter === undefined || Uint32.assert(counter); - digestAlgorithm === null || Digest.AlgorithmName(32).assert(digestAlgorithm); - - if (Node.requirePreimage && digestAlgorithm === null) throw TypeError("preimage required"); - - const msgOrDigest = digestAlgorithm === null ? checkType(ByteArray(32), msg) : Digest.Algorithms[digestAlgorithm](checkType(ByteArray(), msg)); - const entropy = (counter === undefined ? undefined : Buffer.alloc(32)); - entropy?.writeUInt32BE(counter ?? 0, 24); - return await SecP256K1.RecoverableSignature.fromSignature( - checkType(SecP256K1.Signature, (tinyecc as typeof tinyecc & { - signWithEntropy: (message: Buffer, privateKey: Buffer, entropy?: Buffer) => Buffer, - }).signWithEntropy(Buffer.from(msgOrDigest), this.#privateKey, entropy)), - null, - msgOrDigest, - await this.getPublicKey(), - ); - } - - async derive(index: Uint32): Promise { - Uint32.assert(index); - - let serP = Buffer.alloc(37); - if (index < 0x80000000) { - serP.set(SecP256K1.CompressedPoint.from(await this.getPublicKey()), 0); - } else { - serP.set(this.#privateKey, 1); - } - serP.writeUInt32BE(index, 33); - - const I = bip32crypto.hmacSHA512(this.chainCode, serP); - const IL = I.slice(0, 32); - const IR = I.slice(32, 64); - const ki = tinyecc.privateAdd(this.#privateKey, IL); - if (ki === null) throw new Error("ki is null; this should be cryptographically impossible"); - const out = await Node.create(ki, IR); - this.addRevoker(() => out.revoke?.()) - return out as this; - } - - async ecdh(publicKey: SecP256K1.CurvePoint, digestAlgorithm?: Digest.AlgorithmName<32>): Promise> { - SecP256K1.CurvePoint.assert(publicKey); - digestAlgorithm === undefined || Digest.AlgorithmName(32).assert(digestAlgorithm); - - return checkType(ByteArray(32), await this._ecdh(publicKey, digestAlgorithm)); - } - - async ecdhRaw(publicKey: SecP256K1.CurvePoint): Promise { - return checkType(SecP256K1.UncompressedPoint, await this._ecdh(publicKey, null)); - } - - private async _ecdh(publicKey: SecP256K1.CurvePoint, digestAlgorithm?: Digest.AlgorithmName<32> | null): Promise | SecP256K1.UncompressedPoint> { - SecP256K1.CurvePoint.assert(publicKey); - digestAlgorithm === undefined || digestAlgorithm === null || Digest.AlgorithmName(32).assert(digestAlgorithm); - - const sharedFieldElement = checkType(SecP256K1.UncompressedPoint, tinyecc.pointMultiply(Buffer.from(publicKey), this.#privateKey, false)); - if (digestAlgorithm === null) return sharedFieldElement; - - let out = SecP256K1.CurvePoint.x(sharedFieldElement); - if (digestAlgorithm !== undefined) out = Digest.Algorithms[digestAlgorithm](out); - return out; - } +export class Seed extends Revocable(class {}) implements BIP32.Seed { + readonly #seed: Buffer; + + protected constructor(seed: Uint8Array) { + super(); + this.#seed = safeBufferFrom(seed); + this.addRevoker(() => this.#seed.fill(0)); + } + + static async create(seed: Uint8Array): Promise { + const obj = new Seed(seed); + return revocable(obj, (x) => obj.addRevoker(x)); + } + + async toMasterKey(hmacKey?: string | Uint8Array): Promise { + if (hmacKey !== undefined && typeof hmacKey !== "string" && !(hmacKey instanceof Uint8Array)) + throw new Error("bad hmacKey type"); + + // AFAIK all BIP32 implementations use the "Bitcoin seed" string for this derivation, even if they aren't otherwise Bitcoin-related + hmacKey = hmacKey ?? "Bitcoin seed"; + if (typeof hmacKey === "string") hmacKey = new TextEncoder().encode(hmacKey.normalize("NFKD")); + const I = safeBufferFrom(bip32crypto.hmacSHA512(safeBufferFrom(hmacKey), this.#seed)); + const IL = I.slice(0, 32); + const IR = I.slice(32, 64); + const out = await Node.create(IL, IR); + this.addRevoker(() => out.revoke?.()); + return out; + } } diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts index 0c935bb00..a7c1cafa0 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/default/bip39.ts @@ -1,65 +1,66 @@ /// -import * as core from "@shapeshiftoss/hdwallet-core" - +import * as core from "@shapeshiftoss/hdwallet-core"; import * as bip32crypto from "bip32/src/crypto"; import { TextEncoder } from "web-encoding"; -import * as BIP39 from "../../core/bip39"; import * as BIP32 from "../../core/bip32"; +import * as BIP39 from "../../core/bip39"; import { safeBufferFrom } from "../../types"; import * as BIP32Engine from "./bip32"; -import { revocable, Revocable } from "./revocable"; +import { Revocable, revocable } from "./revocable"; export * from "../../core/bip39"; // Poor man's single-block PBKDF2 implementation //TODO: get something better function pbkdf2_sha512_singleblock( - password: string, - salt: Uint8Array, - iterations: number + password: string, + salt: Uint8Array, + iterations: number ): Uint8Array & { length: 64 } { - function be32Buf(index: number): Buffer { + function be32Buf(index: number): Buffer { const indexBE = Buffer.alloc(4); indexBE.writeUInt32BE(index); return indexBE; - } + } - const pwBuffer = safeBufferFrom(new TextEncoder().encode(password)); + const pwBuffer = safeBufferFrom(new TextEncoder().encode(password)); - let out = bip32crypto.hmacSHA512(pwBuffer, core.compatibleBufferConcat([salt, be32Buf(1)])) as Buffer & { length: 64 }; - let lastU = out; - for (let i = 2; i <= iterations; i++) { - let newU = bip32crypto.hmacSHA512(pwBuffer, lastU) as Buffer & { length: 64 }; - for (let j = 0; j < out.length; j++) out[j] ^= newU[j]; - lastU = newU; - } + const out = bip32crypto.hmacSHA512(pwBuffer, core.compatibleBufferConcat([salt, be32Buf(1)])) as Buffer & { + length: 64; + }; + let lastU = out; + for (let i = 2; i <= iterations; i++) { + const newU = bip32crypto.hmacSHA512(pwBuffer, lastU) as Buffer & { length: 64 }; + for (let j = 0; j < out.length; j++) out[j] ^= newU[j]; + lastU = newU; + } - return out; + return out; } export class Mnemonic extends Revocable(class {}) implements BIP39.Mnemonic { - readonly #mnemonic: string; + readonly #mnemonic: string; - protected constructor(mnemonic: string) { - super(); - this.#mnemonic = mnemonic.normalize("NFKD"); - } + protected constructor(mnemonic: string) { + super(); + this.#mnemonic = mnemonic.normalize("NFKD"); + } - static async create(mnemonic: string): Promise { - const obj = new Mnemonic(mnemonic) - return revocable(obj, (x) => obj.addRevoker(x)); - } + static async create(mnemonic: string): Promise { + const obj = new Mnemonic(mnemonic); + return revocable(obj, (x) => obj.addRevoker(x)); + } - async toSeed(passphrase?: string): Promise { - if (passphrase !== undefined && typeof passphrase !== "string") throw new Error("bad passphrase type"); + async toSeed(passphrase?: string): Promise { + if (passphrase !== undefined && typeof passphrase !== "string") throw new Error("bad passphrase type"); - const mnemonic = this.#mnemonic; - const salt = new TextEncoder().encode(`mnemonic${passphrase ?? ""}`.normalize("NFKD")); + const mnemonic = this.#mnemonic; + const salt = new TextEncoder().encode(`mnemonic${passphrase ?? ""}`.normalize("NFKD")); - const out = await BIP32Engine.Seed.create(pbkdf2_sha512_singleblock(mnemonic, salt, 2048)); - this.addRevoker(() => out.revoke?.()) - return out - }; + const out = await BIP32Engine.Seed.create(pbkdf2_sha512_singleblock(mnemonic, salt, 2048)); + this.addRevoker(() => out.revoke?.()); + return out; + } } diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/default/index.test.ts b/packages/hdwallet-native/src/crypto/isolation/engines/default/index.test.ts index 80d567bea..7f99224ea 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/default/index.test.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/default/index.test.ts @@ -7,18 +7,30 @@ describe("Isolation.Engines.Default", () => { let masterKey: Core.BIP32.Node; it("can be loaded with a list of xpubs", async () => { - mnemonic = await Default.BIP39.Mnemonic.create("all all all all all all all all all all all all"); + await expect( + (async () => { + mnemonic = await Default.BIP39.Mnemonic.create("all all all all all all all all all all all all"); + })() + ).resolves.not.toThrow(); + expect(mnemonic).toBeDefined(); }); it("produces a seed", async () => { - seed = await mnemonic.toSeed(); - }) + await expect( + (async () => { + seed = await mnemonic.toSeed(); + })() + ).resolves.not.toThrow(); + expect(seed).toBeDefined(); + }); it("produces a master key", async () => { masterKey = await seed.toMasterKey(); - const pk = await masterKey.getPublicKey() - expect(Buffer.from(pk).toString("hex")).toEqual("03e3b30e8c21923752a408242e069941fedbaef7db7161f7e2c5f3fdafe7e25ddc"); - }) + const pk = await masterKey.getPublicKey(); + expect(Buffer.from(pk).toString("hex")).toEqual( + "03e3b30e8c21923752a408242e069941fedbaef7db7161f7e2c5f3fdafe7e25ddc" + ); + }); it.each([ ["m/44'", "034d600165882b6faf32a3f1f2c4755eeb0f0486954718d46fd9621e8ca40ca6b6"], @@ -63,4 +75,4 @@ describe("Isolation.Engines.Default", () => { const pk = Buffer.from(await node.getPublicKey()).toString("hex"); expect(pk).toEqual(expectedPk); }); -}) +}); diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/default/revocable.ts b/packages/hdwallet-native/src/crypto/isolation/engines/default/revocable.ts index 93667992d..013ac2e72 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/default/revocable.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/default/revocable.ts @@ -1,8 +1,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -const _Set = Set -const _freeze = Object.freeze.bind(Object) -const _revocable = Proxy.revocable.bind(Proxy) +const _Set = Set; +const _freeze = Object.freeze.bind(Object); +const _revocable = Proxy.revocable.bind(Proxy); /* Proxy handler invariants (per MDN): @@ -51,60 +51,76 @@ Proxy handler invariants (per MDN): */ export const revocable = _freeze((x: T, addRevoker: (revoke: () => void) => void) => { - const universalProxyHandler = (x: object) => new Proxy({}, { get(_, p) { - return (_t: any, p2: any, r: any) => { - switch (p) { - case "get": { - const out = Reflect.get(x, p2, r) - if (typeof out === "function") return out.bind(x); - return out - } - case "getOwnPropertyDescriptor": { - const out = Reflect.getOwnPropertyDescriptor(x, p2) - if (out) out.configurable = true - return out - } - case "isExtensible": return true - case "preventExtensions": return false - default: return (Reflect as any)[p](x, p2, r) + const universalProxyHandler = (pseudoTarget: object) => + new Proxy( + {}, + { + get(_, p) { + return (_t: any, p2: any, r: any) => { + switch (p) { + case "get": { + const out = Reflect.get(pseudoTarget, p2, r); + if (typeof out === "function") return out.bind(x); + return out; + } + case "getOwnPropertyDescriptor": { + const out = Reflect.getOwnPropertyDescriptor(pseudoTarget, p2); + if (out) out.configurable = true; + return out; + } + case "isExtensible": + return true; + case "preventExtensions": + return false; + default: + return (Reflect as any)[p](pseudoTarget, p2, r); + } + }; + }, } - }; - }}) - const { proxy, revoke } = _revocable({} as T, universalProxyHandler(x)) - addRevoker(revoke) - return proxy -}) + ); + const { proxy, revoke } = _revocable({} as T, universalProxyHandler(x)); + addRevoker(revoke); + return proxy; +}); export interface Revocable { - revoke(): void - addRevoker(x: () => void): void + revoke(): void; + addRevoker(x: () => void): void; } export const Revocable = _freeze((x: T) => { - const out = _freeze(class Revocable extends x { - readonly #revokers: Set<() => void> = new _Set() - #revoked = false + const out = _freeze( + // eslint-disable-next-line @typescript-eslint/no-shadow + class Revocable extends x { + readonly #revokers: Set<() => void> = new _Set(); + #revoked = false; - readonly revoke = () => { - this.#revoked = true; - this.#revokers.forEach(x => { - try { - x() - } catch { } - }); - this.#revokers.clear(); - }; + readonly revoke = () => { + this.#revoked = true; + this.#revokers.forEach((revoker) => { + try { + revoker(); + } catch { + // revoker errors get swallowed. + } + }); + this.#revokers.clear(); + }; - readonly addRevoker = (x: () => void) => { - if (this.#revoked) { - try { - x() - } catch { } - } else { - this.#revokers.add(x); - } - }; - }); + readonly addRevoker = (revoker: () => void) => { + if (this.#revoked) { + try { + revoker(); + } catch { + // revoker errors get swallowed. + } + } else { + this.#revokers.add(revoker); + } + }; + } + ); _freeze(out.prototype); return out; -}) +}); diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip32.ts b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip32.ts index f59497c8f..fb2d94012 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip32.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip32.ts @@ -3,103 +3,116 @@ import * as bip32crypto from "bip32/src/crypto"; import * as tinyecc from "tiny-secp256k1"; import { BIP32, Digest, SecP256K1 } from "../../core"; -import { ByteArray, Uint32, checkType, safeBufferFrom } from "../../types"; - +import { ByteArray, checkType, safeBufferFrom, Uint32 } from "../../types"; import { DummyEngineError, ParsedXpubTree } from "./types"; export * from "../../core/bip32"; -export class Seed implements BIP32.Seed { - readonly xpubTree: ParsedXpubTree; +export class Node implements BIP32.Node, SecP256K1.ECDSARecoverableKey, SecP256K1.ECDHKey { + readonly xpubTree: ParsedXpubTree; + + protected constructor(xpubTree: ParsedXpubTree) { + this.xpubTree = xpubTree; + } + + static async create(xpubTree: ParsedXpubTree): Promise { + return new Node(xpubTree); + } + + async getPublicKey(): Promise { + return this.xpubTree.publicKey; + } + + async getChainCode(): Promise { + return this.xpubTree.chainCode; + } + + async ecdsaSign(digestAlgorithm: null, msg: ByteArray<32>, counter?: Uint32): Promise; + async ecdsaSign( + digestAlgorithm: Digest.AlgorithmName<32>, + msg: Uint8Array, + counter?: Uint32 + ): Promise; + async ecdsaSign(): Promise { + throw new DummyEngineError(); + } + + async ecdsaSignRecoverable( + digestAlgorithm: null, + msg: ByteArray<32>, + counter?: Uint32 + ): Promise; + async ecdsaSignRecoverable( + digestAlgorithm: Digest.AlgorithmName<32>, + msg: Uint8Array, + counter?: Uint32 + ): Promise; + async ecdsaSignRecoverable(): Promise { + throw new DummyEngineError(); + } + + async derive(index: Uint32): Promise { + Uint32.assert(index); + const child = (() => { + const existingChild = this.xpubTree.children.get(index); + if (existingChild) return existingChild; + + if (index >= 0x80000000) throw new DummyEngineError(); + + const serP = Buffer.alloc(37); + serP.set(this.xpubTree.publicKey, 0); + serP.writeUInt32BE(index, 33); + const I = bip32crypto.hmacSHA512(safeBufferFrom(this.xpubTree.chainCode), serP); + const IL = I.slice(0, 32); + const IR = I.slice(32, 64); + const Ki = tinyecc.pointAddScalar(safeBufferFrom(this.xpubTree.publicKey), IL); + if (Ki === null) throw new Error("Ki is null; this should be cryptographically impossible"); + + const newChild = { + version: this.xpubTree.version, + depth: this.xpubTree.depth + 1, + parentFp: this.xpubTree.fingerprint, + childNum: index, + chainCode: checkType(BIP32.ChainCode, IR), + publicKey: checkType(SecP256K1.CompressedPoint, Ki), + fingerprint: new DataView(toArrayBuffer(Digest.Algorithms.hash160(Ki))).getUint32(0), + children: new Map(), + }; + + this.xpubTree.children.set(index, newChild); + return newChild; + })(); + const out = await Node.create(child); + return out as this; + } + + async ecdh(publicKey: SecP256K1.CurvePoint, digestAlgorithm?: Digest.AlgorithmName<32>): Promise>; + async ecdh(): Promise { + throw new DummyEngineError(); + } + + async ecdhRaw(publicKey: SecP256K1.CurvePoint): Promise; + async ecdhRaw(): Promise { + throw new DummyEngineError(); + } +} - protected constructor(xpubTree: ParsedXpubTree) { - this.xpubTree = xpubTree; - } +export class Seed implements BIP32.Seed { + readonly xpubTree: ParsedXpubTree; - static async create(xpubTree: ParsedXpubTree): Promise { - return new Seed(xpubTree); - } + protected constructor(xpubTree: ParsedXpubTree) { + this.xpubTree = xpubTree; + } - toMasterKey(): Promise - toMasterKey(hmacKey: string | Uint8Array): never - async toMasterKey(hmacKey?: string | Uint8Array): Promise { - if (hmacKey !== undefined) throw new Error("bad hmacKey type"); + static async create(xpubTree: ParsedXpubTree): Promise { + return new Seed(xpubTree); + } - return await Node.create(this.xpubTree); - }; -} + toMasterKey(): Promise; + toMasterKey(hmacKey: string | Uint8Array): never; + async toMasterKey(hmacKey?: string | Uint8Array): Promise { + if (hmacKey !== undefined) throw new Error("bad hmacKey type"); -export class Node implements BIP32.Node, SecP256K1.ECDSARecoverableKey, SecP256K1.ECDHKey { - readonly xpubTree: ParsedXpubTree; - - protected constructor(xpubTree: ParsedXpubTree) { - this.xpubTree = xpubTree; - } - - static async create(xpubTree: ParsedXpubTree): Promise { - return new Node(xpubTree); - } - - async getPublicKey(): Promise { - return this.xpubTree.publicKey; - } - - async getChainCode(): Promise { - return this.xpubTree.chainCode; - } - - async ecdsaSign(digestAlgorithm: null, msg: ByteArray<32>, counter?: Uint32): Promise - async ecdsaSign(digestAlgorithm: Digest.AlgorithmName<32>, msg: Uint8Array, counter?: Uint32): Promise - async ecdsaSign(digestAlgorithm: Digest.AlgorithmName<32> | null, msg: Uint8Array, counter?: Uint32): Promise { - throw new DummyEngineError(); - } - - async ecdsaSignRecoverable(digestAlgorithm: null, msg: ByteArray<32>, counter?: Uint32): Promise - async ecdsaSignRecoverable(digestAlgorithm: Digest.AlgorithmName<32>, msg: Uint8Array, counter?: Uint32): Promise - async ecdsaSignRecoverable(digestAlgorithm: Digest.AlgorithmName<32> | null, msg: Uint8Array, counter?: Uint32): Promise { - throw new DummyEngineError(); - } - - async derive(index: Uint32): Promise { - Uint32.assert(index); - const child = (() => { - const existingChild = this.xpubTree.children.get(index); - if (existingChild) return existingChild; - - if (index >= 0x80000000) throw new DummyEngineError(); - - let serP = Buffer.alloc(37); - serP.set(this.xpubTree.publicKey, 0); - serP.writeUInt32BE(index, 33); - const I = bip32crypto.hmacSHA512(safeBufferFrom(this.xpubTree.chainCode), serP); - const IL = I.slice(0, 32); - const IR = I.slice(32, 64); - const Ki = tinyecc.pointAddScalar(safeBufferFrom(this.xpubTree.publicKey), IL); - if (Ki === null) throw new Error("Ki is null; this should be cryptographically impossible"); - - const newChild = { - version: this.xpubTree.version, - depth: this.xpubTree.depth + 1, - parentFp: this.xpubTree.fingerprint, - childNum: index, - chainCode: checkType(BIP32.ChainCode, IR), - publicKey: checkType(SecP256K1.CompressedPoint, Ki), - fingerprint: new DataView(toArrayBuffer(Digest.Algorithms.hash160(Ki))).getUint32(0), - children: new Map() - }; - - this.xpubTree.children.set(index, newChild); - return newChild; - })(); - const out = await Node.create(child); - return out as this; - } - - async ecdh(publicKey: SecP256K1.CurvePoint, digestAlgorithm?: Digest.AlgorithmName<32>): Promise> { - throw new DummyEngineError() - } - - async ecdhRaw(publicKey: SecP256K1.CurvePoint): Promise { - throw new DummyEngineError() - } + return await Node.create(this.xpubTree); + } } diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip39.ts b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip39.ts index d3480026b..ff02bbe0a 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip39.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/bip39.ts @@ -6,66 +6,67 @@ import * as BIP39 from "../../core/bip39"; import * as Digest from "../../core/digest"; import * as SecP256K1 from "../../core/secp256k1"; import { checkType } from "../../types"; - import * as BIP32Engine from "./bip32"; import { ParsedXpubTree } from "./types"; export * from "../../core/bip39"; export class Mnemonic implements BIP39.Mnemonic { - readonly xpubTree: ParsedXpubTree; - - protected constructor(xpubTree: ParsedXpubTree) { - this.xpubTree = xpubTree; - } + readonly xpubTree: ParsedXpubTree; - static async create(xpubList: string): Promise { - const parsedXpubs: ParsedXpubTree[] = xpubList.split(" ").map(xpub => { - const xpubBuf = bs58check.decode(xpub); - if (xpubBuf.length !== 78) throw new Error("Isolation.Engine.Dummy.BIP39.create - Invalid xpub") - const xpubView = new DataView(toArrayBuffer(xpubBuf)); - const pk = checkType(SecP256K1.CompressedPoint, xpubBuf.slice(45)); - return { - version: xpubView.getUint32(0), - depth: xpubView.getUint8(4), - parentFp: xpubView.getUint32(5), - childNum: xpubView.getUint32(9), - chainCode: checkType(BIP32.ChainCode, xpubBuf.slice(13, 45)), - publicKey: pk, - fingerprint: new DataView(toArrayBuffer(Digest.Algorithms.hash160(pk))).getUint32(0), - children: new Map() - }; - }); + protected constructor(xpubTree: ParsedXpubTree) { + this.xpubTree = xpubTree; + } - const tree: ParsedXpubTree = (() => { - const rootXpubs = parsedXpubs.filter(x => x.parentFp === 0x00000000); - if (rootXpubs.length === 0) throw new Error("can't find root xpub"); - if (rootXpubs.length > 1) throw new Error("more than one root xpub"); - return rootXpubs[0]; - })(); + static async create(xpubList: string): Promise { + const parsedXpubs: ParsedXpubTree[] = xpubList.split(" ").map((xpub) => { + const xpubBuf = bs58check.decode(xpub); + if (xpubBuf.length !== 78) throw new Error("Isolation.Engine.Dummy.BIP39.create - Invalid xpub"); + const xpubView = new DataView(toArrayBuffer(xpubBuf)); + const pk = checkType(SecP256K1.CompressedPoint, xpubBuf.slice(45)); + return { + version: xpubView.getUint32(0), + depth: xpubView.getUint8(4), + parentFp: xpubView.getUint32(5), + childNum: xpubView.getUint32(9), + chainCode: checkType(BIP32.ChainCode, xpubBuf.slice(13, 45)), + publicKey: pk, + fingerprint: new DataView(toArrayBuffer(Digest.Algorithms.hash160(pk))).getUint32(0), + children: new Map(), + }; + }); - const xpubsByFp = parsedXpubs.map(xpub => { - return [xpub.fingerprint, xpub] as const - }).reduce>((a, [k, v]) => { - if (k in a) throw new Error("key fingerprint collision"); - a[k] = v; - return a; - }, {}); + const tree: ParsedXpubTree = (() => { + const rootXpubs = parsedXpubs.filter((x) => x.parentFp === 0x00000000); + if (rootXpubs.length === 0) throw new Error("can't find root xpub"); + if (rootXpubs.length > 1) throw new Error("more than one root xpub"); + return rootXpubs[0]; + })(); - for (const xpub of parsedXpubs.filter(x => x !== tree)) { - if (!(xpub.parentFp in xpubsByFp)) throw new Error("found xpub, but not its parent"); - xpubsByFp[xpub.parentFp].children.set(xpub.childNum, xpub); - } + const xpubsByFp = parsedXpubs + .map((xpub) => { + return [xpub.fingerprint, xpub] as const; + }) + .reduce>((a, [k, v]) => { + if (k in a) throw new Error("key fingerprint collision"); + a[k] = v; + return a; + }, {}); - return new Mnemonic(tree); + for (const xpub of parsedXpubs.filter((x) => x !== tree)) { + if (!(xpub.parentFp in xpubsByFp)) throw new Error("found xpub, but not its parent"); + xpubsByFp[xpub.parentFp].children.set(xpub.childNum, xpub); } - toSeed(): Promise - toSeed(passphrase: ""): Promise - toSeed(passphrase: string): never - async toSeed(passphrase?: string): Promise { - if (passphrase !== undefined && passphrase !== "") throw new Error("bad passphrase type"); + return new Mnemonic(tree); + } + + toSeed(): Promise; + toSeed(passphrase: ""): Promise; + toSeed(passphrase: string): never; + async toSeed(passphrase?: string): Promise { + if (passphrase !== undefined && passphrase !== "") throw new Error("bad passphrase type"); - return await BIP32Engine.Seed.create(this.xpubTree); - }; + return await BIP32Engine.Seed.create(this.xpubTree); + } } diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/index.test.ts b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/index.test.ts index f4c8d4d48..cd24ef7e7 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/index.test.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/index.test.ts @@ -7,31 +7,45 @@ describe("Isolation.Engines.Dummy", () => { let masterKey: Core.BIP32.Node; it("can be loaded with a list of xpubs", async () => { - mnemonic = await Dummy.BIP39.Mnemonic.create([ - "xpub661MyMwAqRbcFLgDU7wpcEVubSF7NkswwmXBUkDiGUW6uopeUMys4AqKXNgpfZKRTLnpKQgffd6a2c3J8JxLkF1AQN17Pm9QYHEqEfo1Rsx", // all seed root key - "xpub68Zyu13qjcQxDzLNfTYnUXtJuX2qJgnxP6osrcAvJGdo6bs9M2Adt2BunbwiYrZS5qpA1QKoMf3uqS2NHpbyZp4KMJxDrL58NTyvHXBeAv6", // all seed m/44' - "xpub6APRH5kELakva27TFbzpfhfsY3Jd4dRGo7NocHb63qWecSgK2dUkjWaYevJsCunicpdAkPg9fvHAdpSFMDCMCDMit8kiTM1w9QoGmfyVwDo", // all seed m/44'/0' - "xpub6BiVtCpG9fQPxnPmHXG8PhtzQdWC2Su4qWu6XW9tpWFYhxydCLJGrWBJZ5H6qTAHdPQ7pQhtpjiYZVZARo14qHiay2fvrX996oEP42u8wZy", // all seed m/44'/0'/0' - "xpub6APRH5kELakyDsZMmBU9HEoeRUzM9F8STp6ztXLPUJQLiXGrbsfACbngkw5vySPfa9vFs2p3kMsRPxhyDTLhKYEf5HLVfDcDuTTazgzvArk", // all seed m/44'/60' - "xpub6CNFa58kEQJu2hwMVoofpDEKVVSg6gfwqBqE2zHAianaUnQkrJzJJ42iLDp7Dmg2aP88qCKoFZ4jidk3tECdQuF4567NGHDfe7iBRwHxgke", // all seed m/44'/60'/0' - "xpub68Zyu13qjcQxUZiesSWiHJMqkg8G8Guft6MvDhwP72zSYXr9iKnNmDo7LxuSVwtpamrNwGQHkGDWoK8MAp3S9GW5fVxsjBY6AdvZc1hB7kK", // all seed m/49' - "xpub6AA5piovovuKytxa5QtBWAbixSjg7fbmu5gqs6QmvARrUMgewJV51roNH4M7GtvZmjBY1m5oAgAjoHivasewSh4S2H7LAikCyuhJxfHdSsK", // all seed m/49'/0' - "xpub6CVKsQYXc9awxgV1tWbG4foDvdcnieK2JkbpPEBKB5WwAPKBZ1mstLbKVB4ov7QzxzjaxNK6EfmNY5Jsk2cG26EVcEkycGW4tchT2dyUhrx", // all seed m/49'/0'/0' - "xpub68Zyu13qjcQz2DTzkBfLNCfsCTgT39rsUY9JT7MFvG3oEJvS8gUYwRX4RheUTFGZ6EtW4dFYhCdBX32GHJCodkQLAARjNsw4Drj1oDxvo9p", // all seed m/84' - "xpub69s3dQnszuX49hTwhNAQEMJyTcRQNZyhtKAqNgQXApquzXdR3fEjXg75ScXzMMMLkUjQnz2Giwt2L7vesiswkAYwzbHezaUXayU8Z81CW56", // all seed m/84'/0' - "xpub6DDUPHpUo4pcy43iJeZjbSVWGav1SMMmuWdMHiGtkK8rhKmfbomtkwW6GKs1GGAKehT6QRocrmda3WWxXawpjmwaUHfFRXuKrXSapdckEYF", // all seed m/84'/0'/0' - ].join(" ")); + await expect( + (async () => { + mnemonic = await Dummy.BIP39.Mnemonic.create( + [ + "xpub661MyMwAqRbcFLgDU7wpcEVubSF7NkswwmXBUkDiGUW6uopeUMys4AqKXNgpfZKRTLnpKQgffd6a2c3J8JxLkF1AQN17Pm9QYHEqEfo1Rsx", // all seed root key + "xpub68Zyu13qjcQxDzLNfTYnUXtJuX2qJgnxP6osrcAvJGdo6bs9M2Adt2BunbwiYrZS5qpA1QKoMf3uqS2NHpbyZp4KMJxDrL58NTyvHXBeAv6", // all seed m/44' + "xpub6APRH5kELakva27TFbzpfhfsY3Jd4dRGo7NocHb63qWecSgK2dUkjWaYevJsCunicpdAkPg9fvHAdpSFMDCMCDMit8kiTM1w9QoGmfyVwDo", // all seed m/44'/0' + "xpub6BiVtCpG9fQPxnPmHXG8PhtzQdWC2Su4qWu6XW9tpWFYhxydCLJGrWBJZ5H6qTAHdPQ7pQhtpjiYZVZARo14qHiay2fvrX996oEP42u8wZy", // all seed m/44'/0'/0' + "xpub6APRH5kELakyDsZMmBU9HEoeRUzM9F8STp6ztXLPUJQLiXGrbsfACbngkw5vySPfa9vFs2p3kMsRPxhyDTLhKYEf5HLVfDcDuTTazgzvArk", // all seed m/44'/60' + "xpub6CNFa58kEQJu2hwMVoofpDEKVVSg6gfwqBqE2zHAianaUnQkrJzJJ42iLDp7Dmg2aP88qCKoFZ4jidk3tECdQuF4567NGHDfe7iBRwHxgke", // all seed m/44'/60'/0' + "xpub68Zyu13qjcQxUZiesSWiHJMqkg8G8Guft6MvDhwP72zSYXr9iKnNmDo7LxuSVwtpamrNwGQHkGDWoK8MAp3S9GW5fVxsjBY6AdvZc1hB7kK", // all seed m/49' + "xpub6AA5piovovuKytxa5QtBWAbixSjg7fbmu5gqs6QmvARrUMgewJV51roNH4M7GtvZmjBY1m5oAgAjoHivasewSh4S2H7LAikCyuhJxfHdSsK", // all seed m/49'/0' + "xpub6CVKsQYXc9awxgV1tWbG4foDvdcnieK2JkbpPEBKB5WwAPKBZ1mstLbKVB4ov7QzxzjaxNK6EfmNY5Jsk2cG26EVcEkycGW4tchT2dyUhrx", // all seed m/49'/0'/0' + "xpub68Zyu13qjcQz2DTzkBfLNCfsCTgT39rsUY9JT7MFvG3oEJvS8gUYwRX4RheUTFGZ6EtW4dFYhCdBX32GHJCodkQLAARjNsw4Drj1oDxvo9p", // all seed m/84' + "xpub69s3dQnszuX49hTwhNAQEMJyTcRQNZyhtKAqNgQXApquzXdR3fEjXg75ScXzMMMLkUjQnz2Giwt2L7vesiswkAYwzbHezaUXayU8Z81CW56", // all seed m/84'/0' + "xpub6DDUPHpUo4pcy43iJeZjbSVWGav1SMMmuWdMHiGtkK8rhKmfbomtkwW6GKs1GGAKehT6QRocrmda3WWxXawpjmwaUHfFRXuKrXSapdckEYF", // all seed m/84'/0'/0' + ].join(" ") + ); + })() + ).resolves.not.toThrow(); + expect(mnemonic).toBeDefined(); }); it("produces a seed", async () => { - seed = await mnemonic.toSeed(); - }) + await expect( + (async () => { + seed = await mnemonic.toSeed(); + })() + ).resolves.not.toThrow(); + expect(seed).toBeDefined(); + }); it("produces a master key", async () => { masterKey = await seed.toMasterKey(); - const pk = await masterKey.getPublicKey() - expect(Buffer.from(pk).toString("hex")).toEqual("03e3b30e8c21923752a408242e069941fedbaef7db7161f7e2c5f3fdafe7e25ddc"); - }) + const pk = await masterKey.getPublicKey(); + expect(Buffer.from(pk).toString("hex")).toEqual( + "03e3b30e8c21923752a408242e069941fedbaef7db7161f7e2c5f3fdafe7e25ddc" + ); + }); it.each([ ["m/44'", "034d600165882b6faf32a3f1f2c4755eeb0f0486954718d46fd9621e8ca40ca6b6"], @@ -76,4 +90,4 @@ describe("Isolation.Engines.Dummy", () => { const pk = Buffer.from(await node.getPublicKey()).toString("hex"); expect(pk).toEqual(expectedPk); }); -}) +}); diff --git a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/types.ts b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/types.ts index 045d0e678..3164004f3 100644 --- a/packages/hdwallet-native/src/crypto/isolation/engines/dummy/types.ts +++ b/packages/hdwallet-native/src/crypto/isolation/engines/dummy/types.ts @@ -8,15 +8,15 @@ export class DummyEngineError extends Error { } export interface ParsedXpub { - version: number - depth: number - parentFp: number - childNum: number - chainCode: ChainCode - publicKey: CompressedPoint + version: number; + depth: number; + parentFp: number; + childNum: number; + chainCode: ChainCode; + publicKey: CompressedPoint; } export type ParsedXpubTree = ParsedXpub & { - fingerprint: number, - children: Map -} + fingerprint: number; + children: Map; +}; diff --git a/packages/hdwallet-native/src/crypto/isolation/types.ts b/packages/hdwallet-native/src/crypto/isolation/types.ts index aa05f5d15..7d91f6dd5 100644 --- a/packages/hdwallet-native/src/crypto/isolation/types.ts +++ b/packages/hdwallet-native/src/crypto/isolation/types.ts @@ -1,22 +1,34 @@ -import { Number as Num, InstanceOf, Object as Obj, Literal, Static, Unknown, String as Str, Never, Runtype, Result } from "funtypes"; - -const positive = Num.withConstraint(x => x > 0 || `expected ${x} to be positive`, {name: "Positive"}); +import { + InstanceOf, + Literal, + Never, + Number as Num, + Object as Obj, + Runtype, + Static, + String as Str, + Unknown, +} from "funtypes"; + +const positive = Num.withConstraint((x) => x > 0 || `expected ${x} to be positive`, { name: "Positive" }); export type Positive = Static; export const Positive: typeof positive = positive; -const negative = Num.withConstraint(x => x < 0 || `expected ${x} to be negative`, {name: "Negative"}); +const negative = Num.withConstraint((x) => x < 0 || `expected ${x} to be negative`, { name: "Negative" }); export type Negative = Static; export const Negative: typeof negative = negative; -const nonNegative = Num.withConstraint(x => x >= 0 || `expected ${x} to be non-negative`, {name: "NonNegative"}); +const nonNegative = Num.withConstraint((x) => x >= 0 || `expected ${x} to be non-negative`, { name: "NonNegative" }); export type NonNegative = Static; export const NonNegative: typeof nonNegative = nonNegative; -const nonPositive = Num.withConstraint(x => x <= 0 || `expected ${x} to be non-positive`, {name: "NonPositive"}); +const nonPositive = Num.withConstraint((x) => x <= 0 || `expected ${x} to be non-positive`, { name: "NonPositive" }); export type NonPositive = Static; export const NonPositive: typeof nonPositive = nonPositive; -export const integer = Num.withConstraint(x => Number.isSafeInteger(x) || `expected ${x} to be an integer`, {name: "Integer"}); +export const integer = Num.withConstraint((x) => Number.isSafeInteger(x) || `expected ${x} to be an integer`, { + name: "Integer", +}); export type Integer = Static; export const Integer: typeof integer = integer; @@ -40,47 +52,58 @@ const uint = NonNegativeInteger.Or(Never); export type Uint = Static; export const Uint: typeof uint = uint; -const uint32 = Num.withConstraint(x => x === ((x & 0xffffffff) >>> 0) || `expected ${x} to be an unsigned 32-bit integer.`, {name: "Uint32"}); +const uint32 = Num.withConstraint( + (x) => x === (x & 0xffffffff) >>> 0 || `expected ${x} to be an unsigned 32-bit integer.`, + { name: "Uint32" } +); export type Uint32 = Static; export const Uint32: typeof uint32 = uint32; -const uint16 = Num.withConstraint(x => x === ((x & 0xffff) >>> 0) || `expected ${x} to be an unsigned 16-bit integer`, {name: "Uint16"}); +const uint16 = Num.withConstraint((x) => x === (x & 0xffff) >>> 0 || `expected ${x} to be an unsigned 16-bit integer`, { + name: "Uint16", +}); export type Uint16 = Static; export const Uint16: typeof uint16 = uint16; -const uint8 = Num.withConstraint(x => x === ((x & 0xff) >>> 0) || `expected ${x} to be an unsigned 8-bit integer`, {name: "Uint8"}); +const uint8 = Num.withConstraint((x) => x === (x & 0xff) >>> 0 || `expected ${x} to be an unsigned 8-bit integer`, { + name: "Uint8", +}); export type Uint8 = Static; export const Uint8: typeof uint8 = uint8; function boundedUintBase(max: T) { - return Object.assign(Uint.withConstraint( - x => x <= max || `expected ${x} to be less than or equal to ${max}`, - {name: `BoundedUint(${max})`}, - ), {max}); + return Object.assign( + Uint.withConstraint((x) => x <= max || `expected ${x} to be less than or equal to ${max}`, { + name: `BoundedUint(${max})`, + }), + { max } + ); } -export type BoundedUint = Static & {max: T}; +export type BoundedUint = Static & { max: T }; const boundedUintStatic = {}; const boundedUint = Object.assign(boundedUintBase, boundedUintStatic); export const BoundedUint: typeof boundedUint = boundedUint; function boundedStringBase(regex: T) { - return Object.assign(Str.withConstraint( - x => regex.test(x) || `expected ${x} to match regex ${regex}`, - {name: `BoundedString(${regex})`}, - ), {regex}); + return Object.assign( + Str.withConstraint((x) => regex.test(x) || `expected ${x} to match regex ${regex}`, { + name: `BoundedString(${regex})`, + }), + { regex } + ); } -export type BoundedString = Static & {regex: T}; +export type BoundedString = Static & { regex: T }; const boundedStringStatic = {}; const boundedString = Object.assign(boundedStringBase, boundedStringStatic); export const BoundedString: typeof boundedString = boundedString; export function assertType>(rt: T, value: unknown): asserts value is Static { - rt.assert(value); + rt.assert(value); } export function checkType>(rt: T, value: unknown): Static { - assertType(rt, value); - return value; + assertType(rt, value); + return value; } // export function ParseWorkaround>(rt: U) { @@ -94,42 +117,47 @@ export function checkType>(rt: T, value: unknown): St type Ternary = T extends undefined ? V : unknown extends T ? V : U; function byteArrayBase(length?: T) { - length === undefined || NonNegativeInteger.assert(length); - const indefinite = InstanceOf(Uint8Array); - const literalConstraint = (length !== undefined ? Literal(length) : Unknown); - const definite = InstanceOf(Uint8Array).And(Obj({ - length: literalConstraint as Literal, - })); - return (length === undefined ? indefinite : definite) as Ternary; + length === undefined || NonNegativeInteger.assert(length); + const indefinite = InstanceOf(Uint8Array); + const literalConstraint = length !== undefined ? Literal(length) : Unknown; + const definite = InstanceOf(Uint8Array).And( + Obj({ + length: literalConstraint as Literal, + }) + ); + return (length === undefined ? indefinite : definite) as Ternary; } -export type ByteArray = Uint8Array & (T extends undefined ? unknown : {length: T}); +export type ByteArray = Uint8Array & + (T extends undefined ? unknown : { length: T }); const byteArrayStatic = { - equal(lhs: ByteArray, rhs: ByteArray): boolean { - const length = lhs.length; - if (length !== rhs.length) return false; - for (let i = 0; i < length; i++) { - if (lhs[i] !== rhs[i]) return false; - } - return true; + equal(lhs: ByteArray, rhs: ByteArray): boolean { + const length = lhs.length; + if (length !== rhs.length) return false; + for (let i = 0; i < length; i++) { + if (lhs[i] !== rhs[i]) return false; } + return true; + }, }; const byteArray = Object.assign(byteArrayBase, byteArrayStatic); export const ByteArray: typeof byteArray = byteArray; function bigEndianIntegerBase(length?: T) { - return ByteArray(length); + return ByteArray(length); } export type BigEndianInteger = ByteArray; const bigEndianIntegerStatic = { - isHigh: (x: BigEndianInteger) => (x[0] & 0x80) !== 0, - isOdd: (x: BigEndianInteger) => (x[x.length - 1] & 1) === 1, + isHigh: (x: BigEndianInteger) => (x[0] & 0x80) !== 0, + isOdd: (x: BigEndianInteger) => (x[x.length - 1] & 1) === 1, }; const bigEndianInteger = Object.assign(bigEndianIntegerBase, ByteArray, bigEndianIntegerStatic); export const BigEndianInteger: typeof bigEndianInteger = bigEndianInteger; -export function safeBufferFrom(input: ByteArray): Buffer & ByteArray { - if (Buffer.isBuffer(input)) return input; - input = checkType(ByteArray(), input) as ByteArray; - return Buffer.alloc(input.byteLength).fill(input) as Buffer & ByteArray; +export function safeBufferFrom( + input: ByteArray +): Buffer & ByteArray { + if (Buffer.isBuffer(input)) return input; + input = checkType(ByteArray(), input) as ByteArray; + return Buffer.alloc(input.byteLength).fill(input) as Buffer & ByteArray; } diff --git a/packages/hdwallet-native/src/ethereum.test.ts b/packages/hdwallet-native/src/ethereum.test.ts index 1f29be802..c755d1142 100644 --- a/packages/hdwallet-native/src/ethereum.test.ts +++ b/packages/hdwallet-native/src/ethereum.test.ts @@ -1,5 +1,5 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import * as ethers from "ethers" +import * as ethers from "ethers"; import { TextEncoder } from "web-encoding"; import * as native from "./native"; @@ -53,11 +53,12 @@ describe("NativeETHWallet", () => { }); // Reflection. Surprise. Terror. For the future. - /*it("should generate another correct ethereum address", async () => { - expect( - await wallet.ethGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/60'/1337'/123/4") }) - ).toBe("0x387F3031b30E2c8eB997E87a69FEA02756983b77"); - });*/ + // eslint-disable-next-line jest/no-disabled-tests + it.skip("should generate another correct ethereum address", async () => { + expect(await wallet.ethGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/60'/1337'/123/4") })).toBe( + "0x387F3031b30E2c8eB997E87a69FEA02756983b77" + ); + }); it("fails when generating another ethereum address", async () => { await expect( @@ -109,7 +110,7 @@ describe("NativeETHWallet", () => { data: "0xCAFEDEADCAFEDEADCAFEDEADCAFEDEAD", chainId: 1, }); - console.log("SIG: ", sig) + console.debug("SIG: ", sig); // This is the output from tiny-secp256k1. expect(sig).toMatchInlineSnapshot(` Object { diff --git a/packages/hdwallet-native/src/ethereum.ts b/packages/hdwallet-native/src/ethereum.ts index 0da2ee9bf..9d2914c95 100644 --- a/packages/hdwallet-native/src/ethereum.ts +++ b/packages/hdwallet-native/src/ethereum.ts @@ -2,10 +2,11 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as ethers from "ethers"; import _ from "lodash"; -import { NativeHDWalletBase } from "./native"; import * as Isolation from "./crypto/isolation"; +import { NativeHDWalletBase } from "./native"; export function MixinNativeETHWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeETHWalletInfo extends Base implements core.ETHWalletInfo { readonly _supportsETHInfo = true; @@ -38,6 +39,7 @@ export function MixinNativeETHWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeETHWallet extends Base { readonly _supportsETH = true; #ethSigner: ethers.Signer | undefined; async ethInitializeWallet(masterKey: Isolation.Core.BIP32.Node): Promise { - const rootNode = await Isolation.Adapters.BIP32.create(masterKey) + const rootNode = await Isolation.Adapters.BIP32.create(masterKey); const isolatedSigner = await rootNode.derivePath(ethers.utils.defaultPath); this.#ethSigner = await Isolation.Adapters.Ethereum.create(isolatedSigner.node); } @@ -66,6 +69,7 @@ export function MixinNativeETHWallet this.#ethSigner!.getAddress()); } @@ -73,6 +77,7 @@ export function MixinNativeETHWallet { const utx = { to: msg.to, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion from: await this.#ethSigner!.getAddress(), nonce: msg.nonce, gasLimit: msg.gasLimit, @@ -80,14 +85,16 @@ export function MixinNativeETHWallet { return this.needsMnemonic(!!this.#ethSigner, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const result = await this.#ethSigner!.signMessage(msg.message); return { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion address: await this.#ethSigner!.getAddress(), signature: result, }; diff --git a/packages/hdwallet-native/src/fio.test.ts b/packages/hdwallet-native/src/fio.test.ts index 90b1e8e15..7eab7f62e 100644 --- a/packages/hdwallet-native/src/fio.test.ts +++ b/packages/hdwallet-native/src/fio.test.ts @@ -3,8 +3,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as bs58 from "bs58"; import * as tinyecc from "tiny-secp256k1"; -import * as native from "./native"; import * as Isolation from "./crypto/isolation"; +import * as native from "./native"; const MNEMONIC = "all all all all all all all all all all all all"; @@ -172,7 +172,7 @@ describe("NativeFioWallet", () => { expect(address).toBe("FIO8Pok1EBMsZWphhfUj2m3MXrxBZJq7erqyi7E1teAfZE18HyjK5"); }); - it("should generate another correct FIO address", async () => { + it("should generate a third correct FIO address", async () => { const address = await wallet.fioGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/235'/1'/0/0") }); expect(address).toMatchInlineSnapshot(`"FIO8HiUzsRDYo69AEmUk39f7h7nawTjn9msbX6oUY5wrX6ERCh3rA"`); }); diff --git a/packages/hdwallet-native/src/fio.ts b/packages/hdwallet-native/src/fio.ts index 3b1448046..80ba9ff42 100644 --- a/packages/hdwallet-native/src/fio.ts +++ b/packages/hdwallet-native/src/fio.ts @@ -2,8 +2,8 @@ import * as fio from "@shapeshiftoss/fiosdk"; import * as core from "@shapeshiftoss/hdwallet-core"; import fetch, { RequestInfo, RequestInit } from "node-fetch"; -import { NativeHDWalletBase } from "./native"; import * as Isolation from "./crypto/isolation"; +import { NativeHDWalletBase } from "./native"; const fetchJson = async (uri: RequestInfo, opts?: RequestInit) => { return fetch(uri, opts); @@ -16,6 +16,7 @@ async function getKeyPair(masterKey: Isolation.Core.BIP32.Node, addressNList: nu } export function MixinNativeFioWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeFioWalletInfo extends Base implements core.FioWalletInfo { readonly _supportsFioInfo = true; @@ -39,6 +40,7 @@ export function MixinNativeFioWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeFioWallet extends Base { readonly _supportsFio = true; baseUrl = "https://fio.eu.eosamsterdam.net/v1/"; @@ -63,6 +66,7 @@ export function MixinNativeFioWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const key = await getKeyPair(this.#masterKey!, addressNList); const sdk = new fio.FIOSDK(key as any, key.publicKey, this.baseUrl, fetchJson); sdk.setSignedTrxReturnOption(true); @@ -82,7 +86,7 @@ export function MixinNativeFioWallet = {}; switch (action.name) { case "addaddress": { @@ -92,18 +96,20 @@ export function MixinNativeFioWallet(msg: core.FioEncryptRequestContentMsg): Promise { + async fioEncryptRequestContent( + msg: core.FioEncryptRequestContentMsg + ): Promise { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const privateKey = await getKeyPair(this.#masterKey!, msg.addressNList); const sdk = core.mustBeDefined(await this.getFioSdk(msg.addressNList)); - return await sdk.transactions.getCipherContent(msg.contentType, msg.content, privateKey, msg.publicKey, msg.iv && Buffer.from(msg.iv)); + return await sdk.transactions.getCipherContent( + msg.contentType, + msg.content, + privateKey, + msg.publicKey, + msg.iv && Buffer.from(msg.iv) + ); }); } - async fioDecryptRequestContent(msg: core.FioDecryptRequestContentMsg): Promise | null> { + async fioDecryptRequestContent( + msg: core.FioDecryptRequestContentMsg + ): Promise | null> { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const privateKey = await getKeyPair(this.#masterKey!, msg.addressNList); const sdk = core.mustBeDefined(await this.getFioSdk(msg.addressNList)); - return await sdk.transactions.getUnCipherContent(msg.contentType, JSON.stringify(msg.content), privateKey, msg.publicKey); + return await sdk.transactions.getUnCipherContent( + msg.contentType, + JSON.stringify(msg.content), + privateKey, + msg.publicKey + ); }); } }; diff --git a/packages/hdwallet-native/src/kava.test.ts b/packages/hdwallet-native/src/kava.test.ts index d9ddc5334..99d1daf7e 100644 --- a/packages/hdwallet-native/src/kava.test.ts +++ b/packages/hdwallet-native/src/kava.test.ts @@ -38,9 +38,9 @@ describe("NativeKavaWallet", () => { }); it("should generate a correct kava address", async () => { - await expect( - wallet.kavaGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/459'/0'/0/0") }) - ).resolves.toBe("kava1x9eec99f6m9d0nc3my4uyw55jefkcxj8dwxcpu"); + await expect(wallet.kavaGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/459'/0'/0/0") })).resolves.toBe( + "kava1x9eec99f6m9d0nc3my4uyw55jefkcxj8dwxcpu" + ); }); it("should generate another correct kava address", async () => { @@ -64,7 +64,7 @@ describe("NativeKavaWallet", () => { chain_id: "foobar", account_number: "foo", sequence: "bar", - }) + }); expect(signed?.signatures?.length).toBe(1); expect(signed?.signatures?.[0].pub_key?.value).toMatchInlineSnapshot( `"AlW0vIrn08ANEFwNKufFfnoU/1pTSjyzo8SMlPKoit3V"` @@ -72,6 +72,5 @@ describe("NativeKavaWallet", () => { expect(signed?.signatures?.[0].signature).toMatchInlineSnapshot( `"77iUQFCVMfXvEj11YOEdMWOC4KDYxZzRz0WKzRFWnX18AwetdDh15be+iFsVgZ4RJFl2jj1JYoCGKrd9YN+Eew=="` ); - }); }); diff --git a/packages/hdwallet-native/src/kava.ts b/packages/hdwallet-native/src/kava.ts index 9a7005d31..2aa21228c 100644 --- a/packages/hdwallet-native/src/kava.ts +++ b/packages/hdwallet-native/src/kava.ts @@ -3,11 +3,12 @@ import * as bech32 from "bech32"; import CryptoJS from "crypto-js"; import * as txBuilder from "tendermint-tx-builder"; +import * as Isolation from "./crypto/isolation"; import { NativeHDWalletBase } from "./native"; import * as util from "./util"; -import * as Isolation from "./crypto/isolation"; export function MixinNativeKavaWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeKavaWalletInfo extends Base implements core.KavaWalletInfo { readonly _supportsKavaInfo = true; @@ -24,7 +25,7 @@ export function MixinNativeKavaWalletInfo { - const slip44 = core.slip44ByCoin("Kava") + const slip44 = core.slip44ByCoin("Kava"); return [ { addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0], @@ -32,6 +33,7 @@ export function MixinNativeKavaWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeKavaWallet extends Base { readonly _supportsKava = true; @@ -67,6 +70,7 @@ export function MixinNativeKavaWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "kava"); return this.createKavaAddress(keyPair.publicKey.toString("hex")); }); @@ -74,9 +78,9 @@ export function MixinNativeKavaWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "kava"); // @TODO: This needs to be fixed after the change to tendermint serialization - // @ts-ignore const adapter = await Isolation.Adapters.Cosmos.create(keyPair); const result = await txBuilder.sign(msg.tx, adapter, msg.sequence, msg.account_number, msg.chain_id); return txBuilder.createSignedTx(msg.tx, result); diff --git a/packages/hdwallet-native/src/modules.d.ts b/packages/hdwallet-native/src/modules.d.ts index ae334ed82..f436eb964 100644 --- a/packages/hdwallet-native/src/modules.d.ts +++ b/packages/hdwallet-native/src/modules.d.ts @@ -1,116 +1,116 @@ /// -declare module 'ethereum-tx-decoder'; -declare module '@shapeshiftoss/fiojs/dist/ecc'; -declare module 'bip32/src/crypto' { - export * from "bip32/types/crypto"; +declare module "ethereum-tx-decoder"; +declare module "@shapeshiftoss/fiojs/dist/ecc"; +declare module "bip32/src/crypto" { + export * from "bip32/types/crypto"; } // These come from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/7e1a46771ee0841d6c24219e0b138689512b6df6/types/tiny-secp256k1/index.d.ts, // except for the additional definition of signWithEntropy() which is omitted in // the DefinitelyTyped package. -declare module 'tiny-secp256k1' { - // Type definitions for tiny-secp256k1 1.0 - // Project: https://github.com/bitcoinjs/tiny-secp256k1 - // Definitions by: Eduardo Henke - // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - /// +declare module "tiny-secp256k1" { + // Type definitions for tiny-secp256k1 1.0 + // Project: https://github.com/bitcoinjs/tiny-secp256k1 + // Definitions by: Eduardo Henke + // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + /// - /** - * Checks if A is a point in the curve - * @param A should be: - * encoded with a sequence tag of 0x02, 0x03 or 0x04 - * A.x is within [1...p - 1] - * A.y is within [1...p - 1] - */ - export function isPoint(A: Buffer): boolean; + /** + * Checks if A is a point in the curve + * @param A should be: + * encoded with a sequence tag of 0x02, 0x03 or 0x04 + * A.x is within [1...p - 1] + * A.y is within [1...p - 1] + */ + export function isPoint(A: Buffer): boolean; - /** - * Returns false if the point is not compressed. - */ - export function isPointCompressed(A: Buffer): boolean; + /** + * Returns false if the point is not compressed. + */ + export function isPointCompressed(A: Buffer): boolean; - /** - * Checks if point is private key - * @param d should be: - * 256-bit - * within [1...order - 1] - */ - export function isPrivate(d: Buffer): boolean; + /** + * Checks if point is private key + * @param d should be: + * 256-bit + * within [1...order - 1] + */ + export function isPrivate(d: Buffer): boolean; - /** - * Returns null if result is at infinity. - * @param A isPoint(A) should be true - * @param B isPoint(B) should be true - * @param compressed optional, if true compresses the resulting point - */ - export function pointAdd(A: Buffer, B: Buffer, compressed?: boolean): Buffer | null; + /** + * Returns null if result is at infinity. + * @param A isPoint(A) should be true + * @param B isPoint(B) should be true + * @param compressed optional, if true compresses the resulting point + */ + export function pointAdd(A: Buffer, B: Buffer, compressed?: boolean): Buffer | null; - /** - * Returns null if result is at infinity. - * @param A isPoint(A) should be true - * @param tweak should be within [1...order - 1] - * @param compressed optional, if true compresses the resulting point - */ - export function pointAddScalar(A: Buffer, tweak: Buffer, compressed?: boolean): Buffer | null; + /** + * Returns null if result is at infinity. + * @param A isPoint(A) should be true + * @param tweak should be within [1...order - 1] + * @param compressed optional, if true compresses the resulting point + */ + export function pointAddScalar(A: Buffer, tweak: Buffer, compressed?: boolean): Buffer | null; - /** - * Compresses point A. - * @param A isPoint(A) should be true - * @param compressed if true compresses A - */ - export function pointCompress(A: Buffer, compressed: boolean): Buffer; + /** + * Compresses point A. + * @param A isPoint(A) should be true + * @param compressed if true compresses A + */ + export function pointCompress(A: Buffer, compressed: boolean): Buffer; - /** - * Returns null if result is at infinity. - * @param d isPrivate(d) should be true - * @param compressed optional, if true compresses the resulting point - */ - export function pointFromScalar(d: Buffer, compressed?: boolean): Buffer | null; + /** + * Returns null if result is at infinity. + * @param d isPrivate(d) should be true + * @param compressed optional, if true compresses the resulting point + */ + export function pointFromScalar(d: Buffer, compressed?: boolean): Buffer | null; - /** - * Returns null if result is at infinity. - * @param A isPoint(A) should be true - * @param tweak should be within [1...order - 1] - * @param compressed optional, if true compresses the resulting point - */ - export function pointMultiply(A: Buffer, tweak: Buffer, compressed?: boolean): Buffer | null; + /** + * Returns null if result is at infinity. + * @param A isPoint(A) should be true + * @param tweak should be within [1...order - 1] + * @param compressed optional, if true compresses the resulting point + */ + export function pointMultiply(A: Buffer, tweak: Buffer, compressed?: boolean): Buffer | null; - /** - * Returns null if result is equal to 0. - * @param d isPrivate(d) should be true - * @param tweak should be within [1...order - 1] - */ - export function privateAdd(d: Buffer, tweak: Buffer): Buffer | null; + /** + * Returns null if result is equal to 0. + * @param d isPrivate(d) should be true + * @param tweak should be within [1...order - 1] + */ + export function privateAdd(d: Buffer, tweak: Buffer): Buffer | null; - /** - * Returns null if result is equal to 0. - * @param d isPrivate(d) should be true - * @param tweak should be within [1...order - 1] - */ - export function privateSub(d: Buffer, tweak: Buffer): Buffer | null; + /** + * Returns null if result is equal to 0. + * @param d isPrivate(d) should be true + * @param tweak should be within [1...order - 1] + */ + export function privateSub(d: Buffer, tweak: Buffer): Buffer | null; - /** - * Returns normalized signatures, each of (r, s) values are guaranteed to less than order / 2. Uses RFC6979. - * @param message should be 256-bit - * @param privateKey isPrivate(privateKey) should be true - */ - export function sign(message: Buffer, privateKey: Buffer): Buffer; + /** + * Returns normalized signatures, each of (r, s) values are guaranteed to less than order / 2. Uses RFC6979. + * @param message should be 256-bit + * @param privateKey isPrivate(privateKey) should be true + */ + export function sign(message: Buffer, privateKey: Buffer): Buffer; - /** - * Returns normalized signatures, each of (r, s) values are guaranteed to less than order / 2. Uses RFC6979. - * Adds e as Added Entropy to the deterministic k generation. - * @param message should be 256-bit - * @param privateKey isPrivate(privateKey) should be true - * @param entropy should be 256-bit - */ - export function signWithEntropy(message: Buffer, privateKey: Buffer, entropy: Buffer): Buffer; + /** + * Returns normalized signatures, each of (r, s) values are guaranteed to less than order / 2. Uses RFC6979. + * Adds e as Added Entropy to the deterministic k generation. + * @param message should be 256-bit + * @param privateKey isPrivate(privateKey) should be true + * @param entropy should be 256-bit + */ + export function signWithEntropy(message: Buffer, privateKey: Buffer, entropy: Buffer): Buffer; - /** - * Returns false if any of (r, s) values are equal to 0, or if the signature is rejected. - * @param message should be 256-bit - * @param publicKey isPoint(publicKey) should be true - * @param signature signature should have all (r, s) values within range [0...order - 1] - */ - export function verify(message: Buffer, publicKey: Buffer, signature: Buffer): boolean; + /** + * Returns false if any of (r, s) values are equal to 0, or if the signature is rejected. + * @param message should be 256-bit + * @param publicKey isPoint(publicKey) should be true + * @param signature signature should have all (r, s) values within range [0...order - 1] + */ + export function verify(message: Buffer, publicKey: Buffer, signature: Buffer): boolean; } diff --git a/packages/hdwallet-native/src/native.test.ts b/packages/hdwallet-native/src/native.test.ts index bdbb3ad22..8f9f81f4b 100644 --- a/packages/hdwallet-native/src/native.test.ts +++ b/packages/hdwallet-native/src/native.test.ts @@ -1,7 +1,6 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as native from "./native"; -import * as Isolation from "./crypto/isolation"; const MNEMONIC = "all all all all all all all all all all all all"; @@ -123,8 +122,7 @@ describe("NativeHDWallet", () => { in: [{ coin: "bitcoin", addressNList: [] }], out: [ { - xpub: - "xpub661MyMwAqRbcFLgDU7wpcEVubSF7NkswwmXBUkDiGUW6uopeUMys4AqKXNgpfZKRTLnpKQgffd6a2c3J8JxLkF1AQN17Pm9QYHEqEfo1Rsx", + xpub: "xpub661MyMwAqRbcFLgDU7wpcEVubSF7NkswwmXBUkDiGUW6uopeUMys4AqKXNgpfZKRTLnpKQgffd6a2c3J8JxLkF1AQN17Pm9QYHEqEfo1Rsx", }, ], }, @@ -132,8 +130,7 @@ describe("NativeHDWallet", () => { in: [{ coin: "bitcoin", addressNList: [1 + 0x80000000, 2 + 0x80000000] }], out: [ { - xpub: - "xpub6A4ydEAik39rFLs1hcm6XiwpFN5XKEf9tdAZWK23tkXmSr8bHmfYyfVt2nTskZQj3yYydcST2DLUFq2iJAELtTVfW9UNnnK8zBi8bzFcQVB", + xpub: "xpub6A4ydEAik39rFLs1hcm6XiwpFN5XKEf9tdAZWK23tkXmSr8bHmfYyfVt2nTskZQj3yYydcST2DLUFq2iJAELtTVfW9UNnnK8zBi8bzFcQVB", }, ], }, @@ -142,15 +139,14 @@ describe("NativeHDWallet", () => { in: [{ coin: "bitcoin", addressNList: [1 + 0x80000000, 2 + 0x80000000, 3] }], out: [ { - xpub: - "xpub6A4ydEAik39rFLs1hcm6XiwpFN5XKEf9tdAZWK23tkXmSr8bHmfYyfVt2nTskZQj3yYydcST2DLUFq2iJAELtTVfW9UNnnK8zBi8bzFcQVB", + xpub: "xpub6A4ydEAik39rFLs1hcm6XiwpFN5XKEf9tdAZWK23tkXmSr8bHmfYyfVt2nTskZQj3yYydcST2DLUFq2iJAELtTVfW9UNnnK8zBi8bzFcQVB", }, ], }, ]; for (const params of testCases) { expect(await wallet.getPublicKeys(params.in)).toStrictEqual(params.out); - }; + } }); it("should load wallet with a mnemonic and deviceId", async () => { @@ -174,23 +170,18 @@ describe("NativeHDWallet", () => { await expect(wallet.loadDevice(undefined as any)).rejects.toThrow("Either [mnemonic] or [masterKey] is required"); }); - it.each([[undefined]])( - "should throw an error if mnemonic is not a string (%o)", - async (param: any) => { - const wallet = native.create({ deviceId: "native" }); - await expect(wallet.loadDevice({ mnemonic: param })).rejects.toThrow( - "Either [mnemonic] or [masterKey] is required" - ); - } - ); + it.each([[undefined]])("should throw an error if mnemonic is not a string (%o)", async (param: any) => { + const wallet = native.create({ deviceId: "native" }); + await expect(wallet.loadDevice({ mnemonic: param })).rejects.toThrow( + "Either [mnemonic] or [masterKey] is required" + ); + }); it.each([[null], [0], [[1, 2, 3]], [{}], [""], ["all all all all all all"]])( "should throw an error if mnemonic is not a string (%o)", async (param: any) => { const wallet = native.create({ deviceId: "native" }); - await expect(wallet.loadDevice({ mnemonic: param })).rejects.toThrow( - "Required property [mnemonic] is invalid" - ); + await expect(wallet.loadDevice({ mnemonic: param })).rejects.toThrow("Required property [mnemonic] is invalid"); } ); }); diff --git a/packages/hdwallet-native/src/native.ts b/packages/hdwallet-native/src/native.ts index fc235f405..9f449b0c2 100644 --- a/packages/hdwallet-native/src/native.ts +++ b/packages/hdwallet-native/src/native.ts @@ -3,20 +3,19 @@ import * as bip39 from "bip39"; import * as eventemitter2 from "eventemitter2"; import _ from "lodash"; -import { MixinNativeBinanceWalletInfo, MixinNativeBinanceWallet } from "./binance"; -import { MixinNativeBTCWallet, MixinNativeBTCWalletInfo } from "./bitcoin"; -import { MixinNativeCosmosWalletInfo, MixinNativeCosmosWallet } from "./cosmos"; -import { MixinNativeOsmosisWallet, MixinNativeOsmosisWalletInfo } from "./osmosis"; -import { MixinNativeETHWalletInfo, MixinNativeETHWallet } from "./ethereum"; -import { MixinNativeFioWalletInfo, MixinNativeFioWallet } from "./fio"; -import { MixinNativeKavaWalletInfo, MixinNativeKavaWallet } from "./kava"; -import { getNetwork } from "./networks"; -import { MixinNativeSecretWalletInfo, MixinNativeSecretWallet } from "./secret"; -import { MixinNativeTerraWalletInfo, MixinNativeTerraWallet } from "./terra"; -import { MixinNativeThorchainWalletInfo, MixinNativeThorchainWallet } from "./thorchain"; - import type { NativeAdapterArgs } from "./adapter"; +import { MixinNativeBinanceWallet, MixinNativeBinanceWalletInfo } from "./binance"; +import { MixinNativeBTCWallet, MixinNativeBTCWalletInfo } from "./bitcoin"; +import { MixinNativeCosmosWallet, MixinNativeCosmosWalletInfo } from "./cosmos"; import * as Isolation from "./crypto/isolation"; +import { MixinNativeETHWallet, MixinNativeETHWalletInfo } from "./ethereum"; +import { MixinNativeFioWallet, MixinNativeFioWalletInfo } from "./fio"; +import { MixinNativeKavaWallet, MixinNativeKavaWalletInfo } from "./kava"; +import { getNetwork } from "./networks"; +import { MixinNativeOsmosisWallet, MixinNativeOsmosisWalletInfo } from "./osmosis"; +import { MixinNativeSecretWallet, MixinNativeSecretWalletInfo } from "./secret"; +import { MixinNativeTerraWallet, MixinNativeTerraWalletInfo } from "./terra"; +import { MixinNativeThorchainWallet, MixinNativeThorchainWalletInfo } from "./thorchain"; export enum NativeEvents { MNEMONIC_REQUIRED = "MNEMONIC_REQUIRED", @@ -30,13 +29,16 @@ function isMnemonicInterface(x: unknown): x is Isolation.Core.BIP39.Mnemonic { type LoadDevice = Omit & { // Set this if your deviceId is dependent on the mnemonic deviceId?: string; -} & ({ - mnemonic: string | Isolation.Core.BIP39.Mnemonic; - masterKey?: never -} | { - mnemonic?: never; - masterKey: Isolation.Core.BIP32.Node; -}) +} & ( + | { + mnemonic: string | Isolation.Core.BIP39.Mnemonic; + masterKey?: never; + } + | { + mnemonic?: never; + masterKey: Isolation.Core.BIP32.Node; + } + ); export class NativeHDWalletInfoBase implements core.HDWalletInfo { getVendor(): string { @@ -71,6 +73,7 @@ export class NativeHDWalletInfoBase implements core.HDWalletInfo { return false; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars describePath(msg: core.DescribePath): core.PathDescription { throw new Error("unreachable"); } @@ -135,7 +138,7 @@ class NativeHDWalletInfo case "digibyte": case "dogecoin": case "litecoin": - case "testnet": + case "testnet": { const unknown = core.unknownUTXOPath(msg.path, msg.coin, msg.scriptType); if (!msg.scriptType) return unknown; @@ -143,6 +146,7 @@ class NativeHDWalletInfo if (!super.btcSupportsScriptTypeSync(msg.coin, msg.scriptType)) return unknown; return core.describeUTXOPath(msg.path, msg.coin, msg.scriptType); + } case "ethereum": return core.describeETHPath(msg.path); case "atom": @@ -216,7 +220,7 @@ export class NativeHDWallet readonly _isNative = true; #deviceId: string; - #initialized: boolean = false; + #initialized = false; #masterKey: Promise | undefined = undefined; constructor({ mnemonic, deviceId, masterKey }: NativeAdapterArgs) { @@ -225,10 +229,11 @@ export class NativeHDWallet this.#masterKey = Promise.resolve(masterKey); } else if (mnemonic) { this.#masterKey = (async () => { - const isolatedMnemonic = typeof mnemonic === "string" ? await Isolation.Engines.Default.BIP39.Mnemonic.create(mnemonic) : mnemonic + const isolatedMnemonic = + typeof mnemonic === "string" ? await Isolation.Engines.Default.BIP39.Mnemonic.create(mnemonic) : mnemonic; const seed = await isolatedMnemonic.toSeed(); return await seed.toMasterKey(); - })() + })(); } this.#deviceId = deviceId; } @@ -259,10 +264,11 @@ export class NativeHDWallet */ async getPublicKeys(msg: Array): Promise { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const masterKey = await this.#masterKey!; return await Promise.all( msg.map(async (getPublicKey) => { - let { addressNList } = getPublicKey; + const { addressNList } = getPublicKey; const network = getNetwork(getPublicKey.coin, getPublicKey.scriptType); // TODO: return the xpub that's actually asked for, not the key of the hardened path // It's done this way for hilarious historical reasons and will break ETH if fixed @@ -272,7 +278,7 @@ export class NativeHDWallet const xpub = node.neutered().toBase58(); return { xpub }; }) - ) + ); }); } @@ -284,10 +290,12 @@ export class NativeHDWallet return false; } + // eslint-disable-next-line @typescript-eslint/no-empty-function async clearSession(): Promise {} async initialize(): Promise { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const masterKey = await this.#masterKey!; try { await Promise.all([ @@ -318,14 +326,19 @@ export class NativeHDWallet return { msg: msg.msg }; } + // eslint-disable-next-line @typescript-eslint/no-empty-function async sendPin(): Promise {} + // eslint-disable-next-line @typescript-eslint/no-empty-function async sendPassphrase(): Promise {} + // eslint-disable-next-line @typescript-eslint/no-empty-function async sendCharacter(): Promise {} + // eslint-disable-next-line @typescript-eslint/no-empty-function async sendWord(): Promise {} + // eslint-disable-next-line @typescript-eslint/no-empty-function async cancel(): Promise {} async wipe(): Promise { @@ -344,33 +357,37 @@ export class NativeHDWallet super.terraWipe(); super.kavaWipe(); - (await oldMasterKey)?.revoke?.() + (await oldMasterKey)?.revoke?.(); } + // eslint-disable-next-line @typescript-eslint/no-empty-function async reset(): Promise {} + // eslint-disable-next-line @typescript-eslint/no-empty-function async recover(): Promise {} async loadDevice(msg?: LoadDevice): Promise { - this.#masterKey = Promise.resolve(await (async (mnemonic, masterKey) => { - if (masterKey !== undefined) { - return masterKey; - } else if (mnemonic !== undefined) { - const isolatedMnemonic = await (async () => { - if (isMnemonicInterface(mnemonic)) return mnemonic; - if (typeof mnemonic === "string" && bip39.validateMnemonic(mnemonic)) { - return await Isolation.Engines.Default.BIP39.Mnemonic.create(mnemonic); - } - throw new Error("Required property [mnemonic] is invalid"); - })(); - const seed = await isolatedMnemonic.toSeed(); - seed.addRevoker?.(() => isolatedMnemonic.revoke?.()) - const masterKey = await seed.toMasterKey() - masterKey.addRevoker?.(() => seed.revoke?.()) - return masterKey - } - throw new Error("Either [mnemonic] or [masterKey] is required"); - })(msg?.mnemonic, msg?.masterKey)); + this.#masterKey = Promise.resolve( + await (async (mnemonic, masterKey) => { + if (masterKey !== undefined) { + return masterKey; + } else if (mnemonic !== undefined) { + const isolatedMnemonic = await (async () => { + if (isMnemonicInterface(mnemonic)) return mnemonic; + if (typeof mnemonic === "string" && bip39.validateMnemonic(mnemonic)) { + return await Isolation.Engines.Default.BIP39.Mnemonic.create(mnemonic); + } + throw new Error("Required property [mnemonic] is invalid"); + })(); + const seed = await isolatedMnemonic.toSeed(); + seed.addRevoker?.(() => isolatedMnemonic.revoke?.()); + const out = await seed.toMasterKey(); + out.addRevoker?.(() => seed.revoke?.()); + return out; + } + throw new Error("Either [mnemonic] or [masterKey] is required"); + })(msg?.mnemonic, msg?.masterKey) + ); if (typeof msg?.deviceId === "string") this.#deviceId = msg?.deviceId; diff --git a/packages/hdwallet-native/src/networks.ts b/packages/hdwallet-native/src/networks.ts index 1f0f56891..9e42dfb85 100644 --- a/packages/hdwallet-native/src/networks.ts +++ b/packages/hdwallet-native/src/networks.ts @@ -1,7 +1,7 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as bitcoin from "bitcoinjs-lib"; -import { BTCScriptType } from "./bitcoin" +import { BTCScriptType } from "./bitcoin"; type BIP32 = { bip32: { @@ -162,7 +162,18 @@ const networks: Networks = { }; //TODO: all below are missing network data -for (const coin of ["bitcoincash", "thorchain", "secret", "terra", "kava", "cardano", "cosmos", "osmosis", "binance", "ethereum"]) +for (const coin of [ + "bitcoincash", + "thorchain", + "secret", + "terra", + "kava", + "cardano", + "cosmos", + "osmosis", + "binance", + "ethereum", +]) networks[coin] = networks.bitcoin; export function getNetwork(coin: string, scriptType?: BTCScriptType): bitcoin.Network { @@ -170,7 +181,7 @@ export function getNetwork(coin: string, scriptType?: BTCScriptType): bitcoin.Ne scriptType = scriptType || core.BTCOutputScriptType.PayToMultisig; if (!(coin in networks)) throw new Error(`${coin} network not supported`); - let network = networks[coin]; + const network = networks[coin]; const bip32 = network[scriptType as core.BTCOutputScriptType]; if (!bip32) { diff --git a/packages/hdwallet-native/src/osmosis.test.ts b/packages/hdwallet-native/src/osmosis.test.ts index 29fb61682..6be05b652 100644 --- a/packages/hdwallet-native/src/osmosis.test.ts +++ b/packages/hdwallet-native/src/osmosis.test.ts @@ -38,42 +38,44 @@ describe("NativeOsmosisWallet", () => { }); it("should generate a correct osmosis address", async () => { - expect( - await wallet.osmosisGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0") }) - ).toBe("osmo1knuunh0lmwyrkjmrj7sky49uxk3peyzh2tlskm"); + expect(await wallet.osmosisGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0") })).toBe( + "osmo1knuunh0lmwyrkjmrj7sky49uxk3peyzh2tlskm" + ); }); it("should generate another correct osmosis address", async () => { - expect( - await wallet.osmosisGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/118'/1337'/123/4") }) - ).toBe("osmo14k4dnrrmxdch6nkvvuugsywrgmvlwrqs2f6kye"); + expect(await wallet.osmosisGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/118'/1337'/123/4") })).toBe( + "osmo14k4dnrrmxdch6nkvvuugsywrgmvlwrqs2f6kye" + ); }); it("should sign a transaction correctly", async () => { const signed = await wallet.osmosisSignTx({ addressNList: core.bip32ToAddressNList("m/44'/118'/0'/0/0"), tx: { - msg: [{ - "type": "cosmos-sdk/MsgSend", - "value": { - "from_address": "osmo1knuunh0lmwyrkjmrj7sky49uxk3peyzh2tlskm", - "to_address": "osmo1knuunh0lmwyrkjmrj7sky49uxk3peyzh2tlskm", - "amount": [ - { - "denom": "uosmo", - "amount": "1000" - } - ] - } - }], + msg: [ + { + type: "cosmos-sdk/MsgSend", + value: { + from_address: "osmo1knuunh0lmwyrkjmrj7sky49uxk3peyzh2tlskm", + to_address: "osmo1knuunh0lmwyrkjmrj7sky49uxk3peyzh2tlskm", + amount: [ + { + denom: "uosmo", + amount: "1000", + }, + ], + }, + }, + ], fee: { - "amount": [ + amount: [ { - "amount": "100", - "denom": "uosmo" - } + amount: "100", + denom: "uosmo", + }, ], - "gas": "100000" + gas: "100000", }, signatures: null, memo: "foobar", diff --git a/packages/hdwallet-native/src/osmosis.ts b/packages/hdwallet-native/src/osmosis.ts index 3b6dea137..26782a36c 100644 --- a/packages/hdwallet-native/src/osmosis.ts +++ b/packages/hdwallet-native/src/osmosis.ts @@ -1,16 +1,16 @@ import * as core from "@shapeshiftoss/hdwallet-core"; +import * as protoTxBuilder from "@shapeshiftoss/proto-tx-builder"; import * as bech32 from "bech32"; import CryptoJS from "crypto-js"; -import * as protoTxBuilder from "@shapeshiftoss/proto-tx-builder"; +import * as Isolation from "./crypto/isolation"; import { NativeHDWalletBase } from "./native"; import * as util from "./util"; -import * as Isolation from "./crypto/isolation"; -import {CosmosSignedTx} from "@shapeshiftoss/hdwallet-core"; const OSMOSIS_CHAIN = "osmosis-1"; export function MixinNativeOsmosisWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeOsmosisWalletInfo extends Base implements core.OsmosisWalletInfo { readonly _supportsOsmosisInfo = true; async osmosisSupportsNetwork(): Promise { @@ -26,7 +26,7 @@ export function MixinNativeOsmosisWalletInfo { - const slip44 = core.slip44ByCoin("Osmo") + const slip44 = core.slip44ByCoin("Osmo"); return [ { addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0], @@ -34,6 +34,7 @@ export function MixinNativeOsmosisWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeOsmosisWallet extends Base { readonly _supportsOsmosis = true; @@ -69,6 +71,7 @@ export function MixinNativeOsmosisWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "osmosis"); return this.createOsmosisAddress(keyPair.publicKey.toString("hex")); }); @@ -76,6 +79,7 @@ export function MixinNativeOsmosisWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "osmosis"); const adapter = await Isolation.Adapters.CosmosDirect.create(keyPair.node, "osmo"); return await protoTxBuilder.sign(msg.tx, adapter, msg.sequence, msg.account_number, OSMOSIS_CHAIN); diff --git a/packages/hdwallet-native/src/secret.test.ts b/packages/hdwallet-native/src/secret.test.ts index 8181c6926..939cc91bd 100644 --- a/packages/hdwallet-native/src/secret.test.ts +++ b/packages/hdwallet-native/src/secret.test.ts @@ -64,11 +64,9 @@ describe("NativeSecretWallet", () => { chain_id: "foobar", account_number: 123, sequence: 456, - }) + }); expect(signed.signatures.length).toBe(1); - expect(signed.signatures[0].pub_key.value).toMatchInlineSnapshot( - `"A2UVKphVsesrnAQEtX4K+qk8Z84wa5xD5mxzdPykAiyR"` - ); + expect(signed.signatures[0].pub_key.value).toMatchInlineSnapshot(`"A2UVKphVsesrnAQEtX4K+qk8Z84wa5xD5mxzdPykAiyR"`); expect(signed.signatures[0].signature).toMatchInlineSnapshot( `"f4HKv09XvsGQn74y4MHL+M+wP/uBjHsIn5PwPfq7xMI7CJkS22Pxx7KlXpeUzCjiaSZvEEIuxbkd9J+Q4g86jg=="` ); diff --git a/packages/hdwallet-native/src/secret.ts b/packages/hdwallet-native/src/secret.ts index 8362f4912..683ee3f3b 100644 --- a/packages/hdwallet-native/src/secret.ts +++ b/packages/hdwallet-native/src/secret.ts @@ -3,11 +3,12 @@ import * as bech32 from "bech32"; import CryptoJS from "crypto-js"; import * as txBuilder from "tendermint-tx-builder"; +import * as Isolation from "./crypto/isolation"; import { NativeHDWalletBase } from "./native"; import * as util from "./util"; -import * as Isolation from "./crypto/isolation"; export function MixinNativeSecretWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeSecretWalletInfo extends Base implements core.SecretWalletInfo { readonly _supportsSecretInfo = true; async secretSupportsNetwork(): Promise { @@ -23,7 +24,7 @@ export function MixinNativeSecretWalletInfo { - const slip44 = core.slip44ByCoin("Secret") + const slip44 = core.slip44ByCoin("Secret"); return [ { addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0], @@ -31,6 +32,7 @@ export function MixinNativeSecretWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeSecretWallet extends Base { readonly _supportsSecret = true; @@ -66,6 +69,7 @@ export function MixinNativeSecretWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "secret"); return this.createSecretAddress(keyPair.publicKey.toString("hex")); }); @@ -73,10 +77,17 @@ export function MixinNativeSecretWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "secret"); - // @ts-ignore + // @TODO: This needs to be fixed after the change to tendermint serialization const adapter = await Isolation.Adapters.Cosmos.create(keyPair); - const result = await txBuilder.sign(msg.tx, adapter, String(msg.sequence), String(msg.account_number), msg.chain_id); + const result = await txBuilder.sign( + msg.tx, + adapter, + String(msg.sequence), + String(msg.account_number), + msg.chain_id + ); return txBuilder.createSignedTx(msg.tx, result); }); } diff --git a/packages/hdwallet-native/src/terra.test.ts b/packages/hdwallet-native/src/terra.test.ts index 65fb0f665..41d9d7676 100644 --- a/packages/hdwallet-native/src/terra.test.ts +++ b/packages/hdwallet-native/src/terra.test.ts @@ -38,9 +38,9 @@ describe("NativeTerraWallet", () => { }); it("should generate a correct terra address", async () => { - await expect( - wallet.terraGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/330'/0'/0/0") }) - ).resolves.toBe("terra1f95csal3u6cyyj23ept3x7ap3u247npf8u2yhz"); + await expect(wallet.terraGetAddress({ addressNList: core.bip32ToAddressNList("m/44'/330'/0'/0/0") })).resolves.toBe( + "terra1f95csal3u6cyyj23ept3x7ap3u247npf8u2yhz" + ); }); it("should generate another correct terra address", async () => { @@ -64,11 +64,9 @@ describe("NativeTerraWallet", () => { chain_id: "foobar", account_number: "foo", sequence: "bar", - }) + }); expect(signed.signatures.length).toBe(1); - expect(signed.signatures[0].pub_key.value).toMatchInlineSnapshot( - `"A6cAUgKWL3/P3nQY+j2fMUBaAW/QC/FGmQzTJ4nqXo0E"` - ); + expect(signed.signatures[0].pub_key.value).toMatchInlineSnapshot(`"A6cAUgKWL3/P3nQY+j2fMUBaAW/QC/FGmQzTJ4nqXo0E"`); expect(signed.signatures[0].signature).toMatchInlineSnapshot( `"zUPR10sr2QwRa10fcb3z/KC6/mWuLq0iff5ImhylIJpqU1RSg49Jxbmvp07D3sWuY0fE5mcSdMWQXWJFw2zsWQ=="` ); diff --git a/packages/hdwallet-native/src/terra.ts b/packages/hdwallet-native/src/terra.ts index 5b3a5c2bb..ba8c7459b 100644 --- a/packages/hdwallet-native/src/terra.ts +++ b/packages/hdwallet-native/src/terra.ts @@ -3,11 +3,12 @@ import * as bech32 from "bech32"; import CryptoJS from "crypto-js"; import * as txBuilder from "tendermint-tx-builder"; +import * as Isolation from "./crypto/isolation"; import { NativeHDWalletBase } from "./native"; import * as util from "./util"; -import * as Isolation from "./crypto/isolation"; export function MixinNativeTerraWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeTerraWalletInfo extends Base implements core.TerraWalletInfo { readonly _supportsTerraInfo = true; @@ -24,7 +25,7 @@ export function MixinNativeTerraWalletInfo { - const slip44 = core.slip44ByCoin("Terra") + const slip44 = core.slip44ByCoin("Terra"); return [ { addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0], @@ -32,6 +33,7 @@ export function MixinNativeTerraWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeTerraWallet extends Base { readonly _supportsTerra = true; @@ -67,6 +70,7 @@ export function MixinNativeTerraWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "terra"); return this.createTerraAddress(keyPair.publicKey.toString("hex")); }); @@ -74,9 +78,9 @@ export function MixinNativeTerraWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "terra"); // @TODO: This needs to be fixed after the change to tendermint serialization - // @ts-ignore const adapter = await Isolation.Adapters.Cosmos.create(keyPair); const result = await txBuilder.sign(msg.tx, adapter, msg.sequence, msg.account_number, "terra"); return txBuilder.createSignedTx(msg.tx, result); diff --git a/packages/hdwallet-native/src/thorchain.ts b/packages/hdwallet-native/src/thorchain.ts index 63f8aec1c..9e73813f2 100644 --- a/packages/hdwallet-native/src/thorchain.ts +++ b/packages/hdwallet-native/src/thorchain.ts @@ -3,13 +3,14 @@ import * as bech32 from "bech32"; import CryptoJS from "crypto-js"; import * as txBuilder from "tendermint-tx-builder"; +import * as Isolation from "./crypto/isolation"; import { NativeHDWalletBase } from "./native"; import * as util from "./util"; -import * as Isolation from "./crypto/isolation"; const THOR_CHAIN = "thorchain"; export function MixinNativeThorchainWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeThorchainWalletInfo extends Base implements core.ThorchainWalletInfo { _supportsThorchainInfo = true; async thorchainSupportsNetwork(): Promise { @@ -25,7 +26,7 @@ export function MixinNativeThorchainWalletInfo { - const slip44 = core.slip44ByCoin("Thorchain") + const slip44 = core.slip44ByCoin("Thorchain"); return [ { addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0], @@ -33,6 +34,7 @@ export function MixinNativeThorchainWalletInfo>(Base: TBase) { + // eslint-disable-next-line @typescript-eslint/no-shadow return class MixinNativeThorchainWallet extends Base { _supportsThorchain = true; @@ -68,6 +71,7 @@ export function MixinNativeThorchainWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "thorchain"); return this.createThorchainAddress(keyPair.publicKey.toString("hex")); }); @@ -75,6 +79,7 @@ export function MixinNativeThorchainWallet { return this.needsMnemonic(!!this.#masterKey, async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const keyPair = await util.getKeyPair(this.#masterKey!, msg.addressNList, "thorchain"); const adapter = await Isolation.Adapters.Cosmos.create(keyPair.node); const result = await txBuilder.sign(msg.tx, adapter, msg.sequence, msg.account_number, THOR_CHAIN); diff --git a/packages/hdwallet-native/src/util.test.ts b/packages/hdwallet-native/src/util.test.ts index 9ea89c60c..ebc75b414 100644 --- a/packages/hdwallet-native/src/util.test.ts +++ b/packages/hdwallet-native/src/util.test.ts @@ -1,17 +1,21 @@ -import * as util from "./util"; import * as Isolation from "./crypto/isolation"; +import * as util from "./util"; describe("getKeyPair", () => { let masterKey: Isolation.Core.BIP32.Node; beforeAll(async () => { - const mnemonic = await Isolation.Engines.Default.BIP39.Mnemonic.create("all all all all all all all all all all all all"); + const mnemonic = await Isolation.Engines.Default.BIP39.Mnemonic.create( + "all all all all all all all all all all all all" + ); const seed = await mnemonic.toSeed(); masterKey = await seed.toMasterKey(); }); it("should produce the key pair at m/1337/0", async () => { const keyPair = await util.getKeyPair(masterKey, [1337, 0], "bitcoin"); - expect(keyPair.publicKey.toString("hex")).toEqual("029bf8b52f7efe773323bf1746b8489c4e823adb73644476cc099df57b1be0cb94"); + expect(keyPair.publicKey.toString("hex")).toEqual( + "029bf8b52f7efe773323bf1746b8489c4e823adb73644476cc099df57b1be0cb94" + ); }); }); diff --git a/packages/hdwallet-native/src/util.ts b/packages/hdwallet-native/src/util.ts index dd1be7d94..a6a0c4e46 100644 --- a/packages/hdwallet-native/src/util.ts +++ b/packages/hdwallet-native/src/util.ts @@ -1,18 +1,14 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import { BTCScriptType } from "./bitcoin"; -import { getNetwork } from "./networks"; import * as Isolation from "./crypto/isolation"; - -function isSeed(x: any): x is Isolation.Core.BIP32.Seed { - return "toMasterKey" in x && typeof x.toMasterKey === "function" -} +import { getNetwork } from "./networks"; export async function getKeyPair( node: Isolation.Core.BIP32.Node, addressNList: number[], coin: core.Coin, - scriptType?: BTCScriptType, + scriptType?: BTCScriptType ): Promise { const network = getNetwork(coin, scriptType); const wallet = await Isolation.Adapters.BIP32.create(node, network); diff --git a/packages/hdwallet-portis/src/bitcoin.ts b/packages/hdwallet-portis/src/bitcoin.ts index ebed312c5..94128be28 100644 --- a/packages/hdwallet-portis/src/bitcoin.ts +++ b/packages/hdwallet-portis/src/bitcoin.ts @@ -4,9 +4,13 @@ import * as bip32 from "bip32"; import * as bitcoin from "bitcoinjs-lib"; import * as bitcoinMsg from "bitcoinjs-message"; -export function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: core.BTCInputScriptType): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { +export function describeUTXOPath( + path: core.BIP32Path, + coin: core.Coin, + scriptType?: core.BTCInputScriptType +): core.PathDescription { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin, scriptType, @@ -17,7 +21,7 @@ export function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptTy if ((path[0] & 0x80000000) >>> 0 !== 0x80000000) return unknown; - let purpose = path[0] & 0x7fffffff; + const purpose = path[0] & 0x7fffffff; if (![44, 49, 84].includes(purpose)) return unknown; @@ -27,13 +31,17 @@ export function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptTy if (purpose === 84 && scriptType !== core.BTCInputScriptType.SpendWitness) return unknown; - let wholeAccount = path.length === 3; + const wholeAccount = path.length === 3; - let script = scriptType ? ({ - [core.BTCInputScriptType.SpendAddress]: ["Legacy"], - [core.BTCInputScriptType.SpendP2SHWitness]: [], - [core.BTCInputScriptType.SpendWitness]: ["Segwit Native"], - } as Partial>)[scriptType] ?? [] as string[] : [] as string[]; + const script = scriptType + ? ( + { + [core.BTCInputScriptType.SpendAddress]: ["Legacy"], + [core.BTCInputScriptType.SpendP2SHWitness]: [], + [core.BTCInputScriptType.SpendWitness]: ["Segwit Native"], + } as Partial> + )[scriptType] ?? ([] as string[]) + : ([] as string[]); let isPrefork = false; const slip44 = core.slip44ByCoin(coin); @@ -49,7 +57,10 @@ export function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptTy return unknown; } case "BitcoinSV": { - if (path[1] === 0x80000000 + core.slip44ByCoin("Bitcoin") || path[1] === 0x80000000 + core.slip44ByCoin("BitcoinCash")) { + if ( + path[1] === 0x80000000 + core.slip44ByCoin("Bitcoin") || + path[1] === 0x80000000 + core.slip44ByCoin("BitcoinCash") + ) { isPrefork = true; break; } @@ -73,9 +84,9 @@ export function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptTy break; } - let attr = attributes.length ? ` (${attributes.join(", ")})` : ""; + const attr = attributes.length ? ` (${attributes.join(", ")})` : ""; - let accountIdx = path[2] & 0x7fffffff; + const accountIdx = path[2] & 0x7fffffff; if (wholeAccount) { return { @@ -88,8 +99,8 @@ export function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptTy isPrefork, }; } else { - let change = path[3] === 1 ? "Change " : ""; - let addressIdx = path[4]; + const change = path[3] === 1 ? "Change " : ""; + const addressIdx = path[4]; return { coin, verbose: `${coin} Account #${accountIdx}, ${change}Address #${addressIdx}${attr}`, @@ -104,6 +115,14 @@ export function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptTy } } +export function verifyScriptTypePurpose(scriptType: core.BTCInputScriptType, purpose: number): boolean { + return ( + (purpose === 0x80000000 + 44 && scriptType === core.BTCInputScriptType.SpendAddress) || + (purpose === 0x80000000 + 49 && scriptType === core.BTCInputScriptType.SpendP2SHWitness) || + (purpose === 0x80000000 + 84 && scriptType === core.BTCInputScriptType.SpendWitness) + ); +} + export async function btcGetAddress(msg: core.BTCGetAddress, portis: any): Promise { if (!msg.addressNList.length) throw new Error("Empty addressNList"); @@ -148,14 +167,6 @@ export async function btcGetAddress(msg: core.BTCGetAddress, portis: any): Promi return core.mustBeDefined(result.address); } -export function verifyScriptTypePurpose(scriptType: core.BTCInputScriptType, purpose: number): boolean { - return ( - (purpose === 0x80000000 + 44 && scriptType === core.BTCInputScriptType.SpendAddress) || - (purpose === 0x80000000 + 49 && scriptType === core.BTCInputScriptType.SpendP2SHWitness) || - (purpose === 0x80000000 + 84 && scriptType === core.BTCInputScriptType.SpendWitness) - ); -} - export function legacyAccount(coin: core.Coin, slip44: number, accountIdx: number): core.BTCAccountPath { return { coin, @@ -180,6 +191,7 @@ export function segwitNativeAccount(coin: core.Coin, slip44: number, accountIdx: }; } +// eslint-disable-next-line @typescript-eslint/no-unused-vars export function btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { return undefined; } @@ -192,9 +204,11 @@ export function btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array = - ({ - Bitcoin: [bip44, bip49, bip84], - } as Partial>)[msg.coin] || []; + ( + { + Bitcoin: [bip44, bip49, bip84], + } as Partial> + )[msg.coin] || []; if (msg.scriptType !== undefined) paths = paths.filter((path) => { diff --git a/packages/hdwallet-portis/src/ethereum.ts b/packages/hdwallet-portis/src/ethereum.ts index 75861aaf7..09b77a9bf 100644 --- a/packages/hdwallet-portis/src/ethereum.ts +++ b/packages/hdwallet-portis/src/ethereum.ts @@ -1,8 +1,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; export function describeETHPath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Ethereum", isKnown: false, @@ -20,7 +20,7 @@ export function describeETHPath(path: core.BIP32Path): core.PathDescription { if (path[4] !== 0) return unknown; - let index = path[2] & 0x7fffffff; + const index = path[2] & 0x7fffffff; return { verbose: `Ethereum Account #${index}`, accountIdx: index, @@ -66,7 +66,11 @@ export async function ethSignTx(msg: core.ETHSignTx, web3: any, from: string): P }; } -export async function ethSignMessage(msg: core.ETHSignMessage, web3: any, address: string): Promise { +export async function ethSignMessage( + msg: core.ETHSignMessage, + web3: any, + address: string +): Promise { const result = await web3.eth.sign(msg.message, address); return { address, diff --git a/packages/hdwallet-portis/src/portis.ts b/packages/hdwallet-portis/src/portis.ts index 0720c0a2d..8d3568f95 100644 --- a/packages/hdwallet-portis/src/portis.ts +++ b/packages/hdwallet-portis/src/portis.ts @@ -12,6 +12,7 @@ class PortisTransport extends core.Transport { return "portis:0"; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public call(...args: any[]): Promise { return Promise.resolve(); } @@ -24,6 +25,111 @@ export function isPortis(wallet: core.HDWallet): wallet is PortisHDWallet { type HasNonTrivialConstructor = T extends { new (): any } ? never : T; export type Portis = InstanceType>; +export class PortisHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo, core.BTCWalletInfo { + readonly _supportsBTCInfo = true; + readonly _supportsETHInfo = true; + + public getVendor(): string { + return "Portis"; + } + + public hasOnDevicePinEntry(): boolean { + return true; + } + + public hasOnDevicePassphrase(): boolean { + return true; + } + + public hasOnDeviceDisplay(): boolean { + return true; + } + + public hasOnDeviceRecovery(): boolean { + return true; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { + return false; + } + + public supportsOfflineSigning(): boolean { + return true; + } + + public supportsBroadcast(): boolean { + return false; + } + + public describePath(msg: core.DescribePath): core.PathDescription { + switch (msg.coin) { + case "Ethereum": + return eth.describeETHPath(msg.path); + case "Bitcoin": + return btc.describeUTXOPath(msg.path, msg.coin, msg.scriptType); + default: + throw new Error("Unsupported path"); + } + } + + public async btcSupportsCoin(coin: core.Coin): Promise { + return btc.btcSupportsCoin(coin); + } + + public async btcSupportsScriptType(coin: core.Coin, scriptType: core.BTCInputScriptType): Promise { + return btc.btcSupportsScriptType(coin, scriptType); + } + + public async btcSupportsSecureTransfer(): Promise { + return Promise.resolve(false); + } + + public btcSupportsNativeShapeShift(): boolean { + return false; + } + + public btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array { + return btc.btcGetAccountPaths(msg); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public btcIsSameAccount(msg: Array): boolean { + // TODO: Probably not correct + return false; + } + + public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { + return btc.btcNextAccountPath(msg); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { + // Portis only supports one account for eth + return undefined; + } + + public async ethSupportsNetwork(chainId = 1): Promise { + return chainId === 1; + } + + public async ethSupportsSecureTransfer(): Promise { + return false; + } + + public ethSupportsNativeShapeShift(): boolean { + return false; + } + + public async ethSupportsEIP1559(): Promise { + return false; + } + + public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { + return eth.ethGetAccountPaths(msg); + } +} + export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWallet { readonly _supportsETH = true; readonly _supportsETHInfo = true; @@ -45,7 +151,7 @@ export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWa this.portis = portis; this.web3 = (async () => { const web3 = (await import("web3")).default; - return new web3(portis.provider) + return new web3(portis.provider); })(); this.info = new PortisHDWalletInfo(); } @@ -113,21 +219,25 @@ export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWa return Promise.resolve({ msg: msg.msg }); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public sendPin(pin: string): Promise { // no concept of pin in Portis return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public sendPassphrase(passphrase: string): Promise { // cannot send passphrase to Portis. Could show the widget? return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public sendCharacter(charater: string): Promise { // no concept of sendCharacter in Portis return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public sendWord(word: string): Promise { // no concept of sendWord in Portis return Promise.resolve(); @@ -142,10 +252,12 @@ export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWa return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public reset(msg: core.ResetDevice): Promise { return Promise.resolve(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public recover(msg: core.RecoverDevice): Promise { // no concept of recover in Portis return Promise.resolve(); @@ -161,14 +273,14 @@ export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWa public async getPublicKeys(msg: Array): Promise> { const publicKeys: { xpub: string }[] = []; - this.portisCallInProgress = new Promise(async (resolve, reject) => { + this.portisCallInProgress = (async () => { try { await this.portisCallInProgress; } catch (e) { console.error(e); } for (let i = 0; i < msg.length; i++) { - const { addressNList, coin } = msg[i]; + const { addressNList } = msg[i]; const bitcoinSlip44 = 0x80000000 + core.slip44ByCoin("Bitcoin"); // TODO we really shouldnt be every using the "bitcoin" string parameter but is here for now to make it work with their btc address on their portis wallet. const portisResult: { error: string; result: string } = await this.portis.getExtendedPublicKey( @@ -176,11 +288,11 @@ export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWa addressNList[1] === bitcoinSlip44 ? "Bitcoin" : "" ); const { result, error } = portisResult; - if (error) reject(error); + if (error) throw error; publicKeys.push({ xpub: result }); } - resolve(publicKeys); - }); + return publicKeys; + })(); return this.portisCallInProgress; } @@ -200,6 +312,7 @@ export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWa return btc.btcSignTx(msg, this.portis); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async btcSignMessage(msg: core.BTCSignMessage): Promise { // portis doesnt support this for btc throw new Error("not supported"); @@ -237,7 +350,7 @@ export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWa return this.info.btcNextAccountPath(msg); } - public async ethSupportsNetwork(chainId: number = 1): Promise { + public async ethSupportsNetwork(chainId = 1): Promise { return this.info.ethSupportsNetwork(chainId); } @@ -298,108 +411,6 @@ export class PortisHDWallet implements core.HDWallet, core.ETHWallet, core.BTCWa } } -export class PortisHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo, core.BTCWalletInfo { - readonly _supportsBTCInfo = true; - readonly _supportsETHInfo = true; - - public getVendor(): string { - return "Portis"; - } - - public hasOnDevicePinEntry(): boolean { - return true; - } - - public hasOnDevicePassphrase(): boolean { - return true; - } - - public hasOnDeviceDisplay(): boolean { - return true; - } - - public hasOnDeviceRecovery(): boolean { - return true; - } - - public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { - // It doesn't... yet? - return false; - } - - public supportsOfflineSigning(): boolean { - return true; - } - - public supportsBroadcast(): boolean { - return false; - } - - public describePath(msg: core.DescribePath): core.PathDescription { - switch (msg.coin) { - case "Ethereum": - return eth.describeETHPath(msg.path); - case "Bitcoin": - return btc.describeUTXOPath(msg.path, msg.coin, msg.scriptType); - default: - throw new Error("Unsupported path"); - } - } - - public async btcSupportsCoin(coin: core.Coin): Promise { - return btc.btcSupportsCoin(coin); - } - - public async btcSupportsScriptType(coin: core.Coin, scriptType: core.BTCInputScriptType): Promise { - return btc.btcSupportsScriptType(coin, scriptType); - } - - public async btcSupportsSecureTransfer(): Promise { - return Promise.resolve(false); - } - - public btcSupportsNativeShapeShift(): boolean { - return false; - } - - public btcGetAccountPaths(msg: core.BTCGetAccountPaths): Array { - return btc.btcGetAccountPaths(msg); - } - - public btcIsSameAccount(msg: Array): boolean { - return false; - } - - public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - return btc.btcNextAccountPath(msg); - } - - public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - // Portis only supports one account for eth - return undefined; - } - - public async ethSupportsNetwork(chainId: number = 1): Promise { - return chainId === 1; - } - - public async ethSupportsSecureTransfer(): Promise { - return false; - } - - public ethSupportsNativeShapeShift(): boolean { - return false; - } - - public async ethSupportsEIP1559(): Promise { - return false; - } - - public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { - return eth.ethGetAccountPaths(msg); - } -} - export function info() { return new PortisHDWalletInfo(); } diff --git a/packages/hdwallet-trezor-connect/src/adapter.ts b/packages/hdwallet-trezor-connect/src/adapter.ts index 2ebd1c24f..7adfe8da4 100644 --- a/packages/hdwallet-trezor-connect/src/adapter.ts +++ b/packages/hdwallet-trezor-connect/src/adapter.ts @@ -2,7 +2,7 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import * as trezor from "@shapeshiftoss/hdwallet-trezor"; import TrezorConnect, { DEVICE, DEVICE_EVENT, TRANSPORT_EVENT, UI } from "trezor-connect"; -import { TrezorConnectTransport, POPUP, TrezorDevice } from "./transport"; +import { POPUP, TrezorConnectTransport, TrezorDevice } from "./transport"; export type DeviceID = string; @@ -32,8 +32,8 @@ export class TrezorAdapter { // until after init has resolved. This awkward sequence is needed because we // need TrezorConnect to be fully operational before we're able to process // the events. - let connectEvents: any[] = []; - let connectHandler = (event: any) => { + const connectEvents: any[] = []; + const connectHandler = (event: any) => { if (event.type === DEVICE.CONNECT) { connectEvents.push(event); } @@ -65,7 +65,7 @@ export class TrezorAdapter { TrezorConnect.on(TRANSPORT_EVENT, (event: any) => { // Log TrezorConnect's event raw: try { - let device_id = event.payload && event.payload.features ? event.payload.features.device_id : ""; + const device_id = event.payload && event.payload.features ? event.payload.features.device_id : ""; this.keyring.emit(["Trezor", device_id, event.type], event); } catch (e) { console.error("Could not emit Trezor transport event", event, e); @@ -73,7 +73,7 @@ export class TrezorAdapter { }); TrezorConnect.on(UI.ADDRESS_VALIDATION, (event: any) => { - console.log("Confirm on Trezor", event); + console.info("Confirm on Trezor", event); }); return true; @@ -133,7 +133,7 @@ export class TrezorAdapter { payload: { features }, } = event; if (!features) return; - let wallet = this.keyring.get(features.device_id) as trezor.TrezorHDWallet; + const wallet = this.keyring.get(features.device_id) as trezor.TrezorHDWallet; if (!wallet) return; wallet.cacheFeatures(features); @@ -176,7 +176,7 @@ export class TrezorAdapter { throw new Error(`Could not pair Trezor: '${payload.error}'`); } - let deviceID = payload.device_id; + const deviceID = payload.device_id; await this.initialize([ { @@ -185,7 +185,7 @@ export class TrezorAdapter { }, ]); - let wallet = this.keyring.get(deviceID) as trezor.TrezorHDWallet; + const wallet = this.keyring.get(deviceID) as trezor.TrezorHDWallet; if (wallet) wallet.cacheFeatures(payload); diff --git a/packages/hdwallet-trezor-connect/src/modules.d.ts b/packages/hdwallet-trezor-connect/src/modules.d.ts index b45db2dc8..5850fce1c 100644 --- a/packages/hdwallet-trezor-connect/src/modules.d.ts +++ b/packages/hdwallet-trezor-connect/src/modules.d.ts @@ -1 +1 @@ -declare module 'trezor-connect'; +declare module "trezor-connect"; diff --git a/packages/hdwallet-trezor-connect/src/transport.ts b/packages/hdwallet-trezor-connect/src/transport.ts index a9e54ff35..564913d22 100644 --- a/packages/hdwallet-trezor-connect/src/transport.ts +++ b/packages/hdwallet-trezor-connect/src/transport.ts @@ -88,7 +88,7 @@ export class TrezorConnectTransport extends trezor.TrezorTransport { this.emit("NEEDS_BACKUP"); } } else if (event.type === "ui-button") { - let kind = event.payload.code; + const kind = event.payload.code; this.emit( core.Events.BUTTON_REQUEST, core.makeEvent({ @@ -109,8 +109,7 @@ export class TrezorConnectTransport extends trezor.TrezorTransport { public static async callQuiet( device: TrezorDevice | undefined, method: string, - msg: any, - msTimeout?: number + msg: any ): Promise { // TrezorConnect only lets us make one call at a time. If this library is // used in a concurrent environment like say, React, then we need to guard @@ -124,7 +123,7 @@ export class TrezorConnectTransport extends trezor.TrezorTransport { await TrezorConnectTransport.cancellable(TrezorConnectTransport.callInProgress); try { - let result = await (TrezorConnect as any)[method]({ device, ...msg }); + const result = await (TrezorConnect as any)[method]({ device, ...msg }); if ( result.payload.error === "Popup closed" || result.payload.error === "Cancelled" || @@ -147,7 +146,7 @@ export class TrezorConnectTransport extends trezor.TrezorTransport { return TrezorConnectTransport.callInProgress; } - public async call(method: string, msg: any, msTimeout?: number): Promise { + public async call(method: string, msg: any): Promise { this.emit( method, core.makeEvent({ @@ -157,7 +156,7 @@ export class TrezorConnectTransport extends trezor.TrezorTransport { }) ); - let response = await TrezorConnectTransport.callQuiet(this.device, method, msg, msTimeout); + const response = await TrezorConnectTransport.callQuiet(this.device, method, msg); this.emit( method, diff --git a/packages/hdwallet-trezor/src/bitcoin.ts b/packages/hdwallet-trezor/src/bitcoin.ts index 156d1cbd5..eadac1aad 100644 --- a/packages/hdwallet-trezor/src/bitcoin.ts +++ b/packages/hdwallet-trezor/src/bitcoin.ts @@ -1,8 +1,8 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import Base64 from "base64-js"; -import { handleError } from "./utils"; import { TrezorTransport } from "./transport"; +import { handleError } from "./utils"; type BTCTrezorSignTxOutput = { amount?: string; @@ -13,17 +13,19 @@ type BTCTrezorSignTxOutput = { }; function translateCoin(coin: core.Coin): string { - return core.mustBeDefined({ - Bitcoin: "btc", - Litecoin: "ltc", - Zcash: "zec", - BitcoinCash: "bch", - BitcoinGold: "btg", - Dash: "dash", - DigiByte: "dgb", - Testnet: "testnet", - Dogecoin: "doge", - }[coin]); + return core.mustBeDefined( + { + Bitcoin: "btc", + Litecoin: "ltc", + Zcash: "zec", + BitcoinCash: "bch", + BitcoinGold: "btg", + Dash: "dash", + DigiByte: "dgb", + Testnet: "testnet", + Dogecoin: "doge", + }[coin] + ); } const segwitCoins = ["Bitcoin", "Litecoin", "BitcoinGold", "Testnet"]; @@ -72,20 +74,26 @@ export async function btcGetAddress(transport: TrezorTransport, msg: core.BTCGet path: core.addressNListToBIP32(msg.addressNList), showOnTrezor: !!msg.showDisplay, coin: translateCoin(msg.coin), - address: msg.showDisplay ? await btcGetAddress(transport, { - ...msg, - showDisplay: false, - }) : undefined + address: msg.showDisplay + ? await btcGetAddress(transport, { + ...msg, + showDisplay: false, + }) + : undefined, }); handleError(transport, res, "Could not get address from Trezor"); - return res.payload.address + return res.payload.address; } -export async function btcSignTx(wallet: core.BTCWallet, transport: TrezorTransport, msg: core.BTCSignTxTrezor): Promise { - let supportsShapeShift = wallet.btcSupportsNativeShapeShift(); - let supportsSecureTransfer = await wallet.btcSupportsSecureTransfer(); +export async function btcSignTx( + wallet: core.BTCWallet, + transport: TrezorTransport, + msg: core.BTCSignTxTrezor +): Promise { + const supportsShapeShift = wallet.btcSupportsNativeShapeShift(); + const supportsSecureTransfer = await wallet.btcSupportsSecureTransfer(); - let inputs = msg.inputs.map((input) => { + const inputs = msg.inputs.map((input) => { return { address_n: input.addressNList, prev_hash: input.txid, @@ -95,7 +103,7 @@ export async function btcSignTx(wallet: core.BTCWallet, transport: TrezorTranspo }; }); - let outputs: BTCTrezorSignTxOutput[] = msg.outputs.map((output) => { + const outputs: BTCTrezorSignTxOutput[] = msg.outputs.map((output) => { if (output.exchangeType && !supportsShapeShift) throw new Error("Trezor does not support Native ShapeShift"); if (output.addressNList) { @@ -107,7 +115,7 @@ export async function btcSignTx(wallet: core.BTCWallet, transport: TrezorTranspo amount: output.amount, script_type: translateOutputScriptType(output.scriptType), }; - } else if (output.addressType as core.BTCOutputAddressType == core.BTCOutputAddressType.Transfer) { + } else if ((output.addressType as core.BTCOutputAddressType) == core.BTCOutputAddressType.Transfer) { throw new Error("invalid arguments"); } @@ -115,7 +123,7 @@ export async function btcSignTx(wallet: core.BTCWallet, transport: TrezorTranspo return { address: output.address, amount: output.amount, - script_type: translateOutputScriptType(core.BTCOutputScriptType.PayToAddress) + script_type: translateOutputScriptType(core.BTCOutputScriptType.PayToAddress), }; } @@ -133,7 +141,7 @@ export async function btcSignTx(wallet: core.BTCWallet, transport: TrezorTranspo }); } - let res = await transport.call("signTransaction", { + const res = await transport.call("signTransaction", { coin: translateCoin(msg.coin), inputs: inputs, outputs: outputs, @@ -156,8 +164,11 @@ export function btcSupportsNativeShapeShift(): boolean { return false; } -export async function btcSignMessage(transport: TrezorTransport, msg: core.BTCSignMessage): Promise { - let res = await transport.call("signMessage", { +export async function btcSignMessage( + transport: TrezorTransport, + msg: core.BTCSignMessage +): Promise { + const res = await transport.call("signMessage", { path: msg.addressNList, message: msg.message, coin: msg.coin ? translateCoin(msg.coin) : undefined, @@ -172,7 +183,7 @@ export async function btcSignMessage(transport: TrezorTransport, msg: core.BTCSi } export async function btcVerifyMessage(transport: TrezorTransport, msg: core.BTCVerifyMessage): Promise { - let res = await transport.call("verifyMessage", { + const res = await transport.call("verifyMessage", { address: msg.address, message: msg.message, signature: Base64.fromByteArray(core.fromHexString(msg.signature)), diff --git a/packages/hdwallet-trezor/src/ethereum.ts b/packages/hdwallet-trezor/src/ethereum.ts index b35d23346..d92fd596a 100644 --- a/packages/hdwallet-trezor/src/ethereum.ts +++ b/packages/hdwallet-trezor/src/ethereum.ts @@ -1,10 +1,11 @@ -import * as core from "@shapeshiftoss/hdwallet-core"; import Common from "@ethereumjs/common"; import { Transaction } from "@ethereumjs/tx"; +import * as core from "@shapeshiftoss/hdwallet-core"; -import { handleError } from "./utils"; import { TrezorTransport } from "./transport"; +import { handleError } from "./utils"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars export async function ethSupportsNetwork(chain_id: number): Promise { return true; } @@ -13,13 +14,23 @@ export async function ethGetAddress(transport: TrezorTransport, msg: core.ETHGet const res = await transport.call("ethereumGetAddress", { path: core.addressNListToBIP32(msg.addressNList), showOnTrezor: !!msg.showDisplay, - address: msg.showDisplay ? await ethGetAddress(transport, { - ...msg, - showDisplay: false, - }) : undefined + address: msg.showDisplay + ? await ethGetAddress(transport, { + ...msg, + showDisplay: false, + }) + : undefined, }); handleError(transport, res, "Could not get ETH address from Trezor"); - return res.payload.address + return res.payload.address; +} + +export async function ethSupportsSecureTransfer(): Promise { + return false; +} + +export function ethSupportsNativeShapeShift(): boolean { + return false; } export async function ethSignTx( @@ -43,7 +54,7 @@ export async function ethSignTx( gasPrice: msg.gasPrice, }; - let res = await transport.call("ethereumSignTransaction", { + const res = await transport.call("ethereumSignTransaction", { path: msg.addressNList, transaction: utx, }); @@ -65,7 +76,7 @@ export async function ethSignMessage( transport: TrezorTransport, msg: core.ETHSignMessage ): Promise { - let res = await transport.call("ethereumSignMessage", { + const res = await transport.call("ethereumSignMessage", { path: msg.addressNList, message: msg.message, }); @@ -77,7 +88,7 @@ export async function ethSignMessage( } export async function ethVerifyMessage(transport: TrezorTransport, msg: core.ETHVerifyMessage): Promise { - let res = await transport.call("ethereumVerifyMessage", { + const res = await transport.call("ethereumVerifyMessage", { address: msg.address, message: msg.message, signature: core.stripHexPrefix(msg.signature), @@ -86,14 +97,6 @@ export async function ethVerifyMessage(transport: TrezorTransport, msg: core.ETH return res.payload.message === "Message verified"; } -export async function ethSupportsSecureTransfer(): Promise { - return false; -} - -export function ethSupportsNativeShapeShift(): boolean { - return false; -} - export function ethSupportsEIP1559(): boolean { return false; } diff --git a/packages/hdwallet-trezor/src/transport.ts b/packages/hdwallet-trezor/src/transport.ts index 3a627afe8..60f8e2747 100644 --- a/packages/hdwallet-trezor/src/transport.ts +++ b/packages/hdwallet-trezor/src/transport.ts @@ -6,7 +6,7 @@ export interface TrezorConnectResponse { } export abstract class TrezorTransport extends core.Transport { - hasPopup: boolean = false; + hasPopup = false; constructor(keyring: core.Keyring) { super(keyring); diff --git a/packages/hdwallet-trezor/src/trezor.ts b/packages/hdwallet-trezor/src/trezor.ts index 12ac10fa5..889666766 100644 --- a/packages/hdwallet-trezor/src/trezor.ts +++ b/packages/hdwallet-trezor/src/trezor.ts @@ -1,18 +1,18 @@ import * as core from "@shapeshiftoss/hdwallet-core"; import _ from "lodash"; -import { handleError } from "./utils"; import * as Btc from "./bitcoin"; import * as Eth from "./ethereum"; import { TrezorTransport } from "./transport"; +import { handleError } from "./utils"; export function isTrezor(wallet: core.HDWallet): wallet is TrezorHDWallet { return _.isObject(wallet) && (wallet as any)._isTrezor; } function describeETHPath(path: core.BIP32Path): core.PathDescription { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin: "Ethereum", isKnown: false, @@ -30,7 +30,7 @@ function describeETHPath(path: core.BIP32Path): core.PathDescription { if ((path[4] & 0x80000000) !== 0) return unknown; - let accountIdx = path[4] & 0x7fffffff; + const accountIdx = path[4] & 0x7fffffff; return { verbose: `Ethereum Account #${accountIdx}`, coin: "Ethereum", @@ -42,8 +42,8 @@ function describeETHPath(path: core.BIP32Path): core.PathDescription { } function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: core.BTCInputScriptType) { - let pathStr = core.addressNListToBIP32(path); - let unknown: core.PathDescription = { + const pathStr = core.addressNListToBIP32(path); + const unknown: core.PathDescription = { verbose: pathStr, coin, scriptType, @@ -58,7 +58,7 @@ function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: co if ((path[0] & 0x80000000) >>> 0 !== 0x80000000) return unknown; - let purpose = path[0] & 0x7fffffff; + const purpose = path[0] & 0x7fffffff; if (![44, 49, 84].includes(purpose)) return unknown; @@ -71,7 +71,7 @@ function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: co const slip44 = core.slip44ByCoin(coin); if (slip44 == undefined || path[1] !== 0x80000000 + slip44) return unknown; - let wholeAccount = path.length === 3; + const wholeAccount = path.length === 3; let script = scriptType ? ( @@ -93,7 +93,7 @@ function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: co script = ""; } - let accountIdx = path[2] & 0x7fffffff; + const accountIdx = path[2] & 0x7fffffff; if (wholeAccount) { return { @@ -106,8 +106,8 @@ function describeUTXOPath(path: core.BIP32Path, coin: core.Coin, scriptType?: co isPrefork: false, }; } else { - let change = path[3] === 1 ? "Change " : ""; - let addressIdx = path[4]; + const change = path[3] === 1 ? "Change " : ""; + const addressIdx = path[4]; return { verbose: `${coin} Account #${accountIdx}, ${change}Address #${addressIdx}${script}`, coin, @@ -191,8 +191,8 @@ export class TrezorHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInfo return true; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { - // It doesn't... yet? return false; } @@ -214,12 +214,12 @@ export class TrezorHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInfo } public btcNextAccountPath(msg: core.BTCAccountPath): core.BTCAccountPath | undefined { - let description = describeUTXOPath(msg.addressNList, msg.coin, msg.scriptType); + const description = describeUTXOPath(msg.addressNList, msg.coin, msg.scriptType); if (!description.isKnown) { return undefined; } - let addressNList = msg.addressNList; + const addressNList = msg.addressNList; if ( addressNList[0] === 0x80000000 + 44 || @@ -237,8 +237,8 @@ export class TrezorHDWalletInfo implements core.HDWalletInfo, core.BTCWalletInfo } public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - let addressNList = msg.hardenedPath.concat(msg.relPath); - let description = describeETHPath(addressNList); + const addressNList = msg.hardenedPath.concat(msg.relPath); + const description = describeETHPath(addressNList); if (!description.isKnown) { return undefined; } @@ -280,7 +280,7 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa } public async isInitialized(): Promise { - let features = await this.getFeatures(/*cached*/ true); + const features = await this.getFeatures(/*cached*/ true); return features.initialized; } @@ -290,12 +290,12 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa } = this.transport as any; if (transportId) return transportId; - let features = await this.getFeatures(/*cached*/ true); + const features = await this.getFeatures(/*cached*/ true); return features.device_id; } public async getFirmwareVersion(): Promise { - let features = await this.getFeatures(/*cached*/ true); + const features = await this.getFeatures(/*cached*/ true); return `v${features.major_version}.${features.minor_version}.${features.patch_version}`; } @@ -304,18 +304,18 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa } public async getModel(): Promise { - let features = await this.getFeatures(/*cached*/ true); + const features = await this.getFeatures(/*cached*/ true); return "Trezor " + features.model; } public async getLabel(): Promise { - let features = await this.getFeatures(/*cached*/ true); + const features = await this.getFeatures(/*cached*/ true); return typeof features.label === "string" ? features.label : ""; } - public async getFeatures(cached: boolean = false): Promise { + public async getFeatures(cached = false): Promise { if (cached && this.featuresCache) return this.featuresCache; - let res = await this.transport.call("getFeatures", {}); + const res = await this.transport.call("getFeatures", {}); handleError(this.transport, res, "Could not get Trezor features"); this.cacheFeatures(res.payload); return res.payload; @@ -327,7 +327,7 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa public async getPublicKeys(msg: Array): Promise> { if (!msg.length) return []; - let res = await this.transport.call("getPublicKey", { + const res = await this.transport.call("getPublicKey", { bundle: msg.map((request) => { return { path: request.addressNList, @@ -388,10 +388,12 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa }); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendCharacter(charater: string): Promise { throw new Error("Trezor does not suport chiphered recovery"); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendWord(word: string): Promise { throw new Error("Trezor does not yet support recoverDevice"); } @@ -403,12 +405,12 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa } public async wipe(): Promise { - let res = await this.transport.call("wipeDevice", {}); + const res = await this.transport.call("wipeDevice", {}); handleError(this.transport, res, "Could not wipe Trezor"); } public async reset(msg: core.ResetDevice): Promise { - let res = await this.transport.call("resetDevice", { + const res = await this.transport.call("resetDevice", { strength: msg.entropy, label: msg.label, pinProtection: msg.pin, @@ -421,6 +423,7 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa await this.transport.cancel(); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async recover(msg: core.RecoverDevice): Promise { // https://github.com/trezor/connect/pull/320 throw new Error("TrezorConnect does not expose Core.RecoverDevice... yet?"); @@ -428,7 +431,7 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa public async loadDevice(msg: core.LoadDevice): Promise { // https://github.com/trezor/connect/issues/363 - let res = await this.transport.call("loadDevice", { + const res = await this.transport.call("loadDevice", { mnemonic: msg.mnemonic, pin: msg.pin, passphraseProtection: msg.passphrase, @@ -454,8 +457,8 @@ export class TrezorHDWallet implements core.HDWallet, core.BTCWallet, core.ETHWa return this.transport.hasPopup; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public hasNativeShapeShift(srcCoin: core.Coin, dstCoin: core.Coin): boolean { - // It doesn't... yet? return false; } diff --git a/packages/hdwallet-xdefi/src/adapter.test.ts b/packages/hdwallet-xdefi/src/adapter.test.ts index 59addae22..3f7513b84 100644 --- a/packages/hdwallet-xdefi/src/adapter.test.ts +++ b/packages/hdwallet-xdefi/src/adapter.test.ts @@ -8,11 +8,7 @@ describe("XDeFiAdapter", () => { const keyring = new core.Keyring(); const adapter = XDeFiAdapter.useKeyring(keyring); expect(await adapter.initialize()).toBe(0); - try { - await adapter.pairDevice(); - } catch (e) { - expect(e.message).toBe("XDeFi provider not found"); - } + await expect(async () => await adapter.pairDevice()).rejects.toThrowError("XDeFi provider not found"); }); it("creates a unique wallet per deviceId", async () => { Object.defineProperty(globalThis, "xfi", { diff --git a/packages/hdwallet-xdefi/src/adapter.ts b/packages/hdwallet-xdefi/src/adapter.ts index b6743f167..47dc75321 100644 --- a/packages/hdwallet-xdefi/src/adapter.ts +++ b/packages/hdwallet-xdefi/src/adapter.ts @@ -1,4 +1,5 @@ import * as core from "@shapeshiftoss/hdwallet-core"; + import { XDeFiHDWallet } from "./xdefi"; export class XDeFiAdapter { diff --git a/packages/hdwallet-xdefi/src/ethereum.test.ts b/packages/hdwallet-xdefi/src/ethereum.test.ts index ab3a66e24..55bee789a 100644 --- a/packages/hdwallet-xdefi/src/ethereum.test.ts +++ b/packages/hdwallet-xdefi/src/ethereum.test.ts @@ -1,12 +1,7 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import { ETHSignedMessage } from "@shapeshiftoss/hdwallet-core"; import * as ethereum from "./ethereum"; -const MNEMONIC = "all all all all all all all all all all all all"; - -const untouchable = require("untouchableMock"); - describe("XDeFi - Ethereum Adapter", () => { it("ethVerifyMessage returns null as its not implemented", async () => { const ethereumProvider = { @@ -140,7 +135,7 @@ describe("XDeFi - Ethereum Adapter", () => { expect(hash).toBe(null); }); - it("ethSignMessage returns a valid signature object ", async () => { + it("ethSignMessage returns a valid signature object", async () => { const ethereumProvider = { request: jest.fn().mockReturnValue( `Object { @@ -189,7 +184,7 @@ describe("XDeFi - Ethereum Adapter", () => { expect(sig).toBe(null); }); - it("ethGetAddress returns a valid address ", async () => { + it("ethGetAddress returns a valid address", async () => { const ethereumProvider = { request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]), }; @@ -198,7 +193,7 @@ describe("XDeFi - Ethereum Adapter", () => { expect(address).toBe("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"); }); - it("ethGetAddress returns null on error ", async () => { + it("ethGetAddress returns null on error", async () => { const ethereumProvider = { request: jest.fn().mockRejectedValue(new Error("An error has occurred")), }; @@ -207,7 +202,7 @@ describe("XDeFi - Ethereum Adapter", () => { expect(address).toBe(null); }); - it("ethGetAddress returns null if no provider ", async () => { + it("ethGetAddress returns null if no provider", async () => { const ethereumProvider = {}; const address = await ethereum.ethGetAddress(ethereumProvider); diff --git a/packages/hdwallet-xdefi/src/xdefi.test.ts b/packages/hdwallet-xdefi/src/xdefi.test.ts index c39834e6d..93ef810b7 100644 --- a/packages/hdwallet-xdefi/src/xdefi.test.ts +++ b/packages/hdwallet-xdefi/src/xdefi.test.ts @@ -1,4 +1,5 @@ import * as core from "@shapeshiftoss/hdwallet-core"; + import { XDeFiHDWallet, XDeFiHDWalletInfo } from "."; describe("XDeFIHDWalletInfo", () => { @@ -121,7 +122,7 @@ describe("XDeFiHDWallet", () => { expect(sig).toBe(null); }); - it("ethGetAddress returns a valid address ", async () => { + it("ethGetAddress returns a valid address", async () => { wallet.provider = { request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]), }; diff --git a/packages/hdwallet-xdefi/src/xdefi.ts b/packages/hdwallet-xdefi/src/xdefi.ts index 28199346a..4b578d32a 100644 --- a/packages/hdwallet-xdefi/src/xdefi.ts +++ b/packages/hdwallet-xdefi/src/xdefi.ts @@ -1,12 +1,14 @@ import * as core from "@shapeshiftoss/hdwallet-core"; -import * as eth from "./ethereum"; import isObject from "lodash/isObject"; +import * as eth from "./ethereum"; + class XDeFiTransport extends core.Transport { public async getDeviceID() { return "xdefi:0"; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function public async call(...args: any[]): Promise {} } @@ -14,6 +16,77 @@ export function isXDeFi(wallet: core.HDWallet): wallet is XDeFiHDWallet { return isObject(wallet) && (wallet as any)._isXDeFi; } +export class XDeFiHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo { + readonly _supportsETHInfo = true; + + public getVendor(): string { + return "XDeFi"; + } + + public hasOnDevicePinEntry(): boolean { + return false; + } + + public hasOnDevicePassphrase(): boolean { + return false; + } + + public hasOnDeviceDisplay(): boolean { + return false; + } + + public hasOnDeviceRecovery(): boolean { + return false; + } + + public hasNativeShapeShift(): boolean { + return false; + } + + public supportsOfflineSigning(): boolean { + return false; + } + + public supportsBroadcast(): boolean { + return true; + } + + public describePath(msg: core.DescribePath): core.PathDescription { + switch (msg.coin) { + case "Ethereum": + return core.describeETHPath(msg.path); + default: + throw new Error("Unsupported path"); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { + // TODO: What do we do here? + return undefined; + } + + public async ethSupportsNetwork(chainId = 1): Promise { + return chainId === 1; + } + + public async ethSupportsSecureTransfer(): Promise { + return false; + } + + public ethSupportsNativeShapeShift(): boolean { + return false; + } + + public async ethSupportsEIP1559(): Promise { + return true; + } + + public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { + return eth.ethGetAccountPaths(msg); + } +} + export class XDeFiHDWallet implements core.HDWallet, core.ETHWallet { readonly _supportsETH = true; readonly _supportsETHInfo = true; @@ -26,7 +99,7 @@ export class XDeFiHDWallet implements core.HDWallet, core.ETHWallet { constructor(provider: unknown) { this.info = new XDeFiHDWalletInfo(); - this.provider = provider + this.provider = provider; } async getFeatures(): Promise> { @@ -90,18 +163,22 @@ export class XDeFiHDWallet implements core.HDWallet, core.ETHWallet { return { msg: msg.msg }; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendPin(pin: string): Promise { // no concept of pin in XDeFi } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendPassphrase(passphrase: string): Promise { // cannot send passphrase to XDeFi. Could show the widget? } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendCharacter(charater: string): Promise { // no concept of sendCharacter in XDeFi } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendWord(word: string): Promise { // no concept of sendWord in XDeFi } @@ -110,14 +187,18 @@ export class XDeFiHDWallet implements core.HDWallet, core.ETHWallet { // no concept of cancel in XDeFi } + // eslint-disable-next-line @typescript-eslint/no-empty-function public async wipe(): Promise {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function public async reset(msg: core.ResetDevice): Promise {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async recover(msg: core.RecoverDevice): Promise { // no concept of recover in XDeFi } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async loadDevice(msg: core.LoadDevice): Promise { // TODO: Does XDeFi allow this to be done programatically? } @@ -126,6 +207,7 @@ export class XDeFiHDWallet implements core.HDWallet, core.ETHWallet { return this.info.describePath(msg); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars public async getPublicKeys(msg: Array): Promise> { // Ethereum public keys are not exposed by the RPC API return []; @@ -135,9 +217,10 @@ export class XDeFiHDWallet implements core.HDWallet, core.ETHWallet { return true; } + // eslint-disable-next-line @typescript-eslint/no-empty-function public async disconnect(): Promise {} - public async ethSupportsNetwork(chainId: number = 1): Promise { + public async ethSupportsNetwork(chainId = 1): Promise { return chainId === 1; } @@ -202,73 +285,3 @@ export class XDeFiHDWallet implements core.HDWallet, core.ETHWallet { return "xDeFi"; } } - -export class XDeFiHDWalletInfo implements core.HDWalletInfo, core.ETHWalletInfo { - readonly _supportsETHInfo = true; - - public getVendor(): string { - return "XDeFi"; - } - - public hasOnDevicePinEntry(): boolean { - return false; - } - - public hasOnDevicePassphrase(): boolean { - return false; - } - - public hasOnDeviceDisplay(): boolean { - return false; - } - - public hasOnDeviceRecovery(): boolean { - return false; - } - - public hasNativeShapeShift(): boolean { - return false; - } - - public supportsOfflineSigning(): boolean { - return false; - } - - public supportsBroadcast(): boolean { - return true; - } - - public describePath(msg: core.DescribePath): core.PathDescription { - switch (msg.coin) { - case "Ethereum": - return core.describeETHPath(msg.path); - default: - throw new Error("Unsupported path"); - } - } - - public ethNextAccountPath(msg: core.ETHAccountPath): core.ETHAccountPath | undefined { - // TODO: What do we do here? - return undefined; - } - - public async ethSupportsNetwork(chainId: number = 1): Promise { - return chainId === 1; - } - - public async ethSupportsSecureTransfer(): Promise { - return false; - } - - public ethSupportsNativeShapeShift(): boolean { - return false; - } - - public async ethSupportsEIP1559(): Promise { - return true; - } - - public ethGetAccountPaths(msg: core.ETHGetAccountPath): Array { - return eth.ethGetAccountPaths(msg); - } -} diff --git a/yarn.lock b/yarn.lock index 4c8a1e4b0..dfe247475 100644 --- a/yarn.lock +++ b/yarn.lock @@ -763,6 +763,21 @@ global-agent "^2.0.2" global-tunnel-ng "^2.7.1" +"@eslint/eslintrc@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" + integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.1" + globals "^13.9.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.4.0.tgz#2d67f6e6ba22246c5c89104e6b9a119fb3039766" @@ -1448,6 +1463,20 @@ randombytes "^2.1.0" text-encoding "0.7.0" +"@humanwhocodes/config-array@^0.9.2": + version "0.9.5" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" + integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -2752,11 +2781,32 @@ "@types/set-cookie-parser" "^2.4.0" set-cookie-parser "^2.4.6" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + "@nodelib/fs.stat@^1.1.2": version "1.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@octokit/auth-token@^2.4.0": version "2.4.5" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" @@ -3952,16 +4002,18 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== +"@types/create-hash@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/create-hash/-/create-hash-1.2.2.tgz#e87247083df8478f6b83655592bde0d709028235" + integrity sha512-Fg8/kfMJObbETFU/Tn+Y0jieYewryLrbKwLCEIwPyklZZVY2qB+64KFjhplGSw+cseZosfFXctXO+PyIYD8iZQ== + dependencies: + "@types/node" "*" + "@types/crypto-js@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.0.2.tgz#4524325a175bf819fec6e42560c389ce1fb92c97" integrity sha512-sCVniU+h3GcGqxOmng11BRvf9TfN9yIs8KKjB8C8d75W69cpTfZG80gau9yTx5SxF3gvHGbJhdESzzvnjtf3Og== -"@types/debug@^4.1.2", "@types/debug@^4.1.5": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.6.tgz#0b7018723084918a865eff99249c490505df2163" - integrity sha512-7fDOJFA/x8B+sO1901BmHlf5dE1cxBU8mRXj8QOEDnn16hhGJv/IHxJtZhvsabZsIMn0eLIyeOKAeqSNJJYTpA== - "@types/elliptic@^6.4.12": version "6.4.13" resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.13.tgz#7e8ac814f748deb01a712e5147b128caf9dffa2d" @@ -4060,6 +4112,11 @@ resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.0.tgz#9541eec4ad6e3ec5633270a3a2b55d981edc44a9" integrity sha512-14t0v1ICYRtRVcHASzes0v/O+TIeASb8aD55cWF1PidtInhFWSXcmhzhHqGjUWf9SUq1w70cvd1cWKUULubAfQ== +"@types/json-schema@^7.0.9": + version "7.0.10" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" + integrity sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A== + "@types/ledgerhq__hw-transport@^4.21.3": version "4.21.4" resolved "https://registry.yarnpkg.com/@types/ledgerhq__hw-transport/-/ledgerhq__hw-transport-4.21.4.tgz#3a78a02d2b51d2b0dd8099412d5567d21118225c" @@ -4284,6 +4341,86 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^5.14.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz#78f246dd8d1b528fc5bfca99a8a64d4023a3d86d" + integrity sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw== + dependencies: + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/type-utils" "5.16.0" + "@typescript-eslint/utils" "5.16.0" + debug "^4.3.2" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.14.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.16.0.tgz#e4de1bde4b4dad5b6124d3da227347616ed55508" + integrity sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA== + dependencies: + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/typescript-estree" "5.16.0" + debug "^4.3.2" + +"@typescript-eslint/scope-manager@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz#7e7909d64bd0c4d8aef629cdc764b9d3e1d3a69a" + integrity sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ== + dependencies: + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/visitor-keys" "5.16.0" + +"@typescript-eslint/type-utils@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz#b482bdde1d7d7c0c7080f7f2f67ea9580b9e0692" + integrity sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ== + dependencies: + "@typescript-eslint/utils" "5.16.0" + debug "^4.3.2" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.16.0.tgz#5827b011982950ed350f075eaecb7f47d3c643ee" + integrity sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g== + +"@typescript-eslint/typescript-estree@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz#32259459ec62f5feddca66adc695342f30101f61" + integrity sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ== + dependencies: + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/visitor-keys" "5.16.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.16.0", "@typescript-eslint/utils@^5.10.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.16.0.tgz#42218b459d6d66418a4eb199a382bdc261650679" + integrity sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/typescript-estree" "5.16.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz#f27dc3b943e6317264c7492e390c6844cd4efbbb" + integrity sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g== + dependencies: + "@typescript-eslint/types" "5.16.0" + eslint-visitor-keys "^3.0.0" + "@walletconnect/browser-utils@^1.6.6": version "1.6.6" resolved "https://registry.yarnpkg.com/@walletconnect/browser-utils/-/browser-utils-1.6.6.tgz#a985b48c99c65a986a051d66a4910010a10a0c56" @@ -4511,6 +4648,11 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + acorn-walk@^6.0.1: version "6.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" @@ -4536,7 +4678,7 @@ acorn@^8.2.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== -acorn@^8.5.0: +acorn@^8.5.0, acorn@^8.7.0: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== @@ -4579,7 +4721,7 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -ajv@^6.10.2, ajv@^6.12.3: +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -4696,6 +4838,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -4743,6 +4890,11 @@ array-union@^1.0.2: dependencies: array-uniq "^1.0.1" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -6552,7 +6704,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -6783,7 +6935,7 @@ debug@3.1.0, debug@=3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.0: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== @@ -6797,6 +6949,13 @@ debug@^3.1.0: dependencies: ms "^2.1.1" +debug@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -6849,6 +7008,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -7004,6 +7168,20 @@ dir-glob@^2.2.2: dependencies: path-type "^3.0.0" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dogecoin-regex@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/dogecoin-regex/-/dogecoin-regex-1.0.10.tgz#0cb93bec25c4f2ff2166fb1bac839e089434a6c7" @@ -7431,6 +7609,46 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-config-prettier@8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + +eslint-plugin-jest@26.1.1: + version "26.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.1.1.tgz#7176dd745ef8bca3070263f62cdf112f2dfc9aa1" + integrity sha512-HRKOuPi5ADhza4ZBK5ufyNXy28bXXkib87w+pQqdvBhSTsamndh6sIAKPAUl8y0/n9jSWBdTPslrwtKWqkp8dA== + dependencies: + "@typescript-eslint/utils" "^5.10.0" + +eslint-plugin-prettier@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" + integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-simple-import-sort@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz#a1dad262f46d2184a90095a60c66fef74727f0f8" + integrity sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw== + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-utils@^1.4.2: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" @@ -7438,21 +7656,107 @@ eslint-utils@^1.4.2: dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.10.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37" + integrity sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA== + dependencies: + "@eslint/eslintrc" "^1.2.1" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== + dependencies: + acorn "^8.7.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^3.3.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -estraverse@^4.2.0: +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" @@ -8110,6 +8414,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -8122,12 +8431,23 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -8142,6 +8462,13 @@ fast-text-encoding@^1.0.3: resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -8180,6 +8507,13 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + 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" @@ -8270,6 +8604,19 @@ fiosdk-offline@^1.2.21: validate "^5.1.0" wif "^2.0.6" +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.5" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -8604,13 +8951,20 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@~5.1.2: +glob-parent@^5.0.0, glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" @@ -8671,6 +9025,13 @@ globals@^13.2.0: dependencies: type-fest "^0.20.2" +globals@^13.6.0, globals@^13.9.0: + version "13.13.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" + integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== + dependencies: + type-fest "^0.20.2" + globalthis@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" @@ -8678,6 +9039,18 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" +globby@^11.0.4: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -9110,6 +9483,11 @@ ignore@^4.0.3: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.8, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + immediate@^3.2.3: version "3.3.0" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" @@ -9123,7 +9501,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.2.1: +import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -9485,6 +9863,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-hex-prefixed@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" @@ -10268,6 +10653,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -10399,6 +10791,11 @@ json-source-map@^0.6.1: resolved "https://registry.yarnpkg.com/json-source-map/-/json-source-map-0.6.1.tgz#e0b1f6f4ce13a9ad57e2ae165a24d06e62c79a0f" integrity sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg== +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" @@ -10627,6 +11024,14 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -10770,6 +11175,11 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -11102,7 +11512,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3: +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -12009,6 +12419,18 @@ optionator@^0.8.1: type-check "~0.3.2" word-wrap "~1.2.3" +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + ordered-binary@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.2.4.tgz#51d3a03af078a0bdba6c7bc8f4fedd1f5d45d83e" @@ -12784,6 +13206,11 @@ precond@0.2: resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -12799,6 +13226,13 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^2.0.5: version "2.3.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" @@ -13130,6 +13564,11 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" @@ -13411,6 +13850,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -13580,6 +14024,11 @@ retry@^0.10.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -13587,7 +14036,7 @@ rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -13707,6 +14156,13 @@ run-async@^2.2.0, run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -14570,6 +15026,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -14623,6 +15086,11 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -14822,6 +15290,11 @@ text-hex@1.0.x: resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -15082,7 +15555,7 @@ ts-node@^8.10.2: source-map-support "^0.5.17" yn "3.1.1" -tslib@^1.10.0, tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -15097,6 +15570,13 @@ tslib@~2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -15129,6 +15609,13 @@ type-assertions@^1.1.0: resolved "https://registry.yarnpkg.com/type-assertions/-/type-assertions-1.1.0.tgz#51c5189fc6c1bdc1c19f48bf5ace6cc619917977" integrity sha512-LJ5h6n63vxS8fSdfTPqIc6IrbCo9X3g6Se+wSikCGsqaAI3ajN0iputclNG07wdWfBoQZIrpASjBQo5BeVNrAg== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -15510,7 +15997,7 @@ uuid@^8.3.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache@^2.0.0: +v8-compile-cache@^2.0.0, v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== @@ -16084,7 +16571,7 @@ winston@^3.3.3: triple-beam "^1.3.0" winston-transport "^4.4.0" -word-wrap@~1.2.3: +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" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==