From 1ff584966e9dbf542386bd1b301ce63dbe0c24ff Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 8 Apr 2024 09:01:03 +0100 Subject: [PATCH 01/75] feat: add library @metamask/eth-ledger-bridge-keyring, and the patch the library with local change. 1. modify the Engine.ts to use new library 2. modify Ledger.ts to use new library will expect some errors during build, will fix this error and unit tests in next commit. --- app/core/Engine.ts | 13 +- app/core/Ledger/Ledger.ts | 25 +- ios/Podfile.lock | 4 +- package.json | 4 +- ...mask+eth-ledger-bridge-keyring+3.0.0.patch | 680 ++++++++++++++++++ yarn.lock | 104 +-- 6 files changed, 742 insertions(+), 88 deletions(-) create mode 100644 patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch diff --git a/app/core/Engine.ts b/app/core/Engine.ts index 5faf3bccb7f..a95ae6407c1 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -121,7 +121,12 @@ import { LoggingControllerState, LoggingControllerActions, } from '@metamask/logging-controller'; -import LedgerKeyring from '@consensys/ledgerhq-metamask-keyring'; +import { + LedgerKeyring, + LedgerMobileBridge, + LedgerTransportMiddleware, +} from '@metamask/eth-ledger-bridge-keyring'; + import Encryptor from './Encryptor'; import { isMainnetByChainId, @@ -203,6 +208,7 @@ interface TestOrigin { type: `${PhishingController['name']}:testOrigin`; handler: PhishingController['test']; } + type PhishingControllerActions = MaybeUpdateState | TestOrigin; type SnapsGlobalActions = @@ -341,6 +347,7 @@ class Engine { * Object that runs and manages the execution of Snaps */ snapExecutionService: WebViewExecutionService; + ///: END:ONLY_INCLUDE_IF /** @@ -616,7 +623,9 @@ class Engine { const qrKeyringBuilder = () => new QRHardwareKeyring(); qrKeyringBuilder.type = QRHardwareKeyring.type; - const ledgerKeyringBuilder = () => new LedgerKeyring(); + const middleware = new LedgerTransportMiddleware(); + const bridge = new LedgerMobileBridge(middleware); + const ledgerKeyringBuilder = () => new LedgerKeyring({ bridge }); ledgerKeyringBuilder.type = LedgerKeyring.type; const keyringController = new KeyringController({ diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index b86f7106e9a..d6a844c789f 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -1,4 +1,7 @@ -import LedgerKeyring from '@consensys/ledgerhq-metamask-keyring'; +import { + LedgerKeyring, + LedgerMobileBridge, +} from '@metamask/eth-ledger-bridge-keyring'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; import ExtendedKeyringTypes from '../../constants/keyringTypes'; @@ -43,8 +46,11 @@ export const connectLedgerHardware = async ( deviceId: string, ): Promise => { const keyring = await getLedgerKeyring(); - keyring.setTransport(transport as unknown as any, deviceId); - const { appName } = await keyring.getAppAndVersion(); + keyring.setHdPath("m/44'/60'/0'/0"); + const bridge = keyring.bridge as LedgerMobileBridge; + bridge.updateTransportMethod(transport); + // keyring.setTransport(transport as unknown as any, deviceId); + const { appName } = await bridge.getAppNameAndVersion(); return appName; }; @@ -66,10 +72,10 @@ export const unlockLedgerDefaultAccount = async ( if (isAccountImportReq) { await keyringController.addNewAccountForKeyring(keyring); } - const address = await keyring.getDefaultAccount(); + const address = await keyring.getFirstPage(); return { - address, + address: address[0].address, balance: `0x0`, }; }; @@ -79,7 +85,8 @@ export const unlockLedgerDefaultAccount = async ( */ export const openEthereumAppOnLedger = async (): Promise => { const keyring = await getLedgerKeyring(); - await keyring.openEthApp(); + const bridge = keyring.bridge as LedgerMobileBridge; + await bridge.openEthApp(); }; /** @@ -87,7 +94,8 @@ export const openEthereumAppOnLedger = async (): Promise => { */ export const closeRunningAppOnLedger = async (): Promise => { const keyring = await getLedgerKeyring(); - await keyring.quitApp(); + const bridge = keyring.bridge as LedgerMobileBridge; + await bridge.closeApps(); }; /** @@ -110,7 +118,8 @@ export const forgetLedger = async (): Promise => { */ export const getDeviceId = async (): Promise => { const ledgerKeyring = await getLedgerKeyring(); - return ledgerKeyring.deviceId; + const bridge = ledgerKeyring.bridge as LedgerMobileBridge; + return bridge.deviceId; }; /** diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 951e5505d7d..84f0501e9bb 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -350,7 +350,7 @@ PODS: - React-Core - react-native-camera/RN (3.44.3): - React-Core - - react-native-compat (2.11.0): + - react-native-compat (2.11.3): - RCT-Folly (= 2021.07.22.00) - React-Core - react-native-cookies (6.2.1): @@ -951,7 +951,7 @@ SPEC CHECKSUMS: react-native-blur: cfdad7b3c01d725ab62a8a729f42ea463998afa2 react-native-branch: 4e42fda662d96893afbbd02839806931398e3d2e react-native-camera: b8cc03e2feec0c04403d0998e37cf519d8fd4c6f - react-native-compat: d411b454141009ceca781cd1ca44132cace77c45 + react-native-compat: 4da17905c26471d956dae70f9912a60d7da7fad2 react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a react-native-gzip: c5e87ee9e359f02350e3a2ee52eb35eddc398868 diff --git a/package.json b/package.json index 6ee1f1b0a4a..19d8c429e8a 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "**/babel-runtime/regenerator-runtime": "^0.13.8" }, "dependencies": { - "@consensys/ledgerhq-metamask-keyring": "0.0.9", + "@metamask/eth-ledger-bridge-keyring": "3.0.0", "@consensys/on-ramp-sdk": "1.26.8", "@eth-optimism/contracts": "0.0.0-2021919175625", "@ethereumjs/common": "^2.3.1", @@ -554,4 +554,4 @@ "@metamask/sdk-communication-layer>utf-8-validate": false } } -} \ No newline at end of file +} diff --git a/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch b/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch new file mode 100644 index 00000000000..e51e53d9e1a --- /dev/null +++ b/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch @@ -0,0 +1,680 @@ +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store b/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store +new file mode 100644 +index 0000000..7a56c26 +Binary files /dev/null and b/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store differ +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store +new file mode 100644 +index 0000000..062174d +Binary files /dev/null and b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store differ +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.d.ts +index b08ff49..34ffdbd 100644 +--- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.d.ts ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.d.ts +@@ -1,3 +1,5 @@ + export * from './ledger-keyring'; + export * from './ledger-iframe-bridge'; ++export * from './ledger-mobile-bridge'; ++export * from './ledger-mobile-bridge/'; + export * from './ledger-bridge'; +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.js +index 743adcf..264b7e2 100644 +--- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.js ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.js +@@ -16,5 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { + Object.defineProperty(exports, "__esModule", { value: true }); + __exportStar(require("./ledger-keyring"), exports); + __exportStar(require("./ledger-iframe-bridge"), exports); ++__exportStar(require("./ledger-mobile-bridge"), exports); ++__exportStar(require("./ledger-mobile-bridge/"), exports); + __exportStar(require("./ledger-bridge"), exports); + //# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.js.map +index de9eb8f..a940cea 100644 +--- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.js.map ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.js.map +@@ -1 +1 @@ +-{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,yDAAuC;AACvC,kDAAgC","sourcesContent":["export * from './ledger-keyring';\nexport * from './ledger-iframe-bridge';\nexport * from './ledger-bridge';\n"]} +\ No newline at end of file ++{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,yDAAuC;AACvC,yDAAuC;AACvC,0DAAwC;AACxC,kDAAgC","sourcesContent":["export * from './ledger-keyring';\nexport * from './ledger-iframe-bridge';\nexport * from './ledger-mobile-bridge';\nexport * from './ledger-mobile-bridge/';\nexport * from './ledger-bridge';\n"]} +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.d.ts +index 518c632..30e0e7a 100644 +--- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.d.ts ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.d.ts +@@ -1,10 +1,9 @@ + import type LedgerHwAppEth from '@ledgerhq/hw-app-eth'; ++import { Transport } from '@ledgerhq/types-devices'; + export declare type GetPublicKeyParams = { + hdPath: string; + }; +-export declare type GetPublicKeyResponse = Awaited> & { +- chainCode: string; +-}; ++export declare type GetPublicKeyResponse = Awaited>; + export declare type LedgerSignTransactionParams = { + hdPath: string; + tx: string; +@@ -37,7 +36,7 @@ export declare type LedgerBridge = { + */ + setOptions(opts: T): Promise; + attemptMakeApp(): Promise; +- updateTransportMethod(transportType: string): Promise; ++ updateTransportMethod(transportType: string | Transport): Promise; + getPublicKey(params: GetPublicKeyParams): Promise; + deviceSignTransaction(params: LedgerSignTransactionParams): Promise; + deviceSignMessage(params: LedgerSignMessageParams): Promise; +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map +index 0932bdd..321e28e 100644 +--- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map +@@ -1 +1 @@ +-{"version":3,"file":"ledger-bridge.js","sourceRoot":"","sources":["../src/ledger-bridge.ts"],"names":[],"mappings":"","sourcesContent":["import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';\n\nexport type GetPublicKeyParams = { hdPath: string };\nexport type GetPublicKeyResponse = Awaited<\n ReturnType\n> & {\n chainCode: string;\n};\n\nexport type LedgerSignTransactionParams = { hdPath: string; tx: string };\nexport type LedgerSignTransactionResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignMessageParams = { hdPath: string; message: string };\nexport type LedgerSignMessageResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTypedDataParams = {\n hdPath: string;\n domainSeparatorHex: string;\n hashStructMessageHex: string;\n};\nexport type LedgerSignTypedDataResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerBridgeOptions = Record;\n\nexport type LedgerBridge = {\n isDeviceConnected: boolean;\n\n init(): Promise;\n\n destroy(): Promise;\n\n /**\n * Method to get the current configuration of the ledger bridge keyring.\n */\n getOptions(): Promise;\n\n /**\n * Method to set the current configuration of the ledger bridge keyring.\n *\n * @param opts - An object contains configuration of the bridge.\n */\n setOptions(opts: T): Promise;\n\n attemptMakeApp(): Promise;\n\n updateTransportMethod(transportType: string): Promise;\n\n getPublicKey(params: GetPublicKeyParams): Promise;\n\n deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise;\n\n deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise;\n\n deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise;\n};\n"]} +\ No newline at end of file ++{"version":3,"file":"ledger-bridge.js","sourceRoot":"","sources":["../src/ledger-bridge.ts"],"names":[],"mappings":"","sourcesContent":["import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';\nimport { Transport } from '@ledgerhq/types-devices';\n\nexport type GetPublicKeyParams = { hdPath: string };\nexport type GetPublicKeyResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTransactionParams = { hdPath: string; tx: string };\nexport type LedgerSignTransactionResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignMessageParams = { hdPath: string; message: string };\nexport type LedgerSignMessageResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTypedDataParams = {\n hdPath: string;\n domainSeparatorHex: string;\n hashStructMessageHex: string;\n};\nexport type LedgerSignTypedDataResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerBridgeOptions = Record;\n\nexport type LedgerBridge = {\n isDeviceConnected: boolean;\n\n init(): Promise;\n\n destroy(): Promise;\n\n /**\n * Method to get the current configuration of the ledger bridge keyring.\n */\n getOptions(): Promise;\n\n /**\n * Method to set the current configuration of the ledger bridge keyring.\n *\n * @param opts - An object contains configuration of the bridge.\n */\n setOptions(opts: T): Promise;\n\n attemptMakeApp(): Promise;\n\n updateTransportMethod(transportType: string | Transport): Promise;\n\n getPublicKey(params: GetPublicKeyParams): Promise;\n\n deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise;\n\n deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise;\n\n deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise;\n};\n"]} +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js +index cd18e51..13ed60d 100644 +--- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js +@@ -295,7 +295,11 @@ class LedgerKeyring extends events_1.EventEmitter { + if (recoveryId.length < 2) { + recoveryId = `0${recoveryId}`; + } +- const signature = `0x${payload.r}${payload.s}${recoveryId}`; ++ let modifiedV = parseInt(String(payload.v), 10).toString(16); ++ if (modifiedV.length < 2) { ++ modifiedV = `0${modifiedV}`; ++ } ++ const signature = `0x${payload.r}${payload.s}${modifiedV}`; + const addressSignedWith = (0, eth_sig_util_1.recoverPersonalSignature)({ + data: message, + signature, +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js.map +index 2b458a0..c02e4ce 100644 +--- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js.map ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js.map +@@ -1 +1 @@ +-{"version":3,"file":"ledger-keyring.js","sourceRoot":"","sources":["../src/ledger-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { RLP } from '@ethereumjs/rlp';\nimport { TransactionFactory, TxData, TypedTransaction } from '@ethereumjs/tx';\nimport * as ethUtil from '@ethereumjs/util';\nimport type { MessageTypes, TypedMessage } from '@metamask/eth-sig-util';\nimport {\n recoverPersonalSignature,\n recoverTypedSignature,\n SignTypedDataVersion,\n TypedDataUtils,\n} from '@metamask/eth-sig-util';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { Buffer } from 'buffer';\nimport type OldEthJsTransaction from 'ethereumjs-tx';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { EventEmitter } from 'events';\nimport HDKey from 'hdkey';\n\nimport { LedgerBridge, LedgerBridgeOptions } from './ledger-bridge';\n\nconst pathBase = 'm';\nconst hdPathString = `${pathBase}/44'/60'/0'`;\nconst keyringType = 'Ledger Hardware';\n\nconst MAX_INDEX = 1000;\n\nenum NetworkApiUrls {\n Ropsten = 'http://api-ropsten.etherscan.io',\n Kovan = 'http://api-kovan.etherscan.io',\n Rinkeby = 'https://api-rinkeby.etherscan.io',\n Mainnet = 'https://api.etherscan.io',\n}\n\ntype SignTransactionPayload = Awaited<\n ReturnType['deviceSignTransaction']>\n>;\n\nexport type AccountDetails = {\n index?: number;\n bip44?: boolean;\n hdPath?: string;\n};\n\nexport type LedgerBridgeKeyringOptions = {\n hdPath: string;\n accounts: readonly string[];\n accountDetails: Readonly>;\n accountIndexes: Readonly>;\n implementFullBIP44: boolean;\n};\n\n/**\n * Check if the given transaction is made with ethereumjs-tx or @ethereumjs/tx\n *\n * Transactions built with older versions of ethereumjs-tx have a\n * getChainId method that newer versions do not.\n * Older versions are mutable\n * while newer versions default to being immutable.\n * Expected shape and type\n * of data for v, r and s differ (Buffer (old) vs BN (new)).\n *\n * @param tx - Transaction to check, instance of either ethereumjs-tx or @ethereumjs/tx.\n * @returns Returns `true` if tx is an old-style ethereumjs-tx transaction.\n */\nfunction isOldStyleEthereumjsTx(\n tx: TypedTransaction | OldEthJsTransaction,\n): tx is OldEthJsTransaction {\n return 'getChainId' in tx && typeof tx.getChainId === 'function';\n}\n\nexport class LedgerKeyring extends EventEmitter {\n static type: string = keyringType;\n\n readonly type: string = keyringType;\n\n page = 0;\n\n perPage = 5;\n\n unlockedAccount = 0;\n\n accounts: readonly string[] = [];\n\n accountDetails: Record = {};\n\n hdk = new HDKey();\n\n hdPath = hdPathString;\n\n paths: Record = {};\n\n network: NetworkApiUrls = NetworkApiUrls.Mainnet;\n\n implementFullBIP44 = false;\n\n bridge: LedgerBridge;\n\n constructor({ bridge }: { bridge: LedgerBridge }) {\n super();\n\n if (!bridge) {\n throw new Error('Bridge is a required dependency for the keyring');\n }\n\n this.bridge = bridge;\n }\n\n async init() {\n return this.bridge.init();\n }\n\n async destroy() {\n return this.bridge.destroy();\n }\n\n async serialize() {\n return {\n hdPath: this.hdPath,\n accounts: this.accounts,\n accountDetails: this.accountDetails,\n implementFullBIP44: false,\n };\n }\n\n async deserialize(opts: Partial = {}) {\n this.hdPath = opts.hdPath ?? hdPathString;\n this.accounts = opts.accounts ?? [];\n this.accountDetails = opts.accountDetails ?? {};\n\n if (!opts.accountDetails) {\n this.#migrateAccountDetails(opts);\n }\n\n this.implementFullBIP44 = opts.implementFullBIP44 ?? false;\n\n const keys = new Set(Object.keys(this.accountDetails));\n // Remove accounts that don't have corresponding account details\n this.accounts = this.accounts.filter((account) =>\n keys.has(ethUtil.toChecksumAddress(account)),\n );\n\n return Promise.resolve();\n }\n\n #migrateAccountDetails(opts: Partial) {\n if (this.#isLedgerLiveHdPath() && opts.accountIndexes) {\n for (const [account, index] of Object.entries(opts.accountIndexes)) {\n this.accountDetails[account] = {\n bip44: true,\n hdPath: this.#getPathForIndex(index),\n };\n }\n }\n const keys = new Set(Object.keys(this.accountDetails));\n // try to migrate non-LedgerLive accounts too\n if (!this.#isLedgerLiveHdPath()) {\n this.accounts.forEach((account) => {\n try {\n const key = ethUtil.toChecksumAddress(account);\n\n if (!keys.has(key)) {\n this.accountDetails[key] = {\n bip44: false,\n hdPath: this.#pathFromAddress(account),\n };\n }\n } catch (error) {\n console.log(`failed to migrate account ${account}`);\n }\n });\n }\n }\n\n isUnlocked() {\n return Boolean(this.hdk?.publicKey);\n }\n\n isConnected() {\n return this.bridge.isDeviceConnected;\n }\n\n setAccountToUnlock(index: number | string) {\n this.unlockedAccount =\n typeof index === 'number' ? index : parseInt(index, 10);\n }\n\n setHdPath(hdPath: string) {\n // Reset HDKey if the path changes\n if (this.hdPath !== hdPath) {\n this.hdk = new HDKey();\n }\n this.hdPath = hdPath;\n }\n\n async unlock(hdPath?: string, updateHdk = true): Promise {\n if (this.isUnlocked() && !hdPath) {\n return 'already unlocked';\n }\n const path = hdPath ? this.#toLedgerPath(hdPath) : this.hdPath;\n\n let payload;\n try {\n payload = await this.bridge.getPublicKey({\n hdPath: path,\n });\n } catch (error) {\n throw error instanceof Error ? error : new Error('Unknown error');\n }\n\n if (updateHdk && payload.chainCode) {\n this.hdk.publicKey = Buffer.from(payload.publicKey, 'hex');\n this.hdk.chainCode = Buffer.from(payload.chainCode, 'hex');\n }\n\n return payload.address;\n }\n\n async addAccounts(amount = 1): Promise {\n return new Promise((resolve, reject) => {\n this.unlock()\n .then(async (_) => {\n const from = this.unlockedAccount;\n const to = from + amount;\n for (let i = from; i < to; i++) {\n const path = this.#getPathForIndex(i);\n let address;\n if (this.#isLedgerLiveHdPath()) {\n address = await this.unlock(path);\n } else {\n address = this.#addressFromIndex(pathBase, i);\n }\n\n this.accountDetails[ethUtil.toChecksumAddress(address)] = {\n // TODO: consider renaming this property, as the current name is misleading\n // It's currently used to represent whether an account uses the Ledger Live path.\n bip44: this.#isLedgerLiveHdPath(),\n hdPath: path,\n };\n\n if (!this.accounts.includes(address)) {\n this.accounts = [...this.accounts, address];\n }\n this.page = 0;\n }\n resolve(this.accounts.slice());\n })\n .catch(reject);\n });\n }\n\n async getFirstPage() {\n this.page = 0;\n return this.#getPage(1);\n }\n\n async getNextPage() {\n return this.#getPage(1);\n }\n\n async getPreviousPage() {\n return this.#getPage(-1);\n }\n\n async getAccounts() {\n return Promise.resolve(this.accounts.slice());\n }\n\n removeAccount(address: string) {\n const filteredAccounts = this.accounts.filter(\n (a) => a.toLowerCase() !== address.toLowerCase(),\n );\n\n if (filteredAccounts.length === this.accounts.length) {\n throw new Error(`Address ${address} not found in this keyring`);\n }\n\n this.accounts = filteredAccounts;\n delete this.accountDetails[ethUtil.toChecksumAddress(address)];\n }\n\n async attemptMakeApp() {\n return this.bridge.attemptMakeApp();\n }\n\n async updateTransportMethod(transportType: string) {\n return this.bridge.updateTransportMethod(transportType);\n }\n\n // tx is an instance of the ethereumjs-transaction class.\n async signTransaction(\n address: string,\n tx: TypedTransaction | OldEthJsTransaction,\n ): Promise {\n let rawTxHex;\n // transactions built with older versions of ethereumjs-tx have a\n // getChainId method that newer versions do not. Older versions are mutable\n // while newer versions default to being immutable. Expected shape and type\n // of data for v, r and s differ (Buffer (old) vs BN (new))\n if (isOldStyleEthereumjsTx(tx)) {\n // In this version of ethereumjs-tx we must add the chainId in hex format\n // to the initial v value. The chainId must be included in the serialized\n // transaction which is only communicated to ethereumjs-tx in this\n // value. In newer versions the chainId is communicated via the 'Common'\n // object.\n // @ts-expect-error tx.v should be a Buffer but we are assigning a string\n tx.v = ethUtil.bufferToHex(tx.getChainId());\n // @ts-expect-error tx.r should be a Buffer but we are assigning a string\n tx.r = '0x00';\n // @ts-expect-error tx.s should be a Buffer but we are assigning a string\n tx.s = '0x00';\n\n rawTxHex = tx.serialize().toString('hex');\n\n return this.#signTransaction(address, rawTxHex, (payload) => {\n tx.v = Buffer.from(payload.v, 'hex');\n tx.r = Buffer.from(payload.r, 'hex');\n tx.s = Buffer.from(payload.s, 'hex');\n return tx;\n });\n }\n\n // The below `encode` call is only necessary for legacy transactions, as `getMessageToSign`\n // calls `rlp.encode` internally for non-legacy transactions. As per the \"Transaction Execution\"\n // section of the ethereum yellow paper, transactions need to be \"well-formed RLP, with no additional\n // trailing bytes\".\n\n // Note also that `getMessageToSign` will return valid RLP for all transaction types, whereas the\n // `serialize` method will not for any transaction type except legacy. This is because `serialize` includes\n // empty r, s and v values in the encoded rlp. This is why we use `getMessageToSign` here instead of `serialize`.\n const messageToSign = tx.getMessageToSign(false);\n\n rawTxHex = Buffer.isBuffer(messageToSign)\n ? messageToSign.toString('hex')\n : Buffer.from(RLP.encode(messageToSign)).toString('hex');\n\n return this.#signTransaction(address, rawTxHex, (payload) => {\n // Because tx will be immutable, first get a plain javascript object that\n // represents the transaction. Using txData here as it aligns with the\n // nomenclature of ethereumjs/tx.\n const txData: TxData = tx.toJSON();\n // The fromTxData utility expects a type to support transactions with a type other than 0\n txData.type = tx.type;\n // The fromTxData utility expects v,r and s to be hex prefixed\n txData.v = ethUtil.addHexPrefix(payload.v);\n txData.r = ethUtil.addHexPrefix(payload.r);\n txData.s = ethUtil.addHexPrefix(payload.s);\n // Adopt the 'common' option from the original transaction and set the\n // returned object to be frozen if the original is frozen.\n return TransactionFactory.fromTxData(txData, {\n common: tx.common,\n freeze: Object.isFrozen(tx),\n });\n });\n }\n\n async #signTransaction(\n address: string,\n rawTxHex: string,\n handleSigning: (\n payload: SignTransactionPayload,\n ) => TypedTransaction | OldEthJsTransaction,\n ): Promise {\n const hdPath = await this.unlockAccountByAddress(address);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing transaction');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignTransaction({\n tx: rawTxHex,\n hdPath,\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing transaction');\n }\n\n const newOrMutatedTx = handleSigning(payload);\n const valid = newOrMutatedTx.verifySignature();\n if (valid) {\n return newOrMutatedTx;\n }\n throw new Error('Ledger: The transaction signature is not valid');\n }\n\n async signMessage(withAccount: string, data: string) {\n return this.signPersonalMessage(withAccount, data);\n }\n\n // For personal_sign, we need to prefix the message:\n async signPersonalMessage(withAccount: string, message: string) {\n const hdPath = await this.unlockAccountByAddress(withAccount);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing message');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignMessage({\n hdPath,\n message: ethUtil.stripHexPrefix(message),\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing message');\n }\n\n let recoveryId = parseInt(String(payload.v), 10).toString(16);\n if (recoveryId.length < 2) {\n recoveryId = `0${recoveryId}`;\n }\n const signature = `0x${payload.r}${payload.s}${recoveryId}`;\n const addressSignedWith = recoverPersonalSignature({\n data: message,\n signature,\n });\n if (\n ethUtil.toChecksumAddress(addressSignedWith) !==\n ethUtil.toChecksumAddress(withAccount)\n ) {\n throw new Error('Ledger: The signature doesnt match the right address');\n }\n return signature;\n }\n\n async unlockAccountByAddress(address: string) {\n const checksummedAddress = ethUtil.toChecksumAddress(address);\n const accountDetails = this.accountDetails[checksummedAddress];\n if (!accountDetails) {\n throw new Error(\n `Ledger: Account for address '${checksummedAddress}' not found`,\n );\n }\n const { hdPath } = accountDetails;\n const unlockedAddress = await this.unlock(hdPath, false);\n\n // unlock resolves to the address for the given hdPath as reported by the ledger device\n // if that address is not the requested address, then this account belongs to a different device or seed\n if (unlockedAddress.toLowerCase() !== address.toLowerCase()) {\n throw new Error(\n `Ledger: Account ${address} does not belong to the connected device`,\n );\n }\n return hdPath;\n }\n\n async signTypedData(\n withAccount: string,\n data: TypedMessage,\n options: { version?: string } = {},\n ) {\n const isV4 = options.version === 'V4';\n if (!isV4) {\n throw new Error(\n 'Ledger: Only version 4 of typed data signing is supported',\n );\n }\n\n const { domain, types, primaryType, message } =\n TypedDataUtils.sanitizeData(data);\n const domainSeparatorHex = TypedDataUtils.hashStruct(\n 'EIP712Domain',\n domain,\n types,\n SignTypedDataVersion.V4,\n ).toString('hex');\n const hashStructMessageHex = TypedDataUtils.hashStruct(\n primaryType.toString(),\n message,\n types,\n SignTypedDataVersion.V4,\n ).toString('hex');\n\n const hdPath = await this.unlockAccountByAddress(withAccount);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing message');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignTypedData({\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing message');\n }\n\n let recoveryId = parseInt(String(payload.v), 10).toString(16);\n if (recoveryId.length < 2) {\n recoveryId = `0${recoveryId}`;\n }\n const signature = `0x${payload.r}${payload.s}${recoveryId}`;\n const addressSignedWith = recoverTypedSignature({\n data,\n signature,\n version: SignTypedDataVersion.V4,\n });\n if (\n ethUtil.toChecksumAddress(addressSignedWith) !==\n ethUtil.toChecksumAddress(withAccount)\n ) {\n throw new Error('Ledger: The signature doesnt match the right address');\n }\n return signature;\n }\n\n exportAccount() {\n throw new Error('Not supported on this device');\n }\n\n forgetDevice() {\n this.accounts = [];\n this.page = 0;\n this.unlockedAccount = 0;\n this.paths = {};\n this.accountDetails = {};\n this.hdk = new HDKey();\n }\n\n /* PRIVATE METHODS */\n async #getPage(increment: number) {\n this.page += increment;\n\n if (this.page <= 0) {\n this.page = 1;\n }\n const from = (this.page - 1) * this.perPage;\n const to = from + this.perPage;\n\n await this.unlock();\n let accounts;\n if (this.#isLedgerLiveHdPath()) {\n accounts = await this.#getAccountsBIP44(from, to);\n } else {\n accounts = this.#getAccountsLegacy(from, to);\n }\n return accounts;\n }\n\n async #getAccountsBIP44(from: number, to: number) {\n const accounts: {\n address: string;\n balance: number | null;\n index: number;\n }[] = [];\n\n for (let i = from; i < to; i++) {\n const path = this.#getPathForIndex(i);\n const address = await this.unlock(path);\n const valid = this.implementFullBIP44\n ? await this.#hasPreviousTransactions(address)\n : true;\n accounts.push({\n address,\n balance: null,\n index: i,\n });\n\n // PER BIP44\n // \"Software should prevent a creation of an account if\n // a previous account does not have a transaction history\n // (meaning none of its addresses have been used before).\"\n if (!valid) {\n break;\n }\n }\n return accounts;\n }\n\n #getAccountsLegacy(from: number, to: number) {\n const accounts: {\n address: string;\n balance: number | null;\n index: number;\n }[] = [];\n\n for (let i = from; i < to; i++) {\n const address = this.#addressFromIndex(pathBase, i);\n accounts.push({\n address,\n balance: null,\n index: i,\n });\n this.paths[ethUtil.toChecksumAddress(address)] = i;\n }\n return accounts;\n }\n\n #addressFromIndex(basePath: string, i: number) {\n const dkey = this.hdk.derive(`${basePath}/${i}`);\n const address = ethUtil\n .publicToAddress(dkey.publicKey, true)\n .toString('hex');\n return ethUtil.toChecksumAddress(`0x${address}`);\n }\n\n #pathFromAddress(address: string) {\n const checksummedAddress = ethUtil.toChecksumAddress(address);\n let index = this.paths[checksummedAddress];\n if (typeof index === 'undefined') {\n for (let i = 0; i < MAX_INDEX; i++) {\n if (checksummedAddress === this.#addressFromIndex(pathBase, i)) {\n index = i;\n break;\n }\n }\n }\n\n if (typeof index === 'undefined') {\n throw new Error('Unknown address');\n }\n return this.#getPathForIndex(index);\n }\n\n #getPathForIndex(index: number) {\n // Check if the path is BIP 44 (Ledger Live)\n return this.#isLedgerLiveHdPath()\n ? `m/44'/60'/${index}'/0/0`\n : `${this.hdPath}/${index}`;\n }\n\n #isLedgerLiveHdPath() {\n return this.hdPath === `m/44'/60'/0'/0/0`;\n }\n\n #toLedgerPath(path: string) {\n return path.toString().replace('m/', '');\n }\n\n async #hasPreviousTransactions(address: string) {\n const apiUrl = this.#getApiUrl();\n const response = await window.fetch(\n `${apiUrl}/api?module=account&action=txlist&address=${address}&tag=latest&page=1&offset=1`,\n );\n const parsedResponse = await response.json();\n if (parsedResponse.status !== '0' && parsedResponse.result.length > 0) {\n return true;\n }\n return false;\n }\n\n #getApiUrl() {\n return this.network;\n }\n}\n"]} +\ No newline at end of file ++{"version":3,"file":"ledger-keyring.js","sourceRoot":"","sources":["../src/ledger-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { RLP } from '@ethereumjs/rlp';\nimport { TransactionFactory, TxData, TypedTransaction } from '@ethereumjs/tx';\nimport * as ethUtil from '@ethereumjs/util';\nimport type { MessageTypes, TypedMessage } from '@metamask/eth-sig-util';\nimport {\n recoverPersonalSignature,\n recoverTypedSignature,\n SignTypedDataVersion,\n TypedDataUtils,\n} from '@metamask/eth-sig-util';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { Buffer } from 'buffer';\nimport type OldEthJsTransaction from 'ethereumjs-tx';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { EventEmitter } from 'events';\nimport HDKey from 'hdkey';\n\nimport { LedgerBridge, LedgerBridgeOptions } from './ledger-bridge';\n\nconst pathBase = 'm';\nconst hdPathString = `${pathBase}/44'/60'/0'`;\nconst keyringType = 'Ledger Hardware';\n\nconst MAX_INDEX = 1000;\n\nenum NetworkApiUrls {\n Ropsten = 'http://api-ropsten.etherscan.io',\n Kovan = 'http://api-kovan.etherscan.io',\n Rinkeby = 'https://api-rinkeby.etherscan.io',\n Mainnet = 'https://api.etherscan.io',\n}\n\ntype SignTransactionPayload = Awaited<\n ReturnType['deviceSignTransaction']>\n>;\n\nexport type AccountDetails = {\n index?: number;\n bip44?: boolean;\n hdPath?: string;\n};\n\nexport type LedgerBridgeKeyringOptions = {\n hdPath: string;\n accounts: readonly string[];\n accountDetails: Readonly>;\n accountIndexes: Readonly>;\n implementFullBIP44: boolean;\n};\n\n/**\n * Check if the given transaction is made with ethereumjs-tx or @ethereumjs/tx\n *\n * Transactions built with older versions of ethereumjs-tx have a\n * getChainId method that newer versions do not.\n * Older versions are mutable\n * while newer versions default to being immutable.\n * Expected shape and type\n * of data for v, r and s differ (Buffer (old) vs BN (new)).\n *\n * @param tx - Transaction to check, instance of either ethereumjs-tx or @ethereumjs/tx.\n * @returns Returns `true` if tx is an old-style ethereumjs-tx transaction.\n */\nfunction isOldStyleEthereumjsTx(\n tx: TypedTransaction | OldEthJsTransaction,\n): tx is OldEthJsTransaction {\n return 'getChainId' in tx && typeof tx.getChainId === 'function';\n}\n\nexport class LedgerKeyring extends EventEmitter {\n static type: string = keyringType;\n\n readonly type: string = keyringType;\n\n page = 0;\n\n perPage = 5;\n\n unlockedAccount = 0;\n\n accounts: readonly string[] = [];\n\n accountDetails: Record = {};\n\n hdk = new HDKey();\n\n hdPath = hdPathString;\n\n paths: Record = {};\n\n network: NetworkApiUrls = NetworkApiUrls.Mainnet;\n\n implementFullBIP44 = false;\n\n bridge: LedgerBridge;\n\n constructor({ bridge }: { bridge: LedgerBridge }) {\n super();\n\n if (!bridge) {\n throw new Error('Bridge is a required dependency for the keyring');\n }\n\n this.bridge = bridge;\n }\n\n async init() {\n return this.bridge.init();\n }\n\n async destroy() {\n return this.bridge.destroy();\n }\n\n async serialize() {\n return {\n hdPath: this.hdPath,\n accounts: this.accounts,\n accountDetails: this.accountDetails,\n implementFullBIP44: false,\n };\n }\n\n async deserialize(opts: Partial = {}) {\n this.hdPath = opts.hdPath ?? hdPathString;\n this.accounts = opts.accounts ?? [];\n this.accountDetails = opts.accountDetails ?? {};\n\n if (!opts.accountDetails) {\n this.#migrateAccountDetails(opts);\n }\n\n this.implementFullBIP44 = opts.implementFullBIP44 ?? false;\n\n const keys = new Set(Object.keys(this.accountDetails));\n // Remove accounts that don't have corresponding account details\n this.accounts = this.accounts.filter((account) =>\n keys.has(ethUtil.toChecksumAddress(account)),\n );\n\n return Promise.resolve();\n }\n\n #migrateAccountDetails(opts: Partial) {\n if (this.#isLedgerLiveHdPath() && opts.accountIndexes) {\n for (const [account, index] of Object.entries(opts.accountIndexes)) {\n this.accountDetails[account] = {\n bip44: true,\n hdPath: this.#getPathForIndex(index),\n };\n }\n }\n const keys = new Set(Object.keys(this.accountDetails));\n // try to migrate non-LedgerLive accounts too\n if (!this.#isLedgerLiveHdPath()) {\n this.accounts.forEach((account) => {\n try {\n const key = ethUtil.toChecksumAddress(account);\n\n if (!keys.has(key)) {\n this.accountDetails[key] = {\n bip44: false,\n hdPath: this.#pathFromAddress(account),\n };\n }\n } catch (error) {\n console.log(`failed to migrate account ${account}`);\n }\n });\n }\n }\n\n isUnlocked() {\n return Boolean(this.hdk?.publicKey);\n }\n\n isConnected() {\n return this.bridge.isDeviceConnected;\n }\n\n setAccountToUnlock(index: number | string) {\n this.unlockedAccount =\n typeof index === 'number' ? index : parseInt(index, 10);\n }\n\n setHdPath(hdPath: string) {\n // Reset HDKey if the path changes\n if (this.hdPath !== hdPath) {\n this.hdk = new HDKey();\n }\n this.hdPath = hdPath;\n }\n\n async unlock(hdPath?: string, updateHdk = true): Promise {\n if (this.isUnlocked() && !hdPath) {\n return 'already unlocked';\n }\n const path = hdPath ? this.#toLedgerPath(hdPath) : this.hdPath;\n\n let payload;\n try {\n payload = await this.bridge.getPublicKey({\n hdPath: path,\n });\n } catch (error) {\n throw error instanceof Error ? error : new Error('Unknown error');\n }\n\n if (updateHdk && payload.chainCode) {\n this.hdk.publicKey = Buffer.from(payload.publicKey, 'hex');\n this.hdk.chainCode = Buffer.from(payload.chainCode, 'hex');\n }\n\n return payload.address;\n }\n\n async addAccounts(amount = 1): Promise {\n return new Promise((resolve, reject) => {\n this.unlock()\n .then(async (_) => {\n const from = this.unlockedAccount;\n const to = from + amount;\n for (let i = from; i < to; i++) {\n const path = this.#getPathForIndex(i);\n let address;\n if (this.#isLedgerLiveHdPath()) {\n address = await this.unlock(path);\n } else {\n address = this.#addressFromIndex(pathBase, i);\n }\n\n this.accountDetails[ethUtil.toChecksumAddress(address)] = {\n // TODO: consider renaming this property, as the current name is misleading\n // It's currently used to represent whether an account uses the Ledger Live path.\n bip44: this.#isLedgerLiveHdPath(),\n hdPath: path,\n };\n\n if (!this.accounts.includes(address)) {\n this.accounts = [...this.accounts, address];\n }\n this.page = 0;\n }\n resolve(this.accounts.slice());\n })\n .catch(reject);\n });\n }\n\n async getFirstPage() {\n this.page = 0;\n return this.#getPage(1);\n }\n\n async getNextPage() {\n return this.#getPage(1);\n }\n\n async getPreviousPage() {\n return this.#getPage(-1);\n }\n\n async getAccounts() {\n return Promise.resolve(this.accounts.slice());\n }\n\n removeAccount(address: string) {\n const filteredAccounts = this.accounts.filter(\n (a) => a.toLowerCase() !== address.toLowerCase(),\n );\n\n if (filteredAccounts.length === this.accounts.length) {\n throw new Error(`Address ${address} not found in this keyring`);\n }\n\n this.accounts = filteredAccounts;\n delete this.accountDetails[ethUtil.toChecksumAddress(address)];\n }\n\n async attemptMakeApp() {\n return this.bridge.attemptMakeApp();\n }\n\n async updateTransportMethod(transportType: string) {\n return this.bridge.updateTransportMethod(transportType);\n }\n\n // tx is an instance of the ethereumjs-transaction class.\n async signTransaction(\n address: string,\n tx: TypedTransaction | OldEthJsTransaction,\n ): Promise {\n let rawTxHex;\n // transactions built with older versions of ethereumjs-tx have a\n // getChainId method that newer versions do not. Older versions are mutable\n // while newer versions default to being immutable. Expected shape and type\n // of data for v, r and s differ (Buffer (old) vs BN (new))\n if (isOldStyleEthereumjsTx(tx)) {\n // In this version of ethereumjs-tx we must add the chainId in hex format\n // to the initial v value. The chainId must be included in the serialized\n // transaction which is only communicated to ethereumjs-tx in this\n // value. In newer versions the chainId is communicated via the 'Common'\n // object.\n // @ts-expect-error tx.v should be a Buffer but we are assigning a string\n tx.v = ethUtil.bufferToHex(tx.getChainId());\n // @ts-expect-error tx.r should be a Buffer but we are assigning a string\n tx.r = '0x00';\n // @ts-expect-error tx.s should be a Buffer but we are assigning a string\n tx.s = '0x00';\n\n rawTxHex = tx.serialize().toString('hex');\n\n return this.#signTransaction(address, rawTxHex, (payload) => {\n tx.v = Buffer.from(payload.v, 'hex');\n tx.r = Buffer.from(payload.r, 'hex');\n tx.s = Buffer.from(payload.s, 'hex');\n return tx;\n });\n }\n\n // The below `encode` call is only necessary for legacy transactions, as `getMessageToSign`\n // calls `rlp.encode` internally for non-legacy transactions. As per the \"Transaction Execution\"\n // section of the ethereum yellow paper, transactions need to be \"well-formed RLP, with no additional\n // trailing bytes\".\n\n // Note also that `getMessageToSign` will return valid RLP for all transaction types, whereas the\n // `serialize` method will not for any transaction type except legacy. This is because `serialize` includes\n // empty r, s and v values in the encoded rlp. This is why we use `getMessageToSign` here instead of `serialize`.\n const messageToSign = tx.getMessageToSign(false);\n\n rawTxHex = Buffer.isBuffer(messageToSign)\n ? messageToSign.toString('hex')\n : Buffer.from(RLP.encode(messageToSign)).toString('hex');\n\n return this.#signTransaction(address, rawTxHex, (payload) => {\n // Because tx will be immutable, first get a plain javascript object that\n // represents the transaction. Using txData here as it aligns with the\n // nomenclature of ethereumjs/tx.\n const txData: TxData = tx.toJSON();\n // The fromTxData utility expects a type to support transactions with a type other than 0\n txData.type = tx.type;\n // The fromTxData utility expects v,r and s to be hex prefixed\n txData.v = ethUtil.addHexPrefix(payload.v);\n txData.r = ethUtil.addHexPrefix(payload.r);\n txData.s = ethUtil.addHexPrefix(payload.s);\n // Adopt the 'common' option from the original transaction and set the\n // returned object to be frozen if the original is frozen.\n return TransactionFactory.fromTxData(txData, {\n common: tx.common,\n freeze: Object.isFrozen(tx),\n });\n });\n }\n\n async #signTransaction(\n address: string,\n rawTxHex: string,\n handleSigning: (\n payload: SignTransactionPayload,\n ) => TypedTransaction | OldEthJsTransaction,\n ): Promise {\n const hdPath = await this.unlockAccountByAddress(address);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing transaction');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignTransaction({\n tx: rawTxHex,\n hdPath,\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing transaction');\n }\n\n const newOrMutatedTx = handleSigning(payload);\n const valid = newOrMutatedTx.verifySignature();\n if (valid) {\n return newOrMutatedTx;\n }\n throw new Error('Ledger: The transaction signature is not valid');\n }\n\n async signMessage(withAccount: string, data: string) {\n return this.signPersonalMessage(withAccount, data);\n }\n\n // For personal_sign, we need to prefix the message:\n async signPersonalMessage(withAccount: string, message: string) {\n const hdPath = await this.unlockAccountByAddress(withAccount);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing message');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignMessage({\n hdPath,\n message: ethUtil.stripHexPrefix(message),\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing message');\n }\n\n let recoveryId = parseInt(String(payload.v), 10).toString(16);\n if (recoveryId.length < 2) {\n recoveryId = `0${recoveryId}`;\n }\n\n let modifiedV = parseInt(String(payload.v), 10).toString(16);\n if (modifiedV.length < 2) {\n modifiedV = `0${modifiedV}`;\n }\n\n const signature = `0x${payload.r}${payload.s}${modifiedV}`;\n const addressSignedWith = recoverPersonalSignature({\n data: message,\n signature,\n });\n if (\n ethUtil.toChecksumAddress(addressSignedWith) !==\n ethUtil.toChecksumAddress(withAccount)\n ) {\n throw new Error('Ledger: The signature doesnt match the right address');\n }\n return signature;\n }\n\n async unlockAccountByAddress(address: string) {\n const checksummedAddress = ethUtil.toChecksumAddress(address);\n const accountDetails = this.accountDetails[checksummedAddress];\n if (!accountDetails) {\n throw new Error(\n `Ledger: Account for address '${checksummedAddress}' not found`,\n );\n }\n const { hdPath } = accountDetails;\n const unlockedAddress = await this.unlock(hdPath, false);\n\n // unlock resolves to the address for the given hdPath as reported by the ledger device\n // if that address is not the requested address, then this account belongs to a different device or seed\n if (unlockedAddress.toLowerCase() !== address.toLowerCase()) {\n throw new Error(\n `Ledger: Account ${address} does not belong to the connected device`,\n );\n }\n return hdPath;\n }\n\n async signTypedData(\n withAccount: string,\n data: TypedMessage,\n options: { version?: string } = {},\n ) {\n const isV4 = options.version === 'V4';\n if (!isV4) {\n throw new Error(\n 'Ledger: Only version 4 of typed data signing is supported',\n );\n }\n\n const { domain, types, primaryType, message } =\n TypedDataUtils.sanitizeData(data);\n const domainSeparatorHex = TypedDataUtils.hashStruct(\n 'EIP712Domain',\n domain,\n types,\n SignTypedDataVersion.V4,\n ).toString('hex');\n const hashStructMessageHex = TypedDataUtils.hashStruct(\n primaryType.toString(),\n message,\n types,\n SignTypedDataVersion.V4,\n ).toString('hex');\n\n const hdPath = await this.unlockAccountByAddress(withAccount);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing message');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignTypedData({\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing message');\n }\n\n let recoveryId = parseInt(String(payload.v), 10).toString(16);\n if (recoveryId.length < 2) {\n recoveryId = `0${recoveryId}`;\n }\n const signature = `0x${payload.r}${payload.s}${recoveryId}`;\n const addressSignedWith = recoverTypedSignature({\n data,\n signature,\n version: SignTypedDataVersion.V4,\n });\n if (\n ethUtil.toChecksumAddress(addressSignedWith) !==\n ethUtil.toChecksumAddress(withAccount)\n ) {\n throw new Error('Ledger: The signature doesnt match the right address');\n }\n return signature;\n }\n\n exportAccount() {\n throw new Error('Not supported on this device');\n }\n\n forgetDevice() {\n this.accounts = [];\n this.page = 0;\n this.unlockedAccount = 0;\n this.paths = {};\n this.accountDetails = {};\n this.hdk = new HDKey();\n }\n\n /* PRIVATE METHODS */\n async #getPage(increment: number) {\n this.page += increment;\n\n if (this.page <= 0) {\n this.page = 1;\n }\n const from = (this.page - 1) * this.perPage;\n const to = from + this.perPage;\n\n await this.unlock();\n let accounts;\n if (this.#isLedgerLiveHdPath()) {\n accounts = await this.#getAccountsBIP44(from, to);\n } else {\n accounts = this.#getAccountsLegacy(from, to);\n }\n return accounts;\n }\n\n async #getAccountsBIP44(from: number, to: number) {\n const accounts: {\n address: string;\n balance: number | null;\n index: number;\n }[] = [];\n\n for (let i = from; i < to; i++) {\n const path = this.#getPathForIndex(i);\n const address = await this.unlock(path);\n const valid = this.implementFullBIP44\n ? await this.#hasPreviousTransactions(address)\n : true;\n accounts.push({\n address,\n balance: null,\n index: i,\n });\n\n // PER BIP44\n // \"Software should prevent a creation of an account if\n // a previous account does not have a transaction history\n // (meaning none of its addresses have been used before).\"\n if (!valid) {\n break;\n }\n }\n return accounts;\n }\n\n #getAccountsLegacy(from: number, to: number) {\n const accounts: {\n address: string;\n balance: number | null;\n index: number;\n }[] = [];\n\n for (let i = from; i < to; i++) {\n const address = this.#addressFromIndex(pathBase, i);\n accounts.push({\n address,\n balance: null,\n index: i,\n });\n this.paths[ethUtil.toChecksumAddress(address)] = i;\n }\n return accounts;\n }\n\n #addressFromIndex(basePath: string, i: number) {\n const dkey = this.hdk.derive(`${basePath}/${i}`);\n const address = ethUtil\n .publicToAddress(dkey.publicKey, true)\n .toString('hex');\n return ethUtil.toChecksumAddress(`0x${address}`);\n }\n\n #pathFromAddress(address: string) {\n const checksummedAddress = ethUtil.toChecksumAddress(address);\n let index = this.paths[checksummedAddress];\n if (typeof index === 'undefined') {\n for (let i = 0; i < MAX_INDEX; i++) {\n if (checksummedAddress === this.#addressFromIndex(pathBase, i)) {\n index = i;\n break;\n }\n }\n }\n\n if (typeof index === 'undefined') {\n throw new Error('Unknown address');\n }\n return this.#getPathForIndex(index);\n }\n\n #getPathForIndex(index: number) {\n // Check if the path is BIP 44 (Ledger Live)\n return this.#isLedgerLiveHdPath()\n ? `m/44'/60'/${index}'/0/0`\n : `${this.hdPath}/${index}`;\n }\n\n #isLedgerLiveHdPath() {\n return this.hdPath === `m/44'/60'/0'/0/0`;\n }\n\n #toLedgerPath(path: string) {\n return path.toString().replace('m/', '');\n }\n\n async #hasPreviousTransactions(address: string) {\n const apiUrl = this.#getApiUrl();\n const response = await window.fetch(\n `${apiUrl}/api?module=account&action=txlist&address=${address}&tag=latest&page=1&offset=1`,\n );\n const parsedResponse = await response.json();\n if (parsedResponse.status !== '0' && parsedResponse.result.length > 0) {\n return true;\n }\n return false;\n }\n\n #getApiUrl() {\n return this.network;\n }\n}\n"]} +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts +new file mode 100644 +index 0000000..b0e325c +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts +@@ -0,0 +1,89 @@ ++import type Transport from '@ledgerhq/hw-transport'; ++import { GetPublicKeyParams, GetPublicKeyResponse, LedgerBridge, LedgerSignMessageParams, LedgerSignMessageResponse, LedgerSignTransactionParams, LedgerSignTransactionResponse, LedgerSignTypedDataParams, LedgerSignTypedDataResponse } from './ledger-bridge'; ++import { GetAppNameAndVersionResponse, LedgerMobileBridgeOptions, TransportMiddleware } from './ledger-mobile-bridge/'; ++export interface LedgerMobileBridge { ++ getAppNameAndVersion(): Promise; ++ openEthApp(): Promise; ++ closeApps(): Promise; ++} ++/** ++ * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware. ++ */ ++export declare class LedgerMobileBridge implements LedgerBridge, LedgerMobileBridge { ++ #private; ++ isDeviceConnected: boolean; ++ constructor(transportMiddleware: TransportMiddleware, opts?: LedgerMobileBridgeOptions); ++ /** ++ * Method to initializes the keyring. ++ * Mobile ledger doesnt not require init. ++ */ ++ init(): Promise; ++ /** ++ * Method to destroy the keyring. ++ * It will dispose the transportmiddleware and set isDeviceConnected to false. ++ */ ++ destroy(): Promise; ++ /** ++ * Method to sign a string Message. ++ * Sending the string message to the device and returning the signed message. ++ * ++ * @param params - The descriptor to open the transport with. ++ * @param params.hdPath - The descriptor to open the transport with. ++ * @param params.message - An optional timeout for the transport connection. ++ * @returns Retrieve v, r, s from the signed message. ++ */ ++ deviceSignMessage({ hdPath, message, }: LedgerSignMessageParams): Promise; ++ /** ++ * Method to sign a EIP712 Message. ++ * Sending the typed data message to the device and returning the signed message. ++ * ++ * @param params - An object contains hdPath, domainSeparatorHex and hashStructMessageHex. ++ * @param params.hdPath - The BIP 32 path of the account. ++ * @param params.domainSeparatorHex - The domain separator. ++ * @param params.hashStructMessageHex - The hashed struct message. ++ * @returns Retrieve v, r, s from the signed message. ++ */ ++ deviceSignTypedData({ hdPath, domainSeparatorHex, hashStructMessageHex, }: LedgerSignTypedDataParams): Promise; ++ /** ++ * Method to sign a transaction ++ * Sending the hexadecimal transaction message to the device and returning the signed transaction. ++ * ++ * @param params - An object contains tx, hdPath. ++ * @param params.tx - The raw ethereum transaction in hexadecimal to sign. ++ * @param params.hdPath - The BIP 32 path of the account. ++ * @returns Retrieve v, r, s from the signed transaction. ++ */ ++ deviceSignTransaction({ tx, hdPath, }: LedgerSignTransactionParams): Promise; ++ /** ++ * Method to retrieve the ethereum address for a given BIP 32 path. ++ * ++ * @param params - An object contains hdPath. ++ * @param params.hdPath - The BIP 32 path of the account. ++ * @returns An object contains publicKey, address and chainCode. ++ */ ++ getPublicKey({ hdPath, }: GetPublicKeyParams): Promise; ++ /** ++ * Method to retrieve the current configuration. ++ * ++ * @returns Retrieve current configuration. ++ */ ++ getOptions(): Promise; ++ /** ++ * Method to set the current configuration. ++ * ++ * @param opts - An configuration object. ++ */ ++ setOptions(opts: LedgerMobileBridgeOptions): Promise; ++ /** ++ * Method to set the transport object to communicate with the device. ++ * ++ * @param transport - The communication interface with the Ledger hardware wallet. There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb). ++ * @returns Retrieve boolean. ++ */ ++ updateTransportMethod(transport: Transport): Promise; ++ /** ++ * Method to init eth app object on ledger device. ++ * This method is not supported on mobile. ++ */ ++ attemptMakeApp(): Promise; ++} +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js +new file mode 100644 +index 0000000..3fe54b8 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js +@@ -0,0 +1,171 @@ ++"use strict"; ++var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { ++ if (kind === "m") throw new TypeError("Private method is not writable"); ++ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); ++ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); ++ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; ++}; ++var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { ++ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); ++ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); ++ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); ++}; ++var __importDefault = (this && this.__importDefault) || function (mod) { ++ return (mod && mod.__esModule) ? mod : { "default": mod }; ++}; ++var _LedgerMobileBridge_instances, _LedgerMobileBridge_transportMiddleware, _LedgerMobileBridge_opts, _LedgerMobileBridge_getTransportMiddleWare, _LedgerMobileBridge_getEthApp; ++Object.defineProperty(exports, "__esModule", { value: true }); ++exports.LedgerMobileBridge = void 0; ++const ledger_1 = __importDefault(require("@ledgerhq/hw-app-eth/lib/services/ledger")); ++/** ++ * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware. ++ */ ++class LedgerMobileBridge { ++ constructor(transportMiddleware, opts = {}) { ++ _LedgerMobileBridge_instances.add(this); ++ _LedgerMobileBridge_transportMiddleware.set(this, void 0); ++ _LedgerMobileBridge_opts.set(this, void 0); ++ this.isDeviceConnected = false; ++ __classPrivateFieldSet(this, _LedgerMobileBridge_opts, opts, "f"); ++ __classPrivateFieldSet(this, _LedgerMobileBridge_transportMiddleware, transportMiddleware, "f"); ++ } ++ /** ++ * Method to initializes the keyring. ++ * Mobile ledger doesnt not require init. ++ */ ++ async init() { ++ return Promise.resolve(); ++ } ++ /** ++ * Method to destroy the keyring. ++ * It will dispose the transportmiddleware and set isDeviceConnected to false. ++ */ ++ async destroy() { ++ try { ++ await __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getTransportMiddleWare).call(this).dispose(); ++ } ++ catch (error) { ++ // eslint-disable-next-line no-console ++ console.error(error); ++ } ++ this.isDeviceConnected = false; ++ } ++ /** ++ * Method to sign a string Message. ++ * Sending the string message to the device and returning the signed message. ++ * ++ * @param params - The descriptor to open the transport with. ++ * @param params.hdPath - The descriptor to open the transport with. ++ * @param params.message - An optional timeout for the transport connection. ++ * @returns Retrieve v, r, s from the signed message. ++ */ ++ async deviceSignMessage({ hdPath, message, }) { ++ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).signPersonalMessage(hdPath, message); ++ } ++ /** ++ * Method to sign a EIP712 Message. ++ * Sending the typed data message to the device and returning the signed message. ++ * ++ * @param params - An object contains hdPath, domainSeparatorHex and hashStructMessageHex. ++ * @param params.hdPath - The BIP 32 path of the account. ++ * @param params.domainSeparatorHex - The domain separator. ++ * @param params.hashStructMessageHex - The hashed struct message. ++ * @returns Retrieve v, r, s from the signed message. ++ */ ++ async deviceSignTypedData({ hdPath, domainSeparatorHex, hashStructMessageHex, }) { ++ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).signEIP712HashedMessage(hdPath, domainSeparatorHex, hashStructMessageHex); ++ } ++ /** ++ * Method to sign a transaction ++ * Sending the hexadecimal transaction message to the device and returning the signed transaction. ++ * ++ * @param params - An object contains tx, hdPath. ++ * @param params.tx - The raw ethereum transaction in hexadecimal to sign. ++ * @param params.hdPath - The BIP 32 path of the account. ++ * @returns Retrieve v, r, s from the signed transaction. ++ */ ++ async deviceSignTransaction({ tx, hdPath, }) { ++ const resolution = await ledger_1.default.resolveTransaction(tx, {}, {}); ++ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).signTransaction(hdPath, tx, resolution); ++ } ++ /** ++ * Method to retrieve the ethereum address for a given BIP 32 path. ++ * ++ * @param params - An object contains hdPath. ++ * @param params.hdPath - The BIP 32 path of the account. ++ * @returns An object contains publicKey, address and chainCode. ++ */ ++ async getPublicKey({ hdPath, }) { ++ return await __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).getAddress(hdPath, false, true); ++ } ++ /** ++ * Method to retrieve the current configuration. ++ * ++ * @returns Retrieve current configuration. ++ */ ++ async getOptions() { ++ return __classPrivateFieldGet(this, _LedgerMobileBridge_opts, "f"); ++ } ++ /** ++ * Method to set the current configuration. ++ * ++ * @param opts - An configuration object. ++ */ ++ async setOptions(opts) { ++ __classPrivateFieldSet(this, _LedgerMobileBridge_opts, opts, "f"); ++ } ++ /** ++ * Method to set the transport object to communicate with the device. ++ * ++ * @param transport - The communication interface with the Ledger hardware wallet. There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb). ++ * @returns Retrieve boolean. ++ */ ++ async updateTransportMethod(transport) { ++ var _a; ++ if (!((_a = transport.deviceModel) === null || _a === void 0 ? void 0 : _a.id)) { ++ throw new Error('Property `deviceModel.id` is not defined in `transport`.'); ++ } ++ __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getTransportMiddleWare).call(this).setTransport(transport); ++ this.isDeviceConnected = true; ++ return Promise.resolve(true); ++ } ++ /** ++ * Method to init eth app object on ledger device. ++ * This method is not supported on mobile. ++ */ ++ async attemptMakeApp() { ++ throw new Error('Method not supported.'); ++ } ++ /** ++ * Method to open ethereum application on ledger device. ++ * ++ */ ++ async openEthApp() { ++ await __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).openEthApp(); ++ } ++ /** ++ * Method to close all running application on ledger device. ++ * ++ */ ++ async closeApps() { ++ await __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).closeApps(); ++ } ++ /** ++ * Method to retrieve the name and version of the running application in ledger device. ++ * ++ * @returns An object contains appName and version. ++ */ ++ async getAppNameAndVersion() { ++ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).getAppNameAndVersion(); ++ } ++} ++exports.LedgerMobileBridge = LedgerMobileBridge; ++_LedgerMobileBridge_transportMiddleware = new WeakMap(), _LedgerMobileBridge_opts = new WeakMap(), _LedgerMobileBridge_instances = new WeakSet(), _LedgerMobileBridge_getTransportMiddleWare = function _LedgerMobileBridge_getTransportMiddleWare() { ++ if (__classPrivateFieldGet(this, _LedgerMobileBridge_transportMiddleware, "f")) { ++ return __classPrivateFieldGet(this, _LedgerMobileBridge_transportMiddleware, "f"); ++ } ++ throw new Error('Instance `transportMiddleware` is not initialized.'); ++}, _LedgerMobileBridge_getEthApp = function _LedgerMobileBridge_getEthApp() { ++ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getTransportMiddleWare).call(this).getEthApp(); ++}; ++//# sourceMappingURL=ledger-mobile-bridge.js.map +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map +new file mode 100644 +index 0000000..8cd62e2 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map +@@ -0,0 +1 @@ ++{"version":3,"file":"ledger-mobile-bridge.js","sourceRoot":"","sources":["../src/ledger-mobile-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["import ledgerService from '@ledgerhq/hw-app-eth/lib/services/ledger';\nimport type Transport from '@ledgerhq/hw-transport';\n\n// eslint-disable-next-line import/no-nodejs-modules\nimport {\n GetPublicKeyParams,\n GetPublicKeyResponse,\n LedgerBridge,\n LedgerSignMessageParams,\n LedgerSignMessageResponse,\n LedgerSignTransactionParams,\n LedgerSignTransactionResponse,\n LedgerSignTypedDataParams,\n LedgerSignTypedDataResponse,\n} from './ledger-bridge';\nimport {\n GetAppNameAndVersionResponse,\n LedgerMobileBridgeOptions,\n TransportMiddleware,\n type MetaMaskLedgerHwAppEth,\n} from './ledger-mobile-bridge/';\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface LedgerMobileBridge {\n getAppNameAndVersion(): Promise;\n openEthApp(): Promise;\n closeApps(): Promise;\n}\n\n/**\n * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware.\n */\nexport class LedgerMobileBridge\n implements LedgerBridge, LedgerMobileBridge\n{\n #transportMiddleware?: TransportMiddleware;\n\n #opts: LedgerMobileBridgeOptions;\n\n isDeviceConnected = false;\n\n constructor(\n transportMiddleware: TransportMiddleware,\n opts: LedgerMobileBridgeOptions = {},\n ) {\n this.#opts = opts;\n this.#transportMiddleware = transportMiddleware;\n }\n\n /**\n * Method to initializes the keyring.\n * Mobile ledger doesnt not require init.\n */\n async init(): Promise {\n return Promise.resolve();\n }\n\n /**\n * Method to destroy the keyring.\n * It will dispose the transportmiddleware and set isDeviceConnected to false.\n */\n async destroy(): Promise {\n try {\n await this.#getTransportMiddleWare().dispose();\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n this.isDeviceConnected = false;\n }\n\n /**\n * Method to sign a string Message.\n * Sending the string message to the device and returning the signed message.\n *\n * @param params - The descriptor to open the transport with.\n * @param params.hdPath - The descriptor to open the transport with.\n * @param params.message - An optional timeout for the transport connection.\n * @returns Retrieve v, r, s from the signed message.\n */\n async deviceSignMessage({\n hdPath,\n message,\n }: LedgerSignMessageParams): Promise {\n return this.#getEthApp().signPersonalMessage(hdPath, message);\n }\n\n /**\n * Method to sign a EIP712 Message.\n * Sending the typed data message to the device and returning the signed message.\n *\n * @param params - An object contains hdPath, domainSeparatorHex and hashStructMessageHex.\n * @param params.hdPath - The BIP 32 path of the account.\n * @param params.domainSeparatorHex - The domain separator.\n * @param params.hashStructMessageHex - The hashed struct message.\n * @returns Retrieve v, r, s from the signed message.\n */\n async deviceSignTypedData({\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n }: LedgerSignTypedDataParams): Promise {\n return this.#getEthApp().signEIP712HashedMessage(\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n );\n }\n\n /**\n * Method to sign a transaction\n * Sending the hexadecimal transaction message to the device and returning the signed transaction.\n *\n * @param params - An object contains tx, hdPath.\n * @param params.tx - The raw ethereum transaction in hexadecimal to sign.\n * @param params.hdPath - The BIP 32 path of the account.\n * @returns Retrieve v, r, s from the signed transaction.\n */\n async deviceSignTransaction({\n tx,\n hdPath,\n }: LedgerSignTransactionParams): Promise {\n const resolution = await ledgerService.resolveTransaction(tx, {}, {});\n return this.#getEthApp().signTransaction(hdPath, tx, resolution);\n }\n\n /**\n * Method to retrieve the ethereum address for a given BIP 32 path.\n *\n * @param params - An object contains hdPath.\n * @param params.hdPath - The BIP 32 path of the account.\n * @returns An object contains publicKey, address and chainCode.\n */\n async getPublicKey({\n hdPath,\n }: GetPublicKeyParams): Promise {\n return await this.#getEthApp().getAddress(hdPath, false, true);\n }\n\n /**\n * Method to retrieve the current configuration.\n *\n * @returns Retrieve current configuration.\n */\n async getOptions(): Promise {\n return this.#opts;\n }\n\n /**\n * Method to set the current configuration.\n *\n * @param opts - An configuration object.\n */\n async setOptions(opts: LedgerMobileBridgeOptions): Promise {\n this.#opts = opts;\n }\n\n /**\n * Method to set the transport object to communicate with the device.\n *\n * @param transport - The communication interface with the Ledger hardware wallet. There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb).\n * @returns Retrieve boolean.\n */\n async updateTransportMethod(transport: Transport): Promise {\n if (!transport.deviceModel?.id) {\n throw new Error(\n 'Property `deviceModel.id` is not defined in `transport`.',\n );\n }\n this.#getTransportMiddleWare().setTransport(transport);\n this.isDeviceConnected = true;\n return Promise.resolve(true);\n }\n\n /**\n * Method to init eth app object on ledger device.\n * This method is not supported on mobile.\n */\n async attemptMakeApp(): Promise {\n throw new Error('Method not supported.');\n }\n\n /**\n * Method to open ethereum application on ledger device.\n *\n */\n async openEthApp(): Promise {\n await this.#getEthApp().openEthApp();\n }\n\n /**\n * Method to close all running application on ledger device.\n *\n */\n async closeApps(): Promise {\n await this.#getEthApp().closeApps();\n }\n\n /**\n * Method to retrieve the name and version of the running application in ledger device.\n *\n * @returns An object contains appName and version.\n */\n async getAppNameAndVersion(): Promise {\n return this.#getEthApp().getAppNameAndVersion();\n }\n\n /**\n * Method to retrieve the transport middleWare object.\n *\n * @returns The TransportMiddleware object.\n */\n #getTransportMiddleWare(): TransportMiddleware {\n if (this.#transportMiddleware) {\n return this.#transportMiddleware;\n }\n throw new Error('Instance `transportMiddleware` is not initialized.');\n }\n\n /**\n * Method to retrieve the ledger Eth App object.\n *\n * @returns The ledger Eth App object.\n */\n #getEthApp(): MetaMaskLedgerHwAppEth {\n return this.#getTransportMiddleWare().getEthApp();\n }\n}\n"]} +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.d.ts +new file mode 100644 +index 0000000..e02de28 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.d.ts +@@ -0,0 +1,3 @@ ++export * from './middleware'; ++export * from './type'; ++export * from './ledger-hw-app'; +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js +new file mode 100644 +index 0000000..b8d180e +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js +@@ -0,0 +1,20 @@ ++"use strict"; ++var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { ++ if (k2 === undefined) k2 = k; ++ var desc = Object.getOwnPropertyDescriptor(m, k); ++ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { ++ desc = { enumerable: true, get: function() { return m[k]; } }; ++ } ++ Object.defineProperty(o, k2, desc); ++}) : (function(o, m, k, k2) { ++ if (k2 === undefined) k2 = k; ++ o[k2] = m[k]; ++})); ++var __exportStar = (this && this.__exportStar) || function(m, exports) { ++ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); ++}; ++Object.defineProperty(exports, "__esModule", { value: true }); ++__exportStar(require("./middleware"), exports); ++__exportStar(require("./type"), exports); ++__exportStar(require("./ledger-hw-app"), exports); ++//# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js.map +new file mode 100644 +index 0000000..dec127e +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js.map +@@ -0,0 +1 @@ ++{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ledger-mobile-bridge/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,yCAAuB;AACvB,kDAAgC","sourcesContent":["export * from './middleware';\nexport * from './type';\nexport * from './ledger-hw-app';\n"]} +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.d.ts +new file mode 100644 +index 0000000..4570615 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.d.ts +@@ -0,0 +1,12 @@ ++import LedgerHwAppEth from '@ledgerhq/hw-app-eth'; ++import { GetAppNameAndVersionResponse } from './type'; ++export interface MetaMaskLedgerHwAppEth extends LedgerHwAppEth { ++ openEthApp(): void; ++ closeApps(): void; ++ getAppNameAndVersion(): Promise; ++} ++export declare class MetaMaskLedgerHwAppEth extends LedgerHwAppEth implements MetaMaskLedgerHwAppEth { ++ readonly mainAppName = "BOLOS"; ++ readonly ethAppName = "Ethereum"; ++ readonly transportEncoding = "ascii"; ++} +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js +new file mode 100644 +index 0000000..a2fa515 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js +@@ -0,0 +1,60 @@ ++"use strict"; ++var __importDefault = (this && this.__importDefault) || function (mod) { ++ return (mod && mod.__esModule) ? mod : { "default": mod }; ++}; ++Object.defineProperty(exports, "__esModule", { value: true }); ++exports.MetaMaskLedgerHwAppEth = void 0; ++const hw_app_eth_1 = __importDefault(require("@ledgerhq/hw-app-eth")); ++// eslint-disable-next-line import/no-nodejs-modules ++const buffer_1 = require("buffer"); ++class MetaMaskLedgerHwAppEth extends hw_app_eth_1.default { ++ constructor() { ++ super(...arguments); ++ this.mainAppName = 'BOLOS'; ++ this.ethAppName = 'Ethereum'; ++ this.transportEncoding = 'ascii'; ++ } ++ /** ++ * Method to open ethereum application on ledger device. ++ * ++ */ ++ async openEthApp() { ++ await this.transport.send(0xe0, 0xd8, 0x00, 0x00, buffer_1.Buffer.from(this.ethAppName, this.transportEncoding)); ++ } ++ /** ++ * Method to close all running application on ledger device. ++ * ++ */ ++ async closeApps() { ++ await this.transport.send(0xb0, 0xa7, 0x00, 0x00); ++ } ++ /** ++ * Method to retrieve the name and version of the running application in ledger device. ++ * ++ * @returns An object contains appName and version. ++ */ ++ async getAppNameAndVersion() { ++ var _a, _b; ++ const response = await this.transport.send(0xb0, 0x01, 0x00, 0x00); ++ if (response[0] !== 1) { ++ throw new Error('Incorrect format return from getAppNameAndVersion.'); ++ } ++ let i = 1; ++ const nameLength = (_a = response[i]) !== null && _a !== void 0 ? _a : 0; ++ i += 1; ++ const appName = response ++ .slice(i, (i += nameLength)) ++ .toString(this.transportEncoding); ++ const versionLength = (_b = response[i]) !== null && _b !== void 0 ? _b : 0; ++ i += 1; ++ const version = response ++ .slice(i, (i += versionLength)) ++ .toString(this.transportEncoding); ++ return { ++ appName, ++ version, ++ }; ++ } ++} ++exports.MetaMaskLedgerHwAppEth = MetaMaskLedgerHwAppEth; ++//# sourceMappingURL=ledger-hw-app.js.map +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js.map +new file mode 100644 +index 0000000..e19f879 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js.map +@@ -0,0 +1 @@ ++{"version":3,"file":"ledger-hw-app.js","sourceRoot":"","sources":["../../src/ledger-mobile-bridge/ledger-hw-app.ts"],"names":[],"mappings":";;;;;;AAAA,sEAAkD;AAClD,oDAAoD;AACpD,mCAAgC;AAWhC,MAAa,sBACX,SAAQ,oBAAc;IADxB;;QAIW,gBAAW,GAAG,OAAO,CAAC;QAEtB,eAAU,GAAG,UAAU,CAAC;QAExB,sBAAiB,GAAG,OAAO,CAAC;IAuDvC,CAAC;IArDC;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACvB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,eAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,iBAAiB,CAAC,CACrD,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,oBAAoB;;QACxB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACnE,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE;YACrB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;SACvE;QAED,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,UAAU,GAAG,MAAA,QAAQ,CAAC,CAAC,CAAC,mCAAI,CAAC,CAAC;QACpC,CAAC,IAAI,CAAC,CAAC;QAEP,MAAM,OAAO,GAAG,QAAQ;aACrB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC;aAC3B,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAEpC,MAAM,aAAa,GAAG,MAAA,QAAQ,CAAC,CAAC,CAAC,mCAAI,CAAC,CAAC;QACvC,CAAC,IAAI,CAAC,CAAC;QAEP,MAAM,OAAO,GAAG,QAAQ;aACrB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC;aAC9B,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAEpC,OAAO;YACL,OAAO;YACP,OAAO;SACR,CAAC;IACJ,CAAC;CACF;AA/DD,wDA+DC","sourcesContent":["import LedgerHwAppEth from '@ledgerhq/hw-app-eth';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { Buffer } from 'buffer';\n\nimport { GetAppNameAndVersionResponse } from './type';\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface MetaMaskLedgerHwAppEth extends LedgerHwAppEth {\n openEthApp(): void;\n closeApps(): void;\n getAppNameAndVersion(): Promise;\n}\n\nexport class MetaMaskLedgerHwAppEth\n extends LedgerHwAppEth\n implements MetaMaskLedgerHwAppEth\n{\n readonly mainAppName = 'BOLOS';\n\n readonly ethAppName = 'Ethereum';\n\n readonly transportEncoding = 'ascii';\n\n /**\n * Method to open ethereum application on ledger device.\n *\n */\n async openEthApp(): Promise {\n await this.transport.send(\n 0xe0,\n 0xd8,\n 0x00,\n 0x00,\n Buffer.from(this.ethAppName, this.transportEncoding),\n );\n }\n\n /**\n * Method to close all running application on ledger device.\n *\n */\n async closeApps(): Promise {\n await this.transport.send(0xb0, 0xa7, 0x00, 0x00);\n }\n\n /**\n * Method to retrieve the name and version of the running application in ledger device.\n *\n * @returns An object contains appName and version.\n */\n async getAppNameAndVersion(): Promise {\n const response = await this.transport.send(0xb0, 0x01, 0x00, 0x00);\n if (response[0] !== 1) {\n throw new Error('Incorrect format return from getAppNameAndVersion.');\n }\n\n let i = 1;\n const nameLength = response[i] ?? 0;\n i += 1;\n\n const appName = response\n .slice(i, (i += nameLength))\n .toString(this.transportEncoding);\n\n const versionLength = response[i] ?? 0;\n i += 1;\n\n const version = response\n .slice(i, (i += versionLength))\n .toString(this.transportEncoding);\n\n return {\n appName,\n version,\n };\n }\n}\n"]} +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.d.ts +new file mode 100644 +index 0000000..7d3ab3d +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.d.ts +@@ -0,0 +1,41 @@ ++import type Transport from '@ledgerhq/hw-transport'; ++import { MetaMaskLedgerHwAppEth } from './ledger-hw-app'; ++export interface TransportMiddleware { ++ setTransport(transport: Transport): void; ++ getTransport(): Transport; ++ getEthApp(): MetaMaskLedgerHwAppEth; ++ dispose(): Promise; ++} ++/** ++ * LedgerTransportMiddleware is a middleware to communicate with the Ledger device via transport or LedgerHwAppEth ++ */ ++export declare class LedgerTransportMiddleware implements TransportMiddleware { ++ #private; ++ readonly mainAppName = "BOLOS"; ++ readonly ethAppName = "Ethereum"; ++ readonly transportEncoding = "ascii"; ++ /** ++ * Method to close the transport connection. ++ * ++ */ ++ dispose(): Promise; ++ /** ++ * Method to set the transport object. ++ * ++ * @param transport - The transport object for communicating with a Ledger hardware wallet. ++ */ ++ setTransport(transport: Transport): void; ++ /** ++ * Method to retrieve the transport object. ++ * ++ * @returns An generic interface for communicating with a Ledger hardware wallet. ++ */ ++ getTransport(): Transport; ++ /** ++ * Method to retrieve the eth app object. ++ * it create a new eth app instance if not exist. ++ * ++ * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation. ++ */ ++ getEthApp(): MetaMaskLedgerHwAppEth; ++} +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js +new file mode 100644 +index 0000000..4a3b05e +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js +@@ -0,0 +1,70 @@ ++"use strict"; ++var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { ++ if (kind === "m") throw new TypeError("Private method is not writable"); ++ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); ++ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); ++ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; ++}; ++var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { ++ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); ++ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); ++ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); ++}; ++var _LedgerTransportMiddleware_app, _LedgerTransportMiddleware_transport; ++Object.defineProperty(exports, "__esModule", { value: true }); ++exports.LedgerTransportMiddleware = void 0; ++const ledger_hw_app_1 = require("./ledger-hw-app"); ++/** ++ * LedgerTransportMiddleware is a middleware to communicate with the Ledger device via transport or LedgerHwAppEth ++ */ ++class LedgerTransportMiddleware { ++ constructor() { ++ this.mainAppName = 'BOLOS'; ++ this.ethAppName = 'Ethereum'; ++ this.transportEncoding = 'ascii'; ++ _LedgerTransportMiddleware_app.set(this, void 0); ++ _LedgerTransportMiddleware_transport.set(this, void 0); ++ } ++ /** ++ * Method to close the transport connection. ++ * ++ */ ++ async dispose() { ++ const transport = this.getTransport(); ++ await transport.close(); ++ } ++ /** ++ * Method to set the transport object. ++ * ++ * @param transport - The transport object for communicating with a Ledger hardware wallet. ++ */ ++ setTransport(transport) { ++ __classPrivateFieldSet(this, _LedgerTransportMiddleware_transport, transport, "f"); ++ } ++ /** ++ * Method to retrieve the transport object. ++ * ++ * @returns An generic interface for communicating with a Ledger hardware wallet. ++ */ ++ getTransport() { ++ if (!__classPrivateFieldGet(this, _LedgerTransportMiddleware_transport, "f")) { ++ throw new Error('Instance `transport` is not initialized.'); ++ } ++ return __classPrivateFieldGet(this, _LedgerTransportMiddleware_transport, "f"); ++ } ++ /** ++ * Method to retrieve the eth app object. ++ * it create a new eth app instance if not exist. ++ * ++ * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation. ++ */ ++ getEthApp() { ++ if (!__classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f")) { ++ __classPrivateFieldSet(this, _LedgerTransportMiddleware_app, new ledger_hw_app_1.MetaMaskLedgerHwAppEth(this.getTransport()), "f"); ++ } ++ return __classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f"); ++ } ++} ++exports.LedgerTransportMiddleware = LedgerTransportMiddleware; ++_LedgerTransportMiddleware_app = new WeakMap(), _LedgerTransportMiddleware_transport = new WeakMap(); ++//# sourceMappingURL=middleware.js.map +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js.map +new file mode 100644 +index 0000000..18d0992 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js.map +@@ -0,0 +1 @@ ++{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/ledger-mobile-bridge/middleware.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAEA,mDAAyD;AAUzD;;GAEG;AACH,MAAa,yBAAyB;IAAtC;QACW,gBAAW,GAAG,OAAO,CAAC;QAEtB,eAAU,GAAG,UAAU,CAAC;QAExB,sBAAiB,GAAG,OAAO,CAAC;QAErC,iDAA8B;QAE9B,uDAAuB;IA4CzB,CAAC;IA1CC;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACtC,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,SAAoB;QAC/B,uBAAA,IAAI,wCAAc,SAAS,MAAA,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,YAAY;QACV,IAAI,CAAC,uBAAA,IAAI,4CAAW,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;SAC7D;QACD,OAAO,uBAAA,IAAI,4CAAW,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,SAAS;QACP,IAAI,CAAC,uBAAA,IAAI,sCAAK,EAAE;YACd,uBAAA,IAAI,kCAAQ,IAAI,sCAAsB,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,MAAA,CAAC;SAC7D;QACD,OAAO,uBAAA,IAAI,sCAAK,CAAC;IACnB,CAAC;CACF;AArDD,8DAqDC","sourcesContent":["import type Transport from '@ledgerhq/hw-transport';\n\nimport { MetaMaskLedgerHwAppEth } from './ledger-hw-app';\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface TransportMiddleware {\n setTransport(transport: Transport): void;\n getTransport(): Transport;\n getEthApp(): MetaMaskLedgerHwAppEth;\n dispose(): Promise;\n}\n\n/**\n * LedgerTransportMiddleware is a middleware to communicate with the Ledger device via transport or LedgerHwAppEth\n */\nexport class LedgerTransportMiddleware implements TransportMiddleware {\n readonly mainAppName = 'BOLOS';\n\n readonly ethAppName = 'Ethereum';\n\n readonly transportEncoding = 'ascii';\n\n #app?: MetaMaskLedgerHwAppEth;\n\n #transport?: Transport;\n\n /**\n * Method to close the transport connection.\n *\n */\n async dispose(): Promise {\n const transport = this.getTransport();\n await transport.close();\n }\n\n /**\n * Method to set the transport object.\n *\n * @param transport - The transport object for communicating with a Ledger hardware wallet.\n */\n setTransport(transport: Transport): void {\n this.#transport = transport;\n }\n\n /**\n * Method to retrieve the transport object.\n *\n * @returns An generic interface for communicating with a Ledger hardware wallet.\n */\n getTransport(): Transport {\n if (!this.#transport) {\n throw new Error('Instance `transport` is not initialized.');\n }\n return this.#transport;\n }\n\n /**\n * Method to retrieve the eth app object.\n * it create a new eth app instance if not exist.\n *\n * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation.\n */\n getEthApp(): MetaMaskLedgerHwAppEth {\n if (!this.#app) {\n this.#app = new MetaMaskLedgerHwAppEth(this.getTransport());\n }\n return this.#app;\n }\n}\n"]} +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.d.ts +new file mode 100644 +index 0000000..2deabaf +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.d.ts +@@ -0,0 +1,5 @@ ++export declare type GetAppNameAndVersionResponse = { ++ appName: string; ++ version: string; ++}; ++export declare type LedgerMobileBridgeOptions = Record; +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js +new file mode 100644 +index 0000000..cd671e5 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js +@@ -0,0 +1,3 @@ ++"use strict"; ++Object.defineProperty(exports, "__esModule", { value: true }); ++//# sourceMappingURL=type.js.map +\ No newline at end of file +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js.map +new file mode 100644 +index 0000000..5f32af5 +--- /dev/null ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js.map +@@ -0,0 +1 @@ ++{"version":3,"file":"type.js","sourceRoot":"","sources":["../../src/ledger-mobile-bridge/type.ts"],"names":[],"mappings":"","sourcesContent":["export type GetAppNameAndVersionResponse = {\n appName: string;\n version: string;\n};\n\nexport type LedgerMobileBridgeOptions = Record;\n"]} +\ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 78f15355e8f..74040a1af94 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1287,17 +1287,6 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== -"@consensys/ledgerhq-metamask-keyring@0.0.9": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@consensys/ledgerhq-metamask-keyring/-/ledgerhq-metamask-keyring-0.0.9.tgz#f824381c9cf55c6e6aad5693263dee77a17d2ac2" - integrity sha512-o/wdUU/7s8fIZxP0CcKjaWQaoJ1bz2+3BgeQSsizR2WLqZJF8phYF6t9hwfZeFFgM1FMZDuanVapCvOi13QjiA== - dependencies: - "@ethereumjs/tx" "^4.2.0" - "@ledgerhq/hw-app-eth" "6.26.1" - "@metamask/eth-sig-util" "^7.0.0" - buffer "^6.0.3" - ethereumjs-util "^7.1.5" - "@consensys/on-ramp-sdk@1.26.8": version "1.26.8" resolved "https://registry.yarnpkg.com/@consensys/on-ramp-sdk/-/on-ramp-sdk-1.26.8.tgz#87344f42e6bced535dee9c0f7440954a6bd6955e" @@ -1848,7 +1837,7 @@ "@ethereumjs/util" "^8.1.0" crc-32 "^1.2.0" -"@ethereumjs/rlp@^4.0.1": +"@ethereumjs/rlp@^4.0.0", "@ethereumjs/rlp@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== @@ -1869,7 +1858,7 @@ "@ethereumjs/common" "^2.6.4" ethereumjs-util "^7.1.5" -"@ethereumjs/tx@^4.0.2", "@ethereumjs/tx@^4.1.2", "@ethereumjs/tx@^4.2.0": +"@ethereumjs/tx@^4.0.2", "@ethereumjs/tx@^4.1.1", "@ethereumjs/tx@^4.1.2", "@ethereumjs/tx@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-4.2.0.tgz#5988ae15daf5a3b3c815493bc6b495e76009e853" integrity sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw== @@ -1888,7 +1877,7 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -2125,7 +2114,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.5.0", "@ethersproject/rlp@^5.7.0": +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== @@ -3427,13 +3416,6 @@ dependencies: invariant "2" -"@ledgerhq/cryptoassets@^6.26.1": - version "6.37.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.37.0.tgz#302833777bcd210809ca7820afb82cff8da5c296" - integrity sha512-xwrDKTS9koQBNNzc7CqgV6zfGHvNFWJjlIL0Kc4O4DVWYR2vUdztUHcvwHD1KPjxNYhVnsgIopmtq47fHt3nMg== - dependencies: - invariant "2" - "@ledgerhq/devices@^5.26.0", "@ledgerhq/devices@^5.51.1": version "5.51.1" resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.51.1.tgz#d741a4a5d8f17c2f9d282fd27147e6fe1999edb7" @@ -3459,7 +3441,7 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== -"@ledgerhq/errors@^6.10.0", "@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.3": +"@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.3": version "6.16.3" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.3.tgz#646f68cc7e6e8d5126bce1ca06140c5ad963bee8" integrity sha512-3w7/SJVXOPa9mpzyll7VKoKnGwDD3BzWgN1Nom8byR40DiQvOKjHX+kKQausCedTHVNBn9euzPCNsftZ9+mxfw== @@ -3475,20 +3457,6 @@ bignumber.js "^9.0.1" rlp "^2.2.6" -"@ledgerhq/hw-app-eth@6.26.1": - version "6.26.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.26.1.tgz#c807087a563c4e1fb539116344ce114f5c84286b" - integrity sha512-maA5h+i92uoB+LXhkix1ZZWqI1qAxLBI5vEncuAbJubIQaJVv/BIlR3oAlZ+slNcq+9QUZ8vnzjRksn67kvoYA== - dependencies: - "@ethersproject/abi" "^5.5.0" - "@ethersproject/rlp" "^5.5.0" - "@ledgerhq/cryptoassets" "^6.26.1" - "@ledgerhq/errors" "^6.10.0" - "@ledgerhq/hw-transport" "^6.24.1" - "@ledgerhq/logs" "^6.10.0" - axios "^0.26.0" - bignumber.js "^9.0.2" - "@ledgerhq/hw-transport-node-hid-noevents@^5.26.0": version "5.51.1" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz#71f37f812e448178ad0bcc2258982150d211c1ab" @@ -3542,7 +3510,7 @@ "@ledgerhq/errors" "^5.50.0" events "^3.3.0" -"@ledgerhq/hw-transport@^6.24.1", "@ledgerhq/hw-transport@^6.30.4": +"@ledgerhq/hw-transport@^6.30.4": version "6.30.5" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.5.tgz#841c9e4bb3849536db110ca2894d693d55bf54fd" integrity sha512-JMl//7BgPBvWxrWyMu82jj6JEYtsQyOyhYtonWNgtxn6KUZWht3gU4gxmLpeIRr+DiS7e50mW7m3GA+EudZmmA== @@ -3557,7 +3525,7 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== -"@ledgerhq/logs@^6.10.0", "@ledgerhq/logs@^6.12.0": +"@ledgerhq/logs@^6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.12.0.tgz#ad903528bf3687a44da435d7b2479d724d374f5d" integrity sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA== @@ -3926,6 +3894,17 @@ "@metamask/obs-store" "^8.1.0" "@metamask/utils" "^8.1.0" +"@metamask/eth-ledger-bridge-keyring@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-ledger-bridge-keyring/-/eth-ledger-bridge-keyring-3.0.0.tgz#cfa5d406bfb3d50397d7fc890ceaae614eb2245a" + integrity sha512-22hMOT8wle7FI9n5kB2rkI2TsGn7uqul6eaXInJmxu+IBGxdn81Doduy6g+WYDNUAj1XEfxfK2LOzGPKBqNIFQ== + dependencies: + "@ethereumjs/rlp" "^4.0.0" + "@ethereumjs/tx" "^4.1.1" + "@ethereumjs/util" "^8.0.0" + "@metamask/eth-sig-util" "^7.0.0" + hdkey "^2.1.0" + "@metamask/eth-query@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-query/-/eth-query-3.0.1.tgz#3439eb6c7d5ccff1d6a66df1d1802bae0c890444" @@ -11569,7 +11548,7 @@ axios-retry@^3.1.2: "@babel/runtime" "^7.15.4" is-retry-allowed "^2.2.0" -axios@1.4.0, axios@1.6.0, axios@^0.26.0, axios@^0.27.0, axios@^0.x, axios@^1.6.7: +axios@1.4.0, axios@1.6.0, axios@^0.27.0, axios@^0.x, axios@^1.6.7: version "1.6.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102" integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg== @@ -11878,7 +11857,7 @@ bignumber.js@^7.2.1: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== -bignumber.js@^9.0.1, bignumber.js@^9.0.2: +bignumber.js@^9.0.1: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== @@ -17605,12 +17584,13 @@ hastscript@^5.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" -hdkey@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.0.1.tgz#0a211d0c510bfc44fa3ec9d44b13b634641cad74" - integrity sha512-c+tl9PHG9/XkGgG0tD7CJpRVaE0jfZizDNmnErUAKQ4EjQSOcOUcV3EN9ZEZS8pZ4usaeiiK0H7stzuzna8feA== +hdkey@^2.0.1, hdkey@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.1.0.tgz#755b30b73f54e93c31919c1b2f19205a8e57cb92" + integrity sha512-i9Wzi0Dy49bNS4tXXeGeu0vIcn86xXdPQUpEYg+SO1YiO8HtomjmmRMaRyqL0r59QfcD4PfVbSF3qmsWFwAemA== dependencies: bs58check "^2.1.2" + ripemd160 "^2.0.2" safe-buffer "^5.1.1" secp256k1 "^4.0.0" @@ -25098,7 +25078,7 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: +ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== @@ -26128,7 +26108,7 @@ string-range@~1.2, string-range@~1.2.1: resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd" integrity sha1-qJPtNH5yKZvIO++78qaSqNI51d0= -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -26146,15 +26126,6 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -26269,14 +26240,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm: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-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.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== @@ -28175,7 +28139,8 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -28210,15 +28175,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From c997d0ca74cfeda3bfa2557ce6fcd309b5d5c54b Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Thu, 11 Apr 2024 16:02:19 +0100 Subject: [PATCH 02/75] feat: make a backup of the change to switch to another branch. --- .../hooks/Ledger/useLedgerBluetooth.ts | 13 +++---- app/core/Ledger/Ledger.test.ts | 16 ++------- app/core/Ledger/Ledger.ts | 24 ++++++------- .../hardwareWallet/hardwareWallets/ledger.ts | 9 ++--- ios/MetaMask.xcodeproj/project.pbxproj | 36 ++++++++----------- ios/Podfile.lock | 2 +- package.json | 5 +-- ...mask+eth-ledger-bridge-keyring+3.0.0.patch | 4 +-- 8 files changed, 43 insertions(+), 66 deletions(-) diff --git a/app/components/hooks/Ledger/useLedgerBluetooth.ts b/app/components/hooks/Ledger/useLedgerBluetooth.ts index 1bf4136aef8..19c458b68d1 100644 --- a/app/components/hooks/Ledger/useLedgerBluetooth.ts +++ b/app/components/hooks/Ledger/useLedgerBluetooth.ts @@ -33,7 +33,7 @@ const RESTART_LIMIT = 5; // Assumptions // 1. One big code block - logic all encapsulated in logicToRun // 2. logicToRun calls setUpBluetoothConnection -function useLedgerBluetooth(deviceId?: string): UseLedgerBluetoothHook { +function useLedgerBluetooth(): UseLedgerBluetoothHook { // This is to track if we are expecting code to run or connection operational const [isSendingLedgerCommands, setIsSendingLedgerCommands] = useState(false); @@ -74,16 +74,18 @@ function useLedgerBluetooth(deviceId?: string): UseLedgerBluetoothHook { // Sets up the Bluetooth transport const setUpBluetoothConnection = async () => { - if (transportRef.current && deviceId) { + if (transportRef.current) { setIsSendingLedgerCommands(true); } - if (!transportRef.current && deviceId) { + if (!transportRef.current) { try { const BluetoothTransport: any = await import( '@ledgerhq/react-native-hw-transport-ble' ); - transportRef.current = await BluetoothTransport.default.open(deviceId); + + //TODO - check if this is the correct way to open the transport + transportRef.current = await BluetoothTransport.default.open(); // eslint-disable-next-line @typescript-eslint/no-unused-vars transportRef.current?.on('disconnect', (e: any) => { transportRef.current = undefined; @@ -127,13 +129,12 @@ function useLedgerBluetooth(deviceId?: string): UseLedgerBluetoothHook { // Must do this at start of every code block to run to ensure transport is set await setUpBluetoothConnection(); - if (!transportRef.current || !deviceId) { + if (!transportRef.current) { throw new Error('transportRef.current is undefined'); } // Initialise the keyring and check for pre-conditions (is the correct app running?) const appName = await connectLedgerHardware( transportRef.current as unknown as BleTransport, - deviceId, ); // BOLOS is the Ledger main screen app diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index d60d79ed1a8..a8f2c6671d6 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -7,7 +7,6 @@ import { forgetLedger, ledgerSignTypedMessage, unlockLedgerDefaultAccount, - getDeviceId, } from './Ledger'; import Engine from '../../core/Engine'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; @@ -21,7 +20,6 @@ const ledgerKeyring = { quitApp: jest.fn(), forgetDevice: jest.fn(), deserialize: jest.fn(), - deviceId: 'deviceId', getName: jest.fn().mockResolvedValue('name'), openEthereumAppOnLedger: jest.fn(), }; @@ -29,7 +27,6 @@ const ledgerKeyring = { describe('Ledger core', () => { const ledgerKeyringClone = { ...ledgerKeyring, - deviceId: 'deviceIdClone', }; const mockAddNewKeyring = jest.fn().mockReturnValue(ledgerKeyring); const mockGetKeyringsByType = jest.fn().mockReturnValue([ledgerKeyringClone]); @@ -82,17 +79,17 @@ describe('Ledger core', () => { describe('connectLedgerHardware', () => { const mockTransport = 'foo' as unknown as BleTransport; it('should call keyring.setTransport', async () => { - await connectLedgerHardware(mockTransport, 'bar'); + await connectLedgerHardware(mockTransport); expect(ledgerKeyring.setTransport).toHaveBeenCalled(); }); it('should call keyring.getAppAndVersion', async () => { - await connectLedgerHardware(mockTransport, 'bar'); + await connectLedgerHardware(mockTransport); expect(ledgerKeyring.getAppAndVersion).toHaveBeenCalled(); }); it('should return app name', async () => { - const value = await connectLedgerHardware(mockTransport, 'bar'); + const value = await connectLedgerHardware(mockTransport); expect(value).toBe('appName'); }); }); @@ -150,13 +147,6 @@ describe('Ledger core', () => { }); }); - describe('getDeviceId', () => { - it('should return deviceId', async () => { - const value = await getDeviceId(); - expect(value).toBe('deviceId'); - }); - }); - describe('ledgerSignTypedMessage', () => { it('should call signTypedMessage from keyring controller and return correct signature', async () => { const expectedArg = { diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index d6a844c789f..85ddbe39fe3 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -38,17 +38,15 @@ export const getLedgerKeyring = async (): Promise => { * Connects to the ledger device by requesting some metadata from it. * * @param transport - The transport to use to connect to the device - * @param deviceId - The device ID to connect to * @returns The name of the currently open application on the device */ export const connectLedgerHardware = async ( transport: BleTransport, - deviceId: string, ): Promise => { const keyring = await getLedgerKeyring(); keyring.setHdPath("m/44'/60'/0'/0"); const bridge = keyring.bridge as LedgerMobileBridge; - bridge.updateTransportMethod(transport); + await bridge.updateTransportMethod(transport); // keyring.setTransport(transport as unknown as any, deviceId); const { appName } = await bridge.getAppNameAndVersion(); return appName; @@ -111,16 +109,16 @@ export const forgetLedger = async (): Promise => { PreferencesController.updateIdentities(await KeyringController.getAccounts()); }; -/** - * Get DeviceId from Ledger Keyring - * - * @returns The DeviceId - */ -export const getDeviceId = async (): Promise => { - const ledgerKeyring = await getLedgerKeyring(); - const bridge = ledgerKeyring.bridge as LedgerMobileBridge; - return bridge.deviceId; -}; +// /** +// * Get DeviceId from Ledger Keyring +// * +// * @returns The DeviceId +// */ +// export const getDeviceId = async (): Promise => { +// const ledgerKeyring = await getLedgerKeyring(); +// const bridge = ledgerKeyring.bridge as LedgerMobileBridge; +// return bridge.deviceId; +// }; /** * signTypedMessage from Ledger Keyring diff --git a/app/util/hardwareWallet/hardwareWallets/ledger.ts b/app/util/hardwareWallet/hardwareWallets/ledger.ts index 988eedd0103..bee7d1b0d0a 100644 --- a/app/util/hardwareWallet/hardwareWallets/ledger.ts +++ b/app/util/hardwareWallet/hardwareWallets/ledger.ts @@ -1,22 +1,17 @@ import { createNavigationDetails } from '../../navigation/navUtils'; import Routes from '../../../constants/navigation/Routes'; -import { getDeviceId } from '../../../core/Ledger/Ledger'; + export interface LedgerSignModelNavParams { messageParams: any; onConfirmationComplete: (confirmed: boolean, rawSignature?: any) => void; version: any; type: any; } -export interface LedgerMessageSignModalParams extends LedgerSignModelNavParams { - deviceId: any; -} export const signModalNavDetail = async (params: LedgerSignModelNavParams) => { - const deviceId = await getDeviceId(); - return createNavigationDetails( + return createNavigationDetails( Routes.LEDGER_MESSAGE_SIGN_MODAL, )({ ...params, - deviceId, }); }; diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 2d2c813935a..29d85a30054 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 07CBADD9D4B441008304F8D3 /* EuclidCircularB-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = A98029A3662F4C1391489A6B /* EuclidCircularB-Light.otf */; }; 0C119A06A4353DD11451F541 /* libPods-MetaMask-Flask.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA078AF57878ABB05D063C8F /* libPods-MetaMask-Flask.a */; }; - 0FD509E0336BF221F6527B24 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + 0FD509E0336BF221F6527B24 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; @@ -131,7 +131,7 @@ B339FF2E289ABD70001B89FB /* EuclidCircularB-SemiboldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 9499B01ECAC44DA29AC44E80 /* EuclidCircularB-SemiboldItalic.otf */; }; B339FF32289ABD70001B89FB /* Branch.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 153F84C92319B8DB00C19B63 /* Branch.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; B339FF3C289ABF2C001B89FB /* MetaMask-QA-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B339FEA72899852C001B89FB /* MetaMask-QA-Info.plist */; }; - B638844E306CAE9147B52C85 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + B638844E306CAE9147B52C85 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; BF39E5BAE0F34F9091FF6AC0 /* EuclidCircularB-Semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = A8DE9C5BC0714D648276E123 /* EuclidCircularB-Semibold.otf */; }; CD13D926E1E84D9ABFE672C0 /* Roboto-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3E2492C67CF345CABD7B8601 /* Roboto-BlackItalic.ttf */; }; CF9895772A3B49BE00B4C9B5 /* RCTMinimizer.m in Sources */ = {isa = PBXBuildFile; fileRef = CF9895762A3B49BE00B4C9B5 /* RCTMinimizer.m */; }; @@ -139,7 +139,7 @@ CF98DA9C28D9FEB700096782 /* RCTScreenshotDetect.m in Sources */ = {isa = PBXBuildFile; fileRef = CF98DA9B28D9FEB700096782 /* RCTScreenshotDetect.m */; }; CFD8DFC828EDD4C800CC75F6 /* RCTScreenshotDetect.m in Sources */ = {isa = PBXBuildFile; fileRef = CF98DA9B28D9FEB700096782 /* RCTScreenshotDetect.m */; }; D171C39A8BD44DBEB6B68480 /* EuclidCircularB-MediumItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 42CBA652072F4BE2A8B815C1 /* EuclidCircularB-MediumItalic.otf */; }; - D45BF85DECACCB74EDCBE88A /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + D45BF85DECACCB74EDCBE88A /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; DC6A024F56DD43E1A83B47B1 /* Roboto-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5FF0FF1DFB74B3C8BB99E09 /* Roboto-MediumItalic.ttf */; }; DDB2D8FF8BDA806A38D61B1B /* libPods-MetaMask-QA.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E0B857D61941CE8A3F59C662 /* libPods-MetaMask-QA.a */; }; E34DE917F6FC4438A6E88402 /* EuclidCircularB-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 13EE4910D3BD408A8FCCA5D7 /* EuclidCircularB-BoldItalic.otf */; }; @@ -325,9 +325,9 @@ 650F2B9D24DC5FF200C3B9C4 /* libRCTAesForked.a in Frameworks */, 153C1ABB2217BCDC0088EFE0 /* JavaScriptCore.framework in Frameworks */, 153F84CA2319B8FD00C19B63 /* Branch.framework in Frameworks */, - 0FD509E0336BF221F6527B24 /* (null) in Frameworks */, - D45BF85DECACCB74EDCBE88A /* (null) in Frameworks */, - B638844E306CAE9147B52C85 /* (null) in Frameworks */, + 0FD509E0336BF221F6527B24 /* BuildFile in Frameworks */, + D45BF85DECACCB74EDCBE88A /* BuildFile in Frameworks */, + B638844E306CAE9147B52C85 /* BuildFile in Frameworks */, 7CFB6EF0B7853F1C4E863E5E /* libPods-MetaMask.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1207,7 +1207,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1300; + CURRENT_PROJECT_VERSION = 1300; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1272,7 +1272,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1300; + CURRENT_PROJECT_VERSION = 1300; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1335,7 +1335,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1300; + CURRENT_PROJECT_VERSION = 1300; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1396,7 +1396,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1300; + CURRENT_PROJECT_VERSION = 1300; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; @@ -1487,11 +1487,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; }; @@ -1533,11 +1529,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; @@ -1554,7 +1546,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMaskDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1300; + CURRENT_PROJECT_VERSION = 1300; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 48XVW22RCG; @@ -1622,7 +1614,7 @@ CODE_SIGN_ENTITLEMENTS = MetaMask/MetaMask.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1300; + CURRENT_PROJECT_VERSION = 1300; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 48XVW22RCG; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 142adebeae7..508788a08a4 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1002,7 +1002,7 @@ SPEC CHECKSUMS: RNReanimated: b1220a0e5168745283ff5d53bfc7d2144b2cee1b RNScreens: ea4cd3a853063cda19a4e3c28d2e52180c80f4eb RNSensors: c363d486c879e181905dea84a2535e49af1c2d25 - RNSentry: 98e170b6eedc5c54e3ac88b6140fffb8b496deb4 + RNSentry: 1b70955af02f86efc3c9a09989f0d0695b0d237d RNShare: f116bbb04f310c665ca483d0bd1e88cf59b3b334 RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f RNVectorIcons: 6607bd3a30291d0edb56f9bbe7ae411ee2b928b0 diff --git a/package.json b/package.json index 176f0bb7d1b..7f55a4260b7 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,6 @@ "**/babel-runtime/regenerator-runtime": "^0.13.8" }, "dependencies": { - "@metamask/eth-ledger-bridge-keyring": "3.0.0", "@consensys/on-ramp-sdk": "1.26.8", "@eth-optimism/contracts": "0.0.0-2021919175625", "@ethereumjs/common": "^2.3.1", @@ -143,6 +142,7 @@ "@metamask/contract-metadata": "^2.1.0", "@metamask/controller-utils": "^4.0.0", "@metamask/design-tokens": "^2.0.0", + "@metamask/eth-ledger-bridge-keyring": "3.0.0", "@metamask/eth-sig-util": "^4.0.1", "@metamask/etherscan-link": "^2.0.0", "@metamask/gas-fee-controller": "6.1.2", @@ -551,7 +551,8 @@ "react-native-svg-asset-plugin>sharp": true, "ts-node>@swc/core": false, "@metamask/sdk-communication-layer>bufferutil": false, - "@metamask/sdk-communication-layer>utf-8-validate": false + "@metamask/sdk-communication-layer>utf-8-validate": false, + "@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false } } } diff --git a/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch b/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch index e51e53d9e1a..f2ffa383490 100644 --- a/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch +++ b/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch @@ -1,10 +1,10 @@ diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store b/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store new file mode 100644 -index 0000000..7a56c26 +index 0000000..abf44d3 Binary files /dev/null and b/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store differ diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store new file mode 100644 -index 0000000..062174d +index 0000000..3fdc246 Binary files /dev/null and b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store differ diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.d.ts index b08ff49..34ffdbd 100644 From 9e9e90b68e737067829ca7d8602f3f731445e2fd Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Thu, 18 Apr 2024 14:18:55 +0100 Subject: [PATCH 03/75] fix: Downgrade the library --- ...mask+eth-ledger-bridge-keyring+3.0.0.patch | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch b/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch index f2ffa383490..613941faf33 100644 --- a/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch +++ b/patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch @@ -1,10 +1,10 @@ diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store b/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store new file mode 100644 -index 0000000..abf44d3 +index 0000000..80dcfc7 Binary files /dev/null and b/node_modules/@metamask/eth-ledger-bridge-keyring/.DS_Store differ diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store new file mode 100644 -index 0000000..3fdc246 +index 0000000..6eedc11 Binary files /dev/null and b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/.DS_Store differ diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/index.d.ts index b08ff49..34ffdbd 100644 @@ -39,12 +39,12 @@ index de9eb8f..a940cea 100644 +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,yDAAuC;AACvC,yDAAuC;AACvC,0DAAwC;AACxC,kDAAgC","sourcesContent":["export * from './ledger-keyring';\nexport * from './ledger-iframe-bridge';\nexport * from './ledger-mobile-bridge';\nexport * from './ledger-mobile-bridge/';\nexport * from './ledger-bridge';\n"]} \ No newline at end of file diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.d.ts -index 518c632..30e0e7a 100644 +index 518c632..e078bd5 100644 --- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.d.ts +++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.d.ts @@ -1,10 +1,9 @@ import type LedgerHwAppEth from '@ledgerhq/hw-app-eth'; -+import { Transport } from '@ledgerhq/types-devices'; ++import type Transport from '@ledgerhq/hw-transport'; export declare type GetPublicKeyParams = { hdPath: string; }; @@ -65,13 +65,13 @@ index 518c632..30e0e7a 100644 deviceSignTransaction(params: LedgerSignTransactionParams): Promise; deviceSignMessage(params: LedgerSignMessageParams): Promise; diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map -index 0932bdd..321e28e 100644 +index 0932bdd..e02c7e8 100644 --- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map +++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map @@ -1 +1 @@ -{"version":3,"file":"ledger-bridge.js","sourceRoot":"","sources":["../src/ledger-bridge.ts"],"names":[],"mappings":"","sourcesContent":["import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';\n\nexport type GetPublicKeyParams = { hdPath: string };\nexport type GetPublicKeyResponse = Awaited<\n ReturnType\n> & {\n chainCode: string;\n};\n\nexport type LedgerSignTransactionParams = { hdPath: string; tx: string };\nexport type LedgerSignTransactionResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignMessageParams = { hdPath: string; message: string };\nexport type LedgerSignMessageResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTypedDataParams = {\n hdPath: string;\n domainSeparatorHex: string;\n hashStructMessageHex: string;\n};\nexport type LedgerSignTypedDataResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerBridgeOptions = Record;\n\nexport type LedgerBridge = {\n isDeviceConnected: boolean;\n\n init(): Promise;\n\n destroy(): Promise;\n\n /**\n * Method to get the current configuration of the ledger bridge keyring.\n */\n getOptions(): Promise;\n\n /**\n * Method to set the current configuration of the ledger bridge keyring.\n *\n * @param opts - An object contains configuration of the bridge.\n */\n setOptions(opts: T): Promise;\n\n attemptMakeApp(): Promise;\n\n updateTransportMethod(transportType: string): Promise;\n\n getPublicKey(params: GetPublicKeyParams): Promise;\n\n deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise;\n\n deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise;\n\n deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise;\n};\n"]} \ No newline at end of file -+{"version":3,"file":"ledger-bridge.js","sourceRoot":"","sources":["../src/ledger-bridge.ts"],"names":[],"mappings":"","sourcesContent":["import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';\nimport { Transport } from '@ledgerhq/types-devices';\n\nexport type GetPublicKeyParams = { hdPath: string };\nexport type GetPublicKeyResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTransactionParams = { hdPath: string; tx: string };\nexport type LedgerSignTransactionResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignMessageParams = { hdPath: string; message: string };\nexport type LedgerSignMessageResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTypedDataParams = {\n hdPath: string;\n domainSeparatorHex: string;\n hashStructMessageHex: string;\n};\nexport type LedgerSignTypedDataResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerBridgeOptions = Record;\n\nexport type LedgerBridge = {\n isDeviceConnected: boolean;\n\n init(): Promise;\n\n destroy(): Promise;\n\n /**\n * Method to get the current configuration of the ledger bridge keyring.\n */\n getOptions(): Promise;\n\n /**\n * Method to set the current configuration of the ledger bridge keyring.\n *\n * @param opts - An object contains configuration of the bridge.\n */\n setOptions(opts: T): Promise;\n\n attemptMakeApp(): Promise;\n\n updateTransportMethod(transportType: string | Transport): Promise;\n\n getPublicKey(params: GetPublicKeyParams): Promise;\n\n deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise;\n\n deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise;\n\n deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise;\n};\n"]} ++{"version":3,"file":"ledger-bridge.js","sourceRoot":"","sources":["../src/ledger-bridge.ts"],"names":[],"mappings":"","sourcesContent":["import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';\nimport type Transport from '@ledgerhq/hw-transport';\nexport type GetPublicKeyParams = { hdPath: string };\nexport type GetPublicKeyResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTransactionParams = { hdPath: string; tx: string };\nexport type LedgerSignTransactionResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignMessageParams = { hdPath: string; message: string };\nexport type LedgerSignMessageResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTypedDataParams = {\n hdPath: string;\n domainSeparatorHex: string;\n hashStructMessageHex: string;\n};\nexport type LedgerSignTypedDataResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerBridgeOptions = Record;\n\nexport type LedgerBridge = {\n isDeviceConnected: boolean;\n\n init(): Promise;\n\n destroy(): Promise;\n\n /**\n * Method to get the current configuration of the ledger bridge keyring.\n */\n getOptions(): Promise;\n\n /**\n * Method to set the current configuration of the ledger bridge keyring.\n *\n * @param opts - An object contains configuration of the bridge.\n */\n setOptions(opts: T): Promise;\n\n attemptMakeApp(): Promise;\n\n updateTransportMethod(transportType: string | Transport): Promise;\n\n getPublicKey(params: GetPublicKeyParams): Promise;\n\n deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise;\n\n deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise;\n\n deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise;\n};\n"]} \ No newline at end of file diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js index cd18e51..13ed60d 100644 @@ -101,7 +101,7 @@ index 2b458a0..c02e4ce 100644 \ No newline at end of file diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts new file mode 100644 -index 0000000..b0e325c +index 0000000..d6f03f2 --- /dev/null +++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts @@ -0,0 +1,89 @@ @@ -116,7 +116,7 @@ index 0000000..b0e325c +/** + * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware. + */ -+export declare class LedgerMobileBridge implements LedgerBridge, LedgerMobileBridge { ++export declare class LedgerMobileBridge implements LedgerBridge { + #private; + isDeviceConnected: boolean; + constructor(transportMiddleware: TransportMiddleware, opts?: LedgerMobileBridgeOptions); @@ -374,11 +374,11 @@ index 0000000..3fe54b8 \ No newline at end of file diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map new file mode 100644 -index 0000000..8cd62e2 +index 0000000..f3dc579 --- /dev/null +++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map @@ -0,0 +1 @@ -+{"version":3,"file":"ledger-mobile-bridge.js","sourceRoot":"","sources":["../src/ledger-mobile-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["import ledgerService from '@ledgerhq/hw-app-eth/lib/services/ledger';\nimport type Transport from '@ledgerhq/hw-transport';\n\n// eslint-disable-next-line import/no-nodejs-modules\nimport {\n GetPublicKeyParams,\n GetPublicKeyResponse,\n LedgerBridge,\n LedgerSignMessageParams,\n LedgerSignMessageResponse,\n LedgerSignTransactionParams,\n LedgerSignTransactionResponse,\n LedgerSignTypedDataParams,\n LedgerSignTypedDataResponse,\n} from './ledger-bridge';\nimport {\n GetAppNameAndVersionResponse,\n LedgerMobileBridgeOptions,\n TransportMiddleware,\n type MetaMaskLedgerHwAppEth,\n} from './ledger-mobile-bridge/';\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface LedgerMobileBridge {\n getAppNameAndVersion(): Promise;\n openEthApp(): Promise;\n closeApps(): Promise;\n}\n\n/**\n * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware.\n */\nexport class LedgerMobileBridge\n implements LedgerBridge, LedgerMobileBridge\n{\n #transportMiddleware?: TransportMiddleware;\n\n #opts: LedgerMobileBridgeOptions;\n\n isDeviceConnected = false;\n\n constructor(\n transportMiddleware: TransportMiddleware,\n opts: LedgerMobileBridgeOptions = {},\n ) {\n this.#opts = opts;\n this.#transportMiddleware = transportMiddleware;\n }\n\n /**\n * Method to initializes the keyring.\n * Mobile ledger doesnt not require init.\n */\n async init(): Promise {\n return Promise.resolve();\n }\n\n /**\n * Method to destroy the keyring.\n * It will dispose the transportmiddleware and set isDeviceConnected to false.\n */\n async destroy(): Promise {\n try {\n await this.#getTransportMiddleWare().dispose();\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n this.isDeviceConnected = false;\n }\n\n /**\n * Method to sign a string Message.\n * Sending the string message to the device and returning the signed message.\n *\n * @param params - The descriptor to open the transport with.\n * @param params.hdPath - The descriptor to open the transport with.\n * @param params.message - An optional timeout for the transport connection.\n * @returns Retrieve v, r, s from the signed message.\n */\n async deviceSignMessage({\n hdPath,\n message,\n }: LedgerSignMessageParams): Promise {\n return this.#getEthApp().signPersonalMessage(hdPath, message);\n }\n\n /**\n * Method to sign a EIP712 Message.\n * Sending the typed data message to the device and returning the signed message.\n *\n * @param params - An object contains hdPath, domainSeparatorHex and hashStructMessageHex.\n * @param params.hdPath - The BIP 32 path of the account.\n * @param params.domainSeparatorHex - The domain separator.\n * @param params.hashStructMessageHex - The hashed struct message.\n * @returns Retrieve v, r, s from the signed message.\n */\n async deviceSignTypedData({\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n }: LedgerSignTypedDataParams): Promise {\n return this.#getEthApp().signEIP712HashedMessage(\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n );\n }\n\n /**\n * Method to sign a transaction\n * Sending the hexadecimal transaction message to the device and returning the signed transaction.\n *\n * @param params - An object contains tx, hdPath.\n * @param params.tx - The raw ethereum transaction in hexadecimal to sign.\n * @param params.hdPath - The BIP 32 path of the account.\n * @returns Retrieve v, r, s from the signed transaction.\n */\n async deviceSignTransaction({\n tx,\n hdPath,\n }: LedgerSignTransactionParams): Promise {\n const resolution = await ledgerService.resolveTransaction(tx, {}, {});\n return this.#getEthApp().signTransaction(hdPath, tx, resolution);\n }\n\n /**\n * Method to retrieve the ethereum address for a given BIP 32 path.\n *\n * @param params - An object contains hdPath.\n * @param params.hdPath - The BIP 32 path of the account.\n * @returns An object contains publicKey, address and chainCode.\n */\n async getPublicKey({\n hdPath,\n }: GetPublicKeyParams): Promise {\n return await this.#getEthApp().getAddress(hdPath, false, true);\n }\n\n /**\n * Method to retrieve the current configuration.\n *\n * @returns Retrieve current configuration.\n */\n async getOptions(): Promise {\n return this.#opts;\n }\n\n /**\n * Method to set the current configuration.\n *\n * @param opts - An configuration object.\n */\n async setOptions(opts: LedgerMobileBridgeOptions): Promise {\n this.#opts = opts;\n }\n\n /**\n * Method to set the transport object to communicate with the device.\n *\n * @param transport - The communication interface with the Ledger hardware wallet. There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb).\n * @returns Retrieve boolean.\n */\n async updateTransportMethod(transport: Transport): Promise {\n if (!transport.deviceModel?.id) {\n throw new Error(\n 'Property `deviceModel.id` is not defined in `transport`.',\n );\n }\n this.#getTransportMiddleWare().setTransport(transport);\n this.isDeviceConnected = true;\n return Promise.resolve(true);\n }\n\n /**\n * Method to init eth app object on ledger device.\n * This method is not supported on mobile.\n */\n async attemptMakeApp(): Promise {\n throw new Error('Method not supported.');\n }\n\n /**\n * Method to open ethereum application on ledger device.\n *\n */\n async openEthApp(): Promise {\n await this.#getEthApp().openEthApp();\n }\n\n /**\n * Method to close all running application on ledger device.\n *\n */\n async closeApps(): Promise {\n await this.#getEthApp().closeApps();\n }\n\n /**\n * Method to retrieve the name and version of the running application in ledger device.\n *\n * @returns An object contains appName and version.\n */\n async getAppNameAndVersion(): Promise {\n return this.#getEthApp().getAppNameAndVersion();\n }\n\n /**\n * Method to retrieve the transport middleWare object.\n *\n * @returns The TransportMiddleware object.\n */\n #getTransportMiddleWare(): TransportMiddleware {\n if (this.#transportMiddleware) {\n return this.#transportMiddleware;\n }\n throw new Error('Instance `transportMiddleware` is not initialized.');\n }\n\n /**\n * Method to retrieve the ledger Eth App object.\n *\n * @returns The ledger Eth App object.\n */\n #getEthApp(): MetaMaskLedgerHwAppEth {\n return this.#getTransportMiddleWare().getEthApp();\n }\n}\n"]} ++{"version":3,"file":"ledger-mobile-bridge.js","sourceRoot":"","sources":["../src/ledger-mobile-bridge.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,sFAAqE;AA6BrE;;GAEG;AACH,MAAa,kBAAkB;IAS7B,YACE,mBAAwC,EACxC,OAAkC,EAAE;;QARtC,0DAA2C;QAE3C,2CAAiC;QAEjC,sBAAiB,GAAG,KAAK,CAAC;QAMxB,uBAAA,IAAI,4BAAS,IAAI,MAAA,CAAC;QAClB,uBAAA,IAAI,2CAAwB,mBAAmB,MAAA,CAAC;IAClD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI;QACR,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,IAAI;YACF,MAAM,uBAAA,IAAI,iFAAwB,MAA5B,IAAI,CAA0B,CAAC,OAAO,EAAE,CAAC;SAChD;QAAC,OAAO,KAAK,EAAE;YACd,sCAAsC;YACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SACtB;QACD,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACjC,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,iBAAiB,CAAC,EACtB,MAAM,EACN,OAAO,GACiB;QACxB,OAAO,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,mBAAmB,CAAC,EACxB,MAAM,EACN,kBAAkB,EAClB,oBAAoB,GACM;QAC1B,OAAO,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,uBAAuB,CAC9C,MAAM,EACN,kBAAkB,EAClB,oBAAoB,CACrB,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,qBAAqB,CAAC,EAC1B,EAAE,EACF,MAAM,GACsB;QAC5B,MAAM,UAAU,GAAG,MAAM,gBAAa,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACtE,OAAO,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,YAAY,CAAC,EACjB,MAAM,GACa;QACnB,OAAO,MAAM,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACjE,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,uBAAA,IAAI,gCAAM,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU,CAAC,IAA+B;QAC9C,uBAAA,IAAI,4BAAS,IAAI,MAAA,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,qBAAqB,CAAC,SAAoB;;QAC9C,IAAI,CAAC,CAAA,MAAA,SAAS,CAAC,WAAW,0CAAE,EAAE,CAAA,EAAE;YAC9B,MAAM,IAAI,KAAK,CACb,0DAA0D,CAC3D,CAAC;SACH;QACD,uBAAA,IAAI,iFAAwB,MAA5B,IAAI,CAA0B,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC9B,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,UAAU,EAAE,CAAC;IACvC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,SAAS,EAAE,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,oBAAoB;QACxB,OAAO,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,oBAAoB,EAAE,CAAC;IAClD,CAAC;CAsBF;AAnMD,gDAmMC;;IAdG,IAAI,uBAAA,IAAI,+CAAqB,EAAE;QAC7B,OAAO,uBAAA,IAAI,+CAAqB,CAAC;KAClC;IACD,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;AACxE,CAAC;IAQC,OAAO,uBAAA,IAAI,iFAAwB,MAA5B,IAAI,CAA0B,CAAC,SAAS,EAAE,CAAC;AACpD,CAAC","sourcesContent":["import ledgerService from '@ledgerhq/hw-app-eth/lib/services/ledger';\nimport type Transport from '@ledgerhq/hw-transport';\n\n// eslint-disable-next-line import/no-nodejs-modules\nimport {\n GetPublicKeyParams,\n GetPublicKeyResponse,\n LedgerBridge,\n LedgerSignMessageParams,\n LedgerSignMessageResponse,\n LedgerSignTransactionParams,\n LedgerSignTransactionResponse,\n LedgerSignTypedDataParams,\n LedgerSignTypedDataResponse,\n} from './ledger-bridge';\nimport {\n GetAppNameAndVersionResponse,\n LedgerMobileBridgeOptions,\n TransportMiddleware,\n type MetaMaskLedgerHwAppEth,\n} from './ledger-mobile-bridge/';\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface LedgerMobileBridge {\n getAppNameAndVersion(): Promise;\n openEthApp(): Promise;\n closeApps(): Promise;\n}\n\n/**\n * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware.\n */\nexport class LedgerMobileBridge\n implements LedgerBridge\n{\n #transportMiddleware?: TransportMiddleware;\n\n #opts: LedgerMobileBridgeOptions;\n\n isDeviceConnected = false;\n\n constructor(\n transportMiddleware: TransportMiddleware,\n opts: LedgerMobileBridgeOptions = {},\n ) {\n this.#opts = opts;\n this.#transportMiddleware = transportMiddleware;\n }\n\n /**\n * Method to initializes the keyring.\n * Mobile ledger doesnt not require init.\n */\n async init(): Promise {\n return Promise.resolve();\n }\n\n /**\n * Method to destroy the keyring.\n * It will dispose the transportmiddleware and set isDeviceConnected to false.\n */\n async destroy(): Promise {\n try {\n await this.#getTransportMiddleWare().dispose();\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n this.isDeviceConnected = false;\n }\n\n /**\n * Method to sign a string Message.\n * Sending the string message to the device and returning the signed message.\n *\n * @param params - The descriptor to open the transport with.\n * @param params.hdPath - The descriptor to open the transport with.\n * @param params.message - An optional timeout for the transport connection.\n * @returns Retrieve v, r, s from the signed message.\n */\n async deviceSignMessage({\n hdPath,\n message,\n }: LedgerSignMessageParams): Promise {\n return this.#getEthApp().signPersonalMessage(hdPath, message);\n }\n\n /**\n * Method to sign a EIP712 Message.\n * Sending the typed data message to the device and returning the signed message.\n *\n * @param params - An object contains hdPath, domainSeparatorHex and hashStructMessageHex.\n * @param params.hdPath - The BIP 32 path of the account.\n * @param params.domainSeparatorHex - The domain separator.\n * @param params.hashStructMessageHex - The hashed struct message.\n * @returns Retrieve v, r, s from the signed message.\n */\n async deviceSignTypedData({\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n }: LedgerSignTypedDataParams): Promise {\n return this.#getEthApp().signEIP712HashedMessage(\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n );\n }\n\n /**\n * Method to sign a transaction\n * Sending the hexadecimal transaction message to the device and returning the signed transaction.\n *\n * @param params - An object contains tx, hdPath.\n * @param params.tx - The raw ethereum transaction in hexadecimal to sign.\n * @param params.hdPath - The BIP 32 path of the account.\n * @returns Retrieve v, r, s from the signed transaction.\n */\n async deviceSignTransaction({\n tx,\n hdPath,\n }: LedgerSignTransactionParams): Promise {\n const resolution = await ledgerService.resolveTransaction(tx, {}, {});\n return this.#getEthApp().signTransaction(hdPath, tx, resolution);\n }\n\n /**\n * Method to retrieve the ethereum address for a given BIP 32 path.\n *\n * @param params - An object contains hdPath.\n * @param params.hdPath - The BIP 32 path of the account.\n * @returns An object contains publicKey, address and chainCode.\n */\n async getPublicKey({\n hdPath,\n }: GetPublicKeyParams): Promise {\n return await this.#getEthApp().getAddress(hdPath, false, true);\n }\n\n /**\n * Method to retrieve the current configuration.\n *\n * @returns Retrieve current configuration.\n */\n async getOptions(): Promise {\n return this.#opts;\n }\n\n /**\n * Method to set the current configuration.\n *\n * @param opts - An configuration object.\n */\n async setOptions(opts: LedgerMobileBridgeOptions): Promise {\n this.#opts = opts;\n }\n\n /**\n * Method to set the transport object to communicate with the device.\n *\n * @param transport - The communication interface with the Ledger hardware wallet. There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb).\n * @returns Retrieve boolean.\n */\n async updateTransportMethod(transport: Transport): Promise {\n if (!transport.deviceModel?.id) {\n throw new Error(\n 'Property `deviceModel.id` is not defined in `transport`.',\n );\n }\n this.#getTransportMiddleWare().setTransport(transport);\n this.isDeviceConnected = true;\n return Promise.resolve(true);\n }\n\n /**\n * Method to init eth app object on ledger device.\n * This method is not supported on mobile.\n */\n async attemptMakeApp(): Promise {\n throw new Error('Method not supported.');\n }\n\n /**\n * Method to open ethereum application on ledger device.\n *\n */\n async openEthApp(): Promise {\n await this.#getEthApp().openEthApp();\n }\n\n /**\n * Method to close all running application on ledger device.\n *\n */\n async closeApps(): Promise {\n await this.#getEthApp().closeApps();\n }\n\n /**\n * Method to retrieve the name and version of the running application in ledger device.\n *\n * @returns An object contains appName and version.\n */\n async getAppNameAndVersion(): Promise {\n return this.#getEthApp().getAppNameAndVersion();\n }\n\n /**\n * Method to retrieve the transport middleWare object.\n *\n * @returns The TransportMiddleware object.\n */\n #getTransportMiddleWare(): TransportMiddleware {\n if (this.#transportMiddleware) {\n return this.#transportMiddleware;\n }\n throw new Error('Instance `transportMiddleware` is not initialized.');\n }\n\n /**\n * Method to retrieve the ledger Eth App object.\n *\n * @returns The ledger Eth App object.\n */\n #getEthApp(): MetaMaskLedgerHwAppEth {\n return this.#getTransportMiddleWare().getEthApp();\n }\n}\n"]} \ No newline at end of file diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.d.ts new file mode 100644 From d376691eff17b27f6074dcefd2bdc09903801555 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 22 Apr 2024 04:11:23 +0100 Subject: [PATCH 04/75] fix: Rebase from main --- app/core/Engine.ts | 14 +++++++++----- app/util/hardwareWallet/hardwareWallets/ledger.ts | 5 ++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/core/Engine.ts b/app/core/Engine.ts index 61d396fdf9b..ac999b657d2 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -126,7 +126,7 @@ import { LedgerTransportMiddleware, } from '@metamask/eth-ledger-bridge-keyring'; -import Encryptor from './Encryptor'; +import { Encryptor, DERIVATION_PARAMS } from './Encryptor'; import { isMainnetByChainId, getDecimalChainId, @@ -176,7 +176,7 @@ import { import { hasProperty, Json } from '@metamask/utils'; // TODO: Export this type from the package directly import { SwapsState } from '@metamask/swaps-controller/dist/SwapsController'; -import { ethErrors } from 'eth-rpc-errors'; +import { providerErrors } from '@metamask/rpc-errors'; import { PPOM, ppomInit } from '../lib/ppom/PPOMView'; import RNFSStorageBackend from '../lib/ppom/ppom-storage-backend'; @@ -191,11 +191,13 @@ import { lowerCase } from 'lodash'; import { networkIdUpdated, networkIdWillUpdate, -} from '../core/redux/slices/inpageProvider'; +} from './redux/slices/inpageProvider'; const NON_EMPTY = 'NON_EMPTY'; -const encryptor = new Encryptor(); +const encryptor = new Encryptor({ + derivationParams: DERIVATION_PARAMS, +}); let currentChainId: any; ///: BEGIN:ONLY_INCLUDE_IF(snaps) @@ -209,6 +211,7 @@ interface TestOrigin { type: `${PhishingController['name']}:testOrigin`; handler: PhishingController['test']; } + type PhishingControllerActions = MaybeUpdateState | TestOrigin; type SnapsGlobalActions = @@ -347,6 +350,7 @@ class Engine { * Object that runs and manages the execution of Snaps */ snapExecutionService: WebViewExecutionService; + ///: END:ONLY_INCLUDE_IF /** @@ -1556,7 +1560,7 @@ class Engine { rejectPendingApproval( id: string, - reason: Error = ethErrors.provider.userRejectedRequest(), + reason: Error = providerErrors.userRejectedRequest(), opts: { ignoreMissing?: boolean; logErrors?: boolean } = {}, ) { const { ApprovalController } = this.context; diff --git a/app/util/hardwareWallet/hardwareWallets/ledger.ts b/app/util/hardwareWallet/hardwareWallets/ledger.ts index bee7d1b0d0a..d4d43321266 100644 --- a/app/util/hardwareWallet/hardwareWallets/ledger.ts +++ b/app/util/hardwareWallet/hardwareWallets/ledger.ts @@ -8,10 +8,9 @@ export interface LedgerSignModelNavParams { type: any; } -export const signModalNavDetail = async (params: LedgerSignModelNavParams) => { - return createNavigationDetails( +export const signModalNavDetail = async (params: LedgerSignModelNavParams) => + createNavigationDetails( Routes.LEDGER_MESSAGE_SIGN_MODAL, )({ ...params, }); -}; From 5eb781ccc00a31114d6aba573bf1ef5e2443fcba Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 22 Apr 2024 13:48:55 +0100 Subject: [PATCH 05/75] feat: add `package.tgz` to project so that project can build without the need of release of library. also fix some issue of missing deviceId in library cause the bluetooth unable to connect. --- .../hooks/Ledger/useLedgerBluetooth.ts | 11 +- app/core/Engine.ts | 11 +- app/core/Ledger/Ledger.ts | 23 +- ios/Podfile.lock | 6 +- package.json | 5 +- package.tgz | Bin 0 -> 33606 bytes ...mask+eth-ledger-bridge-keyring+3.0.0.patch | 680 ------------------ yarn.lock | 73 +- 8 files changed, 78 insertions(+), 731 deletions(-) create mode 100644 package.tgz delete mode 100644 patches/@metamask+eth-ledger-bridge-keyring+3.0.0.patch diff --git a/app/components/hooks/Ledger/useLedgerBluetooth.ts b/app/components/hooks/Ledger/useLedgerBluetooth.ts index 19c458b68d1..ebb79f245c3 100644 --- a/app/components/hooks/Ledger/useLedgerBluetooth.ts +++ b/app/components/hooks/Ledger/useLedgerBluetooth.ts @@ -33,7 +33,7 @@ const RESTART_LIMIT = 5; // Assumptions // 1. One big code block - logic all encapsulated in logicToRun // 2. logicToRun calls setUpBluetoothConnection -function useLedgerBluetooth(): UseLedgerBluetoothHook { +function useLedgerBluetooth(deviceId: string): UseLedgerBluetoothHook { // This is to track if we are expecting code to run or connection operational const [isSendingLedgerCommands, setIsSendingLedgerCommands] = useState(false); @@ -74,18 +74,16 @@ function useLedgerBluetooth(): UseLedgerBluetoothHook { // Sets up the Bluetooth transport const setUpBluetoothConnection = async () => { - if (transportRef.current) { + if (transportRef.current && deviceId) { setIsSendingLedgerCommands(true); } - if (!transportRef.current) { + if (!transportRef.current && deviceId) { try { const BluetoothTransport: any = await import( '@ledgerhq/react-native-hw-transport-ble' ); - - //TODO - check if this is the correct way to open the transport - transportRef.current = await BluetoothTransport.default.open(); + transportRef.current = await BluetoothTransport.default.open(deviceId); // eslint-disable-next-line @typescript-eslint/no-unused-vars transportRef.current?.on('disconnect', (e: any) => { transportRef.current = undefined; @@ -135,6 +133,7 @@ function useLedgerBluetooth(): UseLedgerBluetoothHook { // Initialise the keyring and check for pre-conditions (is the correct app running?) const appName = await connectLedgerHardware( transportRef.current as unknown as BleTransport, + deviceId, ); // BOLOS is the Ledger main screen app diff --git a/app/core/Engine.ts b/app/core/Engine.ts index ac999b657d2..35175cb183b 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -125,7 +125,6 @@ import { LedgerMobileBridge, LedgerTransportMiddleware, } from '@metamask/eth-ledger-bridge-keyring'; - import { Encryptor, DERIVATION_PARAMS } from './Encryptor'; import { isMainnetByChainId, @@ -191,7 +190,7 @@ import { lowerCase } from 'lodash'; import { networkIdUpdated, networkIdWillUpdate, -} from './redux/slices/inpageProvider'; +} from '../core/redux/slices/inpageProvider'; const NON_EMPTY = 'NON_EMPTY'; @@ -629,8 +628,12 @@ class Engine { const qrKeyringBuilder = () => new QRHardwareKeyring(); qrKeyringBuilder.type = QRHardwareKeyring.type; - const middleware = new LedgerTransportMiddleware(); - const bridge = new LedgerMobileBridge(middleware); + console.warn('Create Ledger Transport Middleware'); + + console.warn('Create LedgerMobileBridger'); + const bridge = new LedgerMobileBridge(new LedgerTransportMiddleware()); + + console.warn('Create LedgerKeyringBuilder', bridge); const ledgerKeyringBuilder = () => new LedgerKeyring({ bridge }); ledgerKeyringBuilder.type = LedgerKeyring.type; diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 85ddbe39fe3..c3e24905bfa 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -38,13 +38,17 @@ export const getLedgerKeyring = async (): Promise => { * Connects to the ledger device by requesting some metadata from it. * * @param transport - The transport to use to connect to the device + * @param deviceId - The device ID to connect to * @returns The name of the currently open application on the device */ export const connectLedgerHardware = async ( transport: BleTransport, + deviceId: string, ): Promise => { + console.warn('connectLedgerHardware'); const keyring = await getLedgerKeyring(); keyring.setHdPath("m/44'/60'/0'/0"); + keyring.setDeviceId(deviceId); const bridge = keyring.bridge as LedgerMobileBridge; await bridge.updateTransportMethod(transport); // keyring.setTransport(transport as unknown as any, deviceId); @@ -109,16 +113,15 @@ export const forgetLedger = async (): Promise => { PreferencesController.updateIdentities(await KeyringController.getAccounts()); }; -// /** -// * Get DeviceId from Ledger Keyring -// * -// * @returns The DeviceId -// */ -// export const getDeviceId = async (): Promise => { -// const ledgerKeyring = await getLedgerKeyring(); -// const bridge = ledgerKeyring.bridge as LedgerMobileBridge; -// return bridge.deviceId; -// }; +/** + * Get DeviceId from Ledger Keyring + * + * @returns The DeviceId + */ +export const getDeviceId = async (): Promise => { + const ledgerKeyring = await getLedgerKeyring(); + return ledgerKeyring.deviceId; +}; /** * signTypedMessage from Ledger Keyring diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 84d08bdef08..05bccf12088 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1060,7 +1060,7 @@ SPEC CHECKSUMS: lottie-react-native: 3e722c63015fdb9c27638b0a77969fc412067c18 MMKV: f902fb6719da13c2ab0965233d8963a59416f911 MMKVCore: d26e4d3edd5cb8588c2569222cbd8be4231374e9 - MultiplatformBleAdapter: b1fddd0d499b96b607e00f0faa8e60648343dc1d + MultiplatformBleAdapter: ea8bac405ec200d0ca9de0f89afef6f06fb2abbc nanopb: 438bc412db1928dac798aa6fd75726007be04262 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c Permission-BluetoothPeripheral: 247e379c9ecb4b1af2b87f73e4a15a00a5bc0c1f @@ -1139,7 +1139,7 @@ SPEC CHECKSUMS: RNReanimated: b1220a0e5168745283ff5d53bfc7d2144b2cee1b RNScreens: ea4cd3a853063cda19a4e3c28d2e52180c80f4eb RNSensors: c363d486c879e181905dea84a2535e49af1c2d25 - RNSentry: 1b70955af02f86efc3c9a09989f0d0695b0d237d + RNSentry: 98e170b6eedc5c54e3ac88b6140fffb8b496deb4 RNShare: f116bbb04f310c665ca483d0bd1e88cf59b3b334 RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f RNVectorIcons: 6607bd3a30291d0edb56f9bbe7ae411ee2b928b0 @@ -1154,4 +1154,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 6c1ebc1c49e824e5d559b52f89962406257a7f5c -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/package.json b/package.json index 5dab9092f2b..197894f94cf 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "@metamask/contract-metadata": "^2.1.0", "@metamask/controller-utils": "^5.0.0", "@metamask/design-tokens": "^2.0.0", - "@metamask/eth-ledger-bridge-keyring": "3.0.0", + "@metamask/eth-ledger-bridge-keyring": "file:./package.tgz", "@metamask/eth-sig-util": "^4.0.1", "@metamask/etherscan-link": "^2.0.0", "@metamask/gas-fee-controller": "^10.0.0", @@ -555,7 +555,8 @@ "ts-node>@swc/core": false, "@metamask/sdk-communication-layer>bufferutil": false, "@metamask/sdk-communication-layer>utf-8-validate": false, - "@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false + "@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false, + "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": false } } } diff --git a/package.tgz b/package.tgz new file mode 100644 index 0000000000000000000000000000000000000000..dc88d4864a78e44146950c95d3d0426f858606a5 GIT binary patch literal 33606 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 & { -- chainCode: string; --}; -+export declare type GetPublicKeyResponse = Awaited>; - export declare type LedgerSignTransactionParams = { - hdPath: string; - tx: string; -@@ -37,7 +36,7 @@ export declare type LedgerBridge = { - */ - setOptions(opts: T): Promise; - attemptMakeApp(): Promise; -- updateTransportMethod(transportType: string): Promise; -+ updateTransportMethod(transportType: string | Transport): Promise; - getPublicKey(params: GetPublicKeyParams): Promise; - deviceSignTransaction(params: LedgerSignTransactionParams): Promise; - deviceSignMessage(params: LedgerSignMessageParams): Promise; -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map -index 0932bdd..e02c7e8 100644 ---- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-bridge.js.map -@@ -1 +1 @@ --{"version":3,"file":"ledger-bridge.js","sourceRoot":"","sources":["../src/ledger-bridge.ts"],"names":[],"mappings":"","sourcesContent":["import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';\n\nexport type GetPublicKeyParams = { hdPath: string };\nexport type GetPublicKeyResponse = Awaited<\n ReturnType\n> & {\n chainCode: string;\n};\n\nexport type LedgerSignTransactionParams = { hdPath: string; tx: string };\nexport type LedgerSignTransactionResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignMessageParams = { hdPath: string; message: string };\nexport type LedgerSignMessageResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTypedDataParams = {\n hdPath: string;\n domainSeparatorHex: string;\n hashStructMessageHex: string;\n};\nexport type LedgerSignTypedDataResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerBridgeOptions = Record;\n\nexport type LedgerBridge = {\n isDeviceConnected: boolean;\n\n init(): Promise;\n\n destroy(): Promise;\n\n /**\n * Method to get the current configuration of the ledger bridge keyring.\n */\n getOptions(): Promise;\n\n /**\n * Method to set the current configuration of the ledger bridge keyring.\n *\n * @param opts - An object contains configuration of the bridge.\n */\n setOptions(opts: T): Promise;\n\n attemptMakeApp(): Promise;\n\n updateTransportMethod(transportType: string): Promise;\n\n getPublicKey(params: GetPublicKeyParams): Promise;\n\n deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise;\n\n deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise;\n\n deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise;\n};\n"]} -\ No newline at end of file -+{"version":3,"file":"ledger-bridge.js","sourceRoot":"","sources":["../src/ledger-bridge.ts"],"names":[],"mappings":"","sourcesContent":["import type LedgerHwAppEth from '@ledgerhq/hw-app-eth';\nimport type Transport from '@ledgerhq/hw-transport';\nexport type GetPublicKeyParams = { hdPath: string };\nexport type GetPublicKeyResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTransactionParams = { hdPath: string; tx: string };\nexport type LedgerSignTransactionResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignMessageParams = { hdPath: string; message: string };\nexport type LedgerSignMessageResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerSignTypedDataParams = {\n hdPath: string;\n domainSeparatorHex: string;\n hashStructMessageHex: string;\n};\nexport type LedgerSignTypedDataResponse = Awaited<\n ReturnType\n>;\n\nexport type LedgerBridgeOptions = Record;\n\nexport type LedgerBridge = {\n isDeviceConnected: boolean;\n\n init(): Promise;\n\n destroy(): Promise;\n\n /**\n * Method to get the current configuration of the ledger bridge keyring.\n */\n getOptions(): Promise;\n\n /**\n * Method to set the current configuration of the ledger bridge keyring.\n *\n * @param opts - An object contains configuration of the bridge.\n */\n setOptions(opts: T): Promise;\n\n attemptMakeApp(): Promise;\n\n updateTransportMethod(transportType: string | Transport): Promise;\n\n getPublicKey(params: GetPublicKeyParams): Promise;\n\n deviceSignTransaction(\n params: LedgerSignTransactionParams,\n ): Promise;\n\n deviceSignMessage(\n params: LedgerSignMessageParams,\n ): Promise;\n\n deviceSignTypedData(\n params: LedgerSignTypedDataParams,\n ): Promise;\n};\n"]} -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js -index cd18e51..13ed60d 100644 ---- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js -@@ -295,7 +295,11 @@ class LedgerKeyring extends events_1.EventEmitter { - if (recoveryId.length < 2) { - recoveryId = `0${recoveryId}`; - } -- const signature = `0x${payload.r}${payload.s}${recoveryId}`; -+ let modifiedV = parseInt(String(payload.v), 10).toString(16); -+ if (modifiedV.length < 2) { -+ modifiedV = `0${modifiedV}`; -+ } -+ const signature = `0x${payload.r}${payload.s}${modifiedV}`; - const addressSignedWith = (0, eth_sig_util_1.recoverPersonalSignature)({ - data: message, - signature, -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js.map -index 2b458a0..c02e4ce 100644 ---- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js.map -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-keyring.js.map -@@ -1 +1 @@ --{"version":3,"file":"ledger-keyring.js","sourceRoot":"","sources":["../src/ledger-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { RLP } from '@ethereumjs/rlp';\nimport { TransactionFactory, TxData, TypedTransaction } from '@ethereumjs/tx';\nimport * as ethUtil from '@ethereumjs/util';\nimport type { MessageTypes, TypedMessage } from '@metamask/eth-sig-util';\nimport {\n recoverPersonalSignature,\n recoverTypedSignature,\n SignTypedDataVersion,\n TypedDataUtils,\n} from '@metamask/eth-sig-util';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { Buffer } from 'buffer';\nimport type OldEthJsTransaction from 'ethereumjs-tx';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { EventEmitter } from 'events';\nimport HDKey from 'hdkey';\n\nimport { LedgerBridge, LedgerBridgeOptions } from './ledger-bridge';\n\nconst pathBase = 'm';\nconst hdPathString = `${pathBase}/44'/60'/0'`;\nconst keyringType = 'Ledger Hardware';\n\nconst MAX_INDEX = 1000;\n\nenum NetworkApiUrls {\n Ropsten = 'http://api-ropsten.etherscan.io',\n Kovan = 'http://api-kovan.etherscan.io',\n Rinkeby = 'https://api-rinkeby.etherscan.io',\n Mainnet = 'https://api.etherscan.io',\n}\n\ntype SignTransactionPayload = Awaited<\n ReturnType['deviceSignTransaction']>\n>;\n\nexport type AccountDetails = {\n index?: number;\n bip44?: boolean;\n hdPath?: string;\n};\n\nexport type LedgerBridgeKeyringOptions = {\n hdPath: string;\n accounts: readonly string[];\n accountDetails: Readonly>;\n accountIndexes: Readonly>;\n implementFullBIP44: boolean;\n};\n\n/**\n * Check if the given transaction is made with ethereumjs-tx or @ethereumjs/tx\n *\n * Transactions built with older versions of ethereumjs-tx have a\n * getChainId method that newer versions do not.\n * Older versions are mutable\n * while newer versions default to being immutable.\n * Expected shape and type\n * of data for v, r and s differ (Buffer (old) vs BN (new)).\n *\n * @param tx - Transaction to check, instance of either ethereumjs-tx or @ethereumjs/tx.\n * @returns Returns `true` if tx is an old-style ethereumjs-tx transaction.\n */\nfunction isOldStyleEthereumjsTx(\n tx: TypedTransaction | OldEthJsTransaction,\n): tx is OldEthJsTransaction {\n return 'getChainId' in tx && typeof tx.getChainId === 'function';\n}\n\nexport class LedgerKeyring extends EventEmitter {\n static type: string = keyringType;\n\n readonly type: string = keyringType;\n\n page = 0;\n\n perPage = 5;\n\n unlockedAccount = 0;\n\n accounts: readonly string[] = [];\n\n accountDetails: Record = {};\n\n hdk = new HDKey();\n\n hdPath = hdPathString;\n\n paths: Record = {};\n\n network: NetworkApiUrls = NetworkApiUrls.Mainnet;\n\n implementFullBIP44 = false;\n\n bridge: LedgerBridge;\n\n constructor({ bridge }: { bridge: LedgerBridge }) {\n super();\n\n if (!bridge) {\n throw new Error('Bridge is a required dependency for the keyring');\n }\n\n this.bridge = bridge;\n }\n\n async init() {\n return this.bridge.init();\n }\n\n async destroy() {\n return this.bridge.destroy();\n }\n\n async serialize() {\n return {\n hdPath: this.hdPath,\n accounts: this.accounts,\n accountDetails: this.accountDetails,\n implementFullBIP44: false,\n };\n }\n\n async deserialize(opts: Partial = {}) {\n this.hdPath = opts.hdPath ?? hdPathString;\n this.accounts = opts.accounts ?? [];\n this.accountDetails = opts.accountDetails ?? {};\n\n if (!opts.accountDetails) {\n this.#migrateAccountDetails(opts);\n }\n\n this.implementFullBIP44 = opts.implementFullBIP44 ?? false;\n\n const keys = new Set(Object.keys(this.accountDetails));\n // Remove accounts that don't have corresponding account details\n this.accounts = this.accounts.filter((account) =>\n keys.has(ethUtil.toChecksumAddress(account)),\n );\n\n return Promise.resolve();\n }\n\n #migrateAccountDetails(opts: Partial) {\n if (this.#isLedgerLiveHdPath() && opts.accountIndexes) {\n for (const [account, index] of Object.entries(opts.accountIndexes)) {\n this.accountDetails[account] = {\n bip44: true,\n hdPath: this.#getPathForIndex(index),\n };\n }\n }\n const keys = new Set(Object.keys(this.accountDetails));\n // try to migrate non-LedgerLive accounts too\n if (!this.#isLedgerLiveHdPath()) {\n this.accounts.forEach((account) => {\n try {\n const key = ethUtil.toChecksumAddress(account);\n\n if (!keys.has(key)) {\n this.accountDetails[key] = {\n bip44: false,\n hdPath: this.#pathFromAddress(account),\n };\n }\n } catch (error) {\n console.log(`failed to migrate account ${account}`);\n }\n });\n }\n }\n\n isUnlocked() {\n return Boolean(this.hdk?.publicKey);\n }\n\n isConnected() {\n return this.bridge.isDeviceConnected;\n }\n\n setAccountToUnlock(index: number | string) {\n this.unlockedAccount =\n typeof index === 'number' ? index : parseInt(index, 10);\n }\n\n setHdPath(hdPath: string) {\n // Reset HDKey if the path changes\n if (this.hdPath !== hdPath) {\n this.hdk = new HDKey();\n }\n this.hdPath = hdPath;\n }\n\n async unlock(hdPath?: string, updateHdk = true): Promise {\n if (this.isUnlocked() && !hdPath) {\n return 'already unlocked';\n }\n const path = hdPath ? this.#toLedgerPath(hdPath) : this.hdPath;\n\n let payload;\n try {\n payload = await this.bridge.getPublicKey({\n hdPath: path,\n });\n } catch (error) {\n throw error instanceof Error ? error : new Error('Unknown error');\n }\n\n if (updateHdk && payload.chainCode) {\n this.hdk.publicKey = Buffer.from(payload.publicKey, 'hex');\n this.hdk.chainCode = Buffer.from(payload.chainCode, 'hex');\n }\n\n return payload.address;\n }\n\n async addAccounts(amount = 1): Promise {\n return new Promise((resolve, reject) => {\n this.unlock()\n .then(async (_) => {\n const from = this.unlockedAccount;\n const to = from + amount;\n for (let i = from; i < to; i++) {\n const path = this.#getPathForIndex(i);\n let address;\n if (this.#isLedgerLiveHdPath()) {\n address = await this.unlock(path);\n } else {\n address = this.#addressFromIndex(pathBase, i);\n }\n\n this.accountDetails[ethUtil.toChecksumAddress(address)] = {\n // TODO: consider renaming this property, as the current name is misleading\n // It's currently used to represent whether an account uses the Ledger Live path.\n bip44: this.#isLedgerLiveHdPath(),\n hdPath: path,\n };\n\n if (!this.accounts.includes(address)) {\n this.accounts = [...this.accounts, address];\n }\n this.page = 0;\n }\n resolve(this.accounts.slice());\n })\n .catch(reject);\n });\n }\n\n async getFirstPage() {\n this.page = 0;\n return this.#getPage(1);\n }\n\n async getNextPage() {\n return this.#getPage(1);\n }\n\n async getPreviousPage() {\n return this.#getPage(-1);\n }\n\n async getAccounts() {\n return Promise.resolve(this.accounts.slice());\n }\n\n removeAccount(address: string) {\n const filteredAccounts = this.accounts.filter(\n (a) => a.toLowerCase() !== address.toLowerCase(),\n );\n\n if (filteredAccounts.length === this.accounts.length) {\n throw new Error(`Address ${address} not found in this keyring`);\n }\n\n this.accounts = filteredAccounts;\n delete this.accountDetails[ethUtil.toChecksumAddress(address)];\n }\n\n async attemptMakeApp() {\n return this.bridge.attemptMakeApp();\n }\n\n async updateTransportMethod(transportType: string) {\n return this.bridge.updateTransportMethod(transportType);\n }\n\n // tx is an instance of the ethereumjs-transaction class.\n async signTransaction(\n address: string,\n tx: TypedTransaction | OldEthJsTransaction,\n ): Promise {\n let rawTxHex;\n // transactions built with older versions of ethereumjs-tx have a\n // getChainId method that newer versions do not. Older versions are mutable\n // while newer versions default to being immutable. Expected shape and type\n // of data for v, r and s differ (Buffer (old) vs BN (new))\n if (isOldStyleEthereumjsTx(tx)) {\n // In this version of ethereumjs-tx we must add the chainId in hex format\n // to the initial v value. The chainId must be included in the serialized\n // transaction which is only communicated to ethereumjs-tx in this\n // value. In newer versions the chainId is communicated via the 'Common'\n // object.\n // @ts-expect-error tx.v should be a Buffer but we are assigning a string\n tx.v = ethUtil.bufferToHex(tx.getChainId());\n // @ts-expect-error tx.r should be a Buffer but we are assigning a string\n tx.r = '0x00';\n // @ts-expect-error tx.s should be a Buffer but we are assigning a string\n tx.s = '0x00';\n\n rawTxHex = tx.serialize().toString('hex');\n\n return this.#signTransaction(address, rawTxHex, (payload) => {\n tx.v = Buffer.from(payload.v, 'hex');\n tx.r = Buffer.from(payload.r, 'hex');\n tx.s = Buffer.from(payload.s, 'hex');\n return tx;\n });\n }\n\n // The below `encode` call is only necessary for legacy transactions, as `getMessageToSign`\n // calls `rlp.encode` internally for non-legacy transactions. As per the \"Transaction Execution\"\n // section of the ethereum yellow paper, transactions need to be \"well-formed RLP, with no additional\n // trailing bytes\".\n\n // Note also that `getMessageToSign` will return valid RLP for all transaction types, whereas the\n // `serialize` method will not for any transaction type except legacy. This is because `serialize` includes\n // empty r, s and v values in the encoded rlp. This is why we use `getMessageToSign` here instead of `serialize`.\n const messageToSign = tx.getMessageToSign(false);\n\n rawTxHex = Buffer.isBuffer(messageToSign)\n ? messageToSign.toString('hex')\n : Buffer.from(RLP.encode(messageToSign)).toString('hex');\n\n return this.#signTransaction(address, rawTxHex, (payload) => {\n // Because tx will be immutable, first get a plain javascript object that\n // represents the transaction. Using txData here as it aligns with the\n // nomenclature of ethereumjs/tx.\n const txData: TxData = tx.toJSON();\n // The fromTxData utility expects a type to support transactions with a type other than 0\n txData.type = tx.type;\n // The fromTxData utility expects v,r and s to be hex prefixed\n txData.v = ethUtil.addHexPrefix(payload.v);\n txData.r = ethUtil.addHexPrefix(payload.r);\n txData.s = ethUtil.addHexPrefix(payload.s);\n // Adopt the 'common' option from the original transaction and set the\n // returned object to be frozen if the original is frozen.\n return TransactionFactory.fromTxData(txData, {\n common: tx.common,\n freeze: Object.isFrozen(tx),\n });\n });\n }\n\n async #signTransaction(\n address: string,\n rawTxHex: string,\n handleSigning: (\n payload: SignTransactionPayload,\n ) => TypedTransaction | OldEthJsTransaction,\n ): Promise {\n const hdPath = await this.unlockAccountByAddress(address);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing transaction');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignTransaction({\n tx: rawTxHex,\n hdPath,\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing transaction');\n }\n\n const newOrMutatedTx = handleSigning(payload);\n const valid = newOrMutatedTx.verifySignature();\n if (valid) {\n return newOrMutatedTx;\n }\n throw new Error('Ledger: The transaction signature is not valid');\n }\n\n async signMessage(withAccount: string, data: string) {\n return this.signPersonalMessage(withAccount, data);\n }\n\n // For personal_sign, we need to prefix the message:\n async signPersonalMessage(withAccount: string, message: string) {\n const hdPath = await this.unlockAccountByAddress(withAccount);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing message');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignMessage({\n hdPath,\n message: ethUtil.stripHexPrefix(message),\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing message');\n }\n\n let recoveryId = parseInt(String(payload.v), 10).toString(16);\n if (recoveryId.length < 2) {\n recoveryId = `0${recoveryId}`;\n }\n const signature = `0x${payload.r}${payload.s}${recoveryId}`;\n const addressSignedWith = recoverPersonalSignature({\n data: message,\n signature,\n });\n if (\n ethUtil.toChecksumAddress(addressSignedWith) !==\n ethUtil.toChecksumAddress(withAccount)\n ) {\n throw new Error('Ledger: The signature doesnt match the right address');\n }\n return signature;\n }\n\n async unlockAccountByAddress(address: string) {\n const checksummedAddress = ethUtil.toChecksumAddress(address);\n const accountDetails = this.accountDetails[checksummedAddress];\n if (!accountDetails) {\n throw new Error(\n `Ledger: Account for address '${checksummedAddress}' not found`,\n );\n }\n const { hdPath } = accountDetails;\n const unlockedAddress = await this.unlock(hdPath, false);\n\n // unlock resolves to the address for the given hdPath as reported by the ledger device\n // if that address is not the requested address, then this account belongs to a different device or seed\n if (unlockedAddress.toLowerCase() !== address.toLowerCase()) {\n throw new Error(\n `Ledger: Account ${address} does not belong to the connected device`,\n );\n }\n return hdPath;\n }\n\n async signTypedData(\n withAccount: string,\n data: TypedMessage,\n options: { version?: string } = {},\n ) {\n const isV4 = options.version === 'V4';\n if (!isV4) {\n throw new Error(\n 'Ledger: Only version 4 of typed data signing is supported',\n );\n }\n\n const { domain, types, primaryType, message } =\n TypedDataUtils.sanitizeData(data);\n const domainSeparatorHex = TypedDataUtils.hashStruct(\n 'EIP712Domain',\n domain,\n types,\n SignTypedDataVersion.V4,\n ).toString('hex');\n const hashStructMessageHex = TypedDataUtils.hashStruct(\n primaryType.toString(),\n message,\n types,\n SignTypedDataVersion.V4,\n ).toString('hex');\n\n const hdPath = await this.unlockAccountByAddress(withAccount);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing message');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignTypedData({\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing message');\n }\n\n let recoveryId = parseInt(String(payload.v), 10).toString(16);\n if (recoveryId.length < 2) {\n recoveryId = `0${recoveryId}`;\n }\n const signature = `0x${payload.r}${payload.s}${recoveryId}`;\n const addressSignedWith = recoverTypedSignature({\n data,\n signature,\n version: SignTypedDataVersion.V4,\n });\n if (\n ethUtil.toChecksumAddress(addressSignedWith) !==\n ethUtil.toChecksumAddress(withAccount)\n ) {\n throw new Error('Ledger: The signature doesnt match the right address');\n }\n return signature;\n }\n\n exportAccount() {\n throw new Error('Not supported on this device');\n }\n\n forgetDevice() {\n this.accounts = [];\n this.page = 0;\n this.unlockedAccount = 0;\n this.paths = {};\n this.accountDetails = {};\n this.hdk = new HDKey();\n }\n\n /* PRIVATE METHODS */\n async #getPage(increment: number) {\n this.page += increment;\n\n if (this.page <= 0) {\n this.page = 1;\n }\n const from = (this.page - 1) * this.perPage;\n const to = from + this.perPage;\n\n await this.unlock();\n let accounts;\n if (this.#isLedgerLiveHdPath()) {\n accounts = await this.#getAccountsBIP44(from, to);\n } else {\n accounts = this.#getAccountsLegacy(from, to);\n }\n return accounts;\n }\n\n async #getAccountsBIP44(from: number, to: number) {\n const accounts: {\n address: string;\n balance: number | null;\n index: number;\n }[] = [];\n\n for (let i = from; i < to; i++) {\n const path = this.#getPathForIndex(i);\n const address = await this.unlock(path);\n const valid = this.implementFullBIP44\n ? await this.#hasPreviousTransactions(address)\n : true;\n accounts.push({\n address,\n balance: null,\n index: i,\n });\n\n // PER BIP44\n // \"Software should prevent a creation of an account if\n // a previous account does not have a transaction history\n // (meaning none of its addresses have been used before).\"\n if (!valid) {\n break;\n }\n }\n return accounts;\n }\n\n #getAccountsLegacy(from: number, to: number) {\n const accounts: {\n address: string;\n balance: number | null;\n index: number;\n }[] = [];\n\n for (let i = from; i < to; i++) {\n const address = this.#addressFromIndex(pathBase, i);\n accounts.push({\n address,\n balance: null,\n index: i,\n });\n this.paths[ethUtil.toChecksumAddress(address)] = i;\n }\n return accounts;\n }\n\n #addressFromIndex(basePath: string, i: number) {\n const dkey = this.hdk.derive(`${basePath}/${i}`);\n const address = ethUtil\n .publicToAddress(dkey.publicKey, true)\n .toString('hex');\n return ethUtil.toChecksumAddress(`0x${address}`);\n }\n\n #pathFromAddress(address: string) {\n const checksummedAddress = ethUtil.toChecksumAddress(address);\n let index = this.paths[checksummedAddress];\n if (typeof index === 'undefined') {\n for (let i = 0; i < MAX_INDEX; i++) {\n if (checksummedAddress === this.#addressFromIndex(pathBase, i)) {\n index = i;\n break;\n }\n }\n }\n\n if (typeof index === 'undefined') {\n throw new Error('Unknown address');\n }\n return this.#getPathForIndex(index);\n }\n\n #getPathForIndex(index: number) {\n // Check if the path is BIP 44 (Ledger Live)\n return this.#isLedgerLiveHdPath()\n ? `m/44'/60'/${index}'/0/0`\n : `${this.hdPath}/${index}`;\n }\n\n #isLedgerLiveHdPath() {\n return this.hdPath === `m/44'/60'/0'/0/0`;\n }\n\n #toLedgerPath(path: string) {\n return path.toString().replace('m/', '');\n }\n\n async #hasPreviousTransactions(address: string) {\n const apiUrl = this.#getApiUrl();\n const response = await window.fetch(\n `${apiUrl}/api?module=account&action=txlist&address=${address}&tag=latest&page=1&offset=1`,\n );\n const parsedResponse = await response.json();\n if (parsedResponse.status !== '0' && parsedResponse.result.length > 0) {\n return true;\n }\n return false;\n }\n\n #getApiUrl() {\n return this.network;\n }\n}\n"]} -\ No newline at end of file -+{"version":3,"file":"ledger-keyring.js","sourceRoot":"","sources":["../src/ledger-keyring.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAsC;AACtC,uCAA8E;AAC9E,0DAA4C;AAE5C,yDAKgC;AAChC,oDAAoD;AACpD,mCAAgC;AAEhC,oDAAoD;AACpD,mCAAsC;AACtC,kDAA0B;AAI1B,MAAM,QAAQ,GAAG,GAAG,CAAC;AACrB,MAAM,YAAY,GAAG,GAAG,QAAQ,aAAa,CAAC;AAC9C,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAEtC,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB,IAAK,cAKJ;AALD,WAAK,cAAc;IACjB,6DAA2C,CAAA;IAC3C,yDAAuC,CAAA;IACvC,8DAA4C,CAAA;IAC5C,sDAAoC,CAAA;AACtC,CAAC,EALI,cAAc,KAAd,cAAc,QAKlB;AAoBD;;;;;;;;;;;;GAYG;AACH,SAAS,sBAAsB,CAC7B,EAA0C;IAE1C,OAAO,YAAY,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,UAAU,KAAK,UAAU,CAAC;AACnE,CAAC;AAED,MAAa,aAAc,SAAQ,qBAAY;IA2B7C,YAAY,EAAE,MAAM,EAAiD;QACnE,KAAK,EAAE,CAAC;;QAzBD,SAAI,GAAW,WAAW,CAAC;QAEpC,SAAI,GAAG,CAAC,CAAC;QAET,YAAO,GAAG,CAAC,CAAC;QAEZ,oBAAe,GAAG,CAAC,CAAC;QAEpB,aAAQ,GAAsB,EAAE,CAAC;QAEjC,mBAAc,GAAmC,EAAE,CAAC;QAEpD,QAAG,GAAG,IAAI,eAAK,EAAE,CAAC;QAElB,WAAM,GAAG,YAAY,CAAC;QAEtB,UAAK,GAA2B,EAAE,CAAC;QAEnC,YAAO,GAAmB,cAAc,CAAC,OAAO,CAAC;QAEjD,uBAAkB,GAAG,KAAK,CAAC;QAOzB,IAAI,CAAC,MAAM,EAAE;YACX,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;SACpE;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,OAAO;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,SAAS;QACb,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,kBAAkB,EAAE,KAAK;SAC1B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAA4C,EAAE;;QAC9D,IAAI,CAAC,MAAM,GAAG,MAAA,IAAI,CAAC,MAAM,mCAAI,YAAY,CAAC;QAC1C,IAAI,CAAC,QAAQ,GAAG,MAAA,IAAI,CAAC,QAAQ,mCAAI,EAAE,CAAC;QACpC,IAAI,CAAC,cAAc,GAAG,MAAA,IAAI,CAAC,cAAc,mCAAI,EAAE,CAAC;QAEhD,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;YACxB,uBAAA,IAAI,sEAAuB,MAA3B,IAAI,EAAwB,IAAI,CAAC,CAAC;SACnC;QAED,IAAI,CAAC,kBAAkB,GAAG,MAAA,IAAI,CAAC,kBAAkB,mCAAI,KAAK,CAAC;QAE3D,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;QAC/D,gEAAgE;QAChE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAC/C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAC7C,CAAC;QAEF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IA+BD,UAAU;;QACR,OAAO,OAAO,CAAC,MAAA,IAAI,CAAC,GAAG,0CAAE,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC;IACvC,CAAC;IAED,kBAAkB,CAAC,KAAsB;QACvC,IAAI,CAAC,eAAe;YAClB,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,SAAS,CAAC,MAAc;QACtB,kCAAkC;QAClC,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE;YAC1B,IAAI,CAAC,GAAG,GAAG,IAAI,eAAK,EAAE,CAAC;SACxB;QACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAe,EAAE,SAAS,GAAG,IAAI;QAC5C,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE;YAChC,OAAO,kBAAkB,CAAC;SAC3B;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,uBAAA,IAAI,6DAAc,MAAlB,IAAI,EAAe,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QAE/D,IAAI,OAAO,CAAC;QACZ,IAAI;YACF,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;gBACvC,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;SACJ;QAAC,OAAO,KAAK,EAAE;YACd,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;SACnE;QAED,IAAI,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE;YAClC,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAC3D,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;SAC5D;QAED,OAAO,OAAO,CAAC,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;QAC1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,MAAM,EAAE;iBACV,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;gBAChB,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC;gBAClC,MAAM,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC;gBACzB,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;oBAC9B,MAAM,IAAI,GAAG,uBAAA,IAAI,gEAAiB,MAArB,IAAI,EAAkB,CAAC,CAAC,CAAC;oBACtC,IAAI,OAAO,CAAC;oBACZ,IAAI,uBAAA,IAAI,mEAAoB,MAAxB,IAAI,CAAsB,EAAE;wBAC9B,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;qBACnC;yBAAM;wBACL,OAAO,GAAG,uBAAA,IAAI,iEAAkB,MAAtB,IAAI,EAAmB,QAAQ,EAAE,CAAC,CAAC,CAAC;qBAC/C;oBAED,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,GAAG;wBACxD,2EAA2E;wBAC3E,iFAAiF;wBACjF,KAAK,EAAE,uBAAA,IAAI,mEAAoB,MAAxB,IAAI,CAAsB;wBACjC,MAAM,EAAE,IAAI;qBACb,CAAC;oBAEF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;wBACpC,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;qBAC7C;oBACD,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;iBACf;gBACD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;YACjC,CAAC,CAAC;iBACD,KAAK,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACd,OAAO,uBAAA,IAAI,wDAAS,MAAb,IAAI,EAAU,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,uBAAA,IAAI,wDAAS,MAAb,IAAI,EAAU,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,OAAO,uBAAA,IAAI,wDAAS,MAAb,IAAI,EAAU,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,aAAa,CAAC,OAAe;QAC3B,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CACjD,CAAC;QAEF,IAAI,gBAAgB,CAAC,MAAM,KAAK,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;YACpD,MAAM,IAAI,KAAK,CAAC,WAAW,OAAO,4BAA4B,CAAC,CAAC;SACjE;QAED,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC;QACjC,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,aAAqB;QAC/C,OAAO,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,aAAa,CAAC,CAAC;IAC1D,CAAC;IAED,yDAAyD;IACzD,KAAK,CAAC,eAAe,CACnB,OAAe,EACf,EAA0C;QAE1C,IAAI,QAAQ,CAAC;QACb,iEAAiE;QACjE,2EAA2E;QAC3E,2EAA2E;QAC3E,2DAA2D;QAC3D,IAAI,sBAAsB,CAAC,EAAE,CAAC,EAAE;YAC9B,yEAAyE;YACzE,yEAAyE;YACzE,kEAAkE;YAClE,wEAAwE;YACxE,UAAU;YACV,yEAAyE;YACzE,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;YAC5C,yEAAyE;YACzE,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC;YACd,yEAAyE;YACzE,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC;YAEd,QAAQ,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAE1C,OAAO,uBAAA,IAAI,gEAAiB,MAArB,IAAI,EAAkB,OAAO,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE;gBAC1D,EAAE,CAAC,CAAC,GAAG,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACrC,EAAE,CAAC,CAAC,GAAG,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACrC,EAAE,CAAC,CAAC,GAAG,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACrC,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;SACJ;QAED,2FAA2F;QAC3F,gGAAgG;QAChG,qGAAqG;QACrG,mBAAmB;QAEnB,iGAAiG;QACjG,2GAA2G;QAC3G,iHAAiH;QACjH,MAAM,aAAa,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAEjD,QAAQ,GAAG,eAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;YACvC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC/B,CAAC,CAAC,eAAM,CAAC,IAAI,CAAC,SAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAE3D,OAAO,uBAAA,IAAI,gEAAiB,MAArB,IAAI,EAAkB,OAAO,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE;YAC1D,yEAAyE;YACzE,sEAAsE;YACtE,iCAAiC;YACjC,MAAM,MAAM,GAAW,EAAE,CAAC,MAAM,EAAE,CAAC;YACnC,yFAAyF;YACzF,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC;YACtB,8DAA8D;YAC9D,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC3C,sEAAsE;YACtE,0DAA0D;YAC1D,OAAO,uBAAkB,CAAC,UAAU,CAAC,MAAM,EAAE;gBAC3C,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;aAC5B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAmCD,KAAK,CAAC,WAAW,CAAC,WAAmB,EAAE,IAAY;QACjD,OAAO,IAAI,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC;IAED,oDAAoD;IACpD,KAAK,CAAC,mBAAmB,CAAC,WAAmB,EAAE,OAAe;QAC5D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC;QAE9D,IAAI,CAAC,MAAM,EAAE;YACX,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;SAChE;QAED,IAAI,OAAO,CAAC;QACZ,IAAI;YACF,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC;gBAC5C,MAAM;gBACN,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC;aACzC,CAAC,CAAC;SACJ;QAAC,OAAO,KAAK,EAAE;YACd,MAAM,KAAK,YAAY,KAAK;gBAC1B,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;SAC9D;QAED,IAAI,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;YACzB,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;SAC/B;QAED,IAAI,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7D,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE;YACxB,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;SAC7B;QAED,MAAM,SAAS,GAAG,KAAK,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,EAAE,CAAC;QAC3D,MAAM,iBAAiB,GAAG,IAAA,uCAAwB,EAAC;YACjD,IAAI,EAAE,OAAO;YACb,SAAS;SACV,CAAC,CAAC;QACH,IACE,OAAO,CAAC,iBAAiB,CAAC,iBAAiB,CAAC;YAC5C,OAAO,CAAC,iBAAiB,CAAC,WAAW,CAAC,EACtC;YACA,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;SACzE;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,OAAe;QAC1C,MAAM,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC9D,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;QAC/D,IAAI,CAAC,cAAc,EAAE;YACnB,MAAM,IAAI,KAAK,CACb,gCAAgC,kBAAkB,aAAa,CAChE,CAAC;SACH;QACD,MAAM,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC;QAClC,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAEzD,uFAAuF;QACvF,wGAAwG;QACxG,IAAI,eAAe,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,EAAE;YAC3D,MAAM,IAAI,KAAK,CACb,mBAAmB,OAAO,0CAA0C,CACrE,CAAC;SACH;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,WAAmB,EACnB,IAAqB,EACrB,UAAgC,EAAE;QAElC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC;QACtC,IAAI,CAAC,IAAI,EAAE;YACT,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;SACH;QAED,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,GAC3C,6BAAc,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,kBAAkB,GAAG,6BAAc,CAAC,UAAU,CAClD,cAAc,EACd,MAAM,EACN,KAAK,EACL,mCAAoB,CAAC,EAAE,CACxB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,MAAM,oBAAoB,GAAG,6BAAc,CAAC,UAAU,CACpD,WAAW,CAAC,QAAQ,EAAE,EACtB,OAAO,EACP,KAAK,EACL,mCAAoB,CAAC,EAAE,CACxB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAElB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC;QAE9D,IAAI,CAAC,MAAM,EAAE;YACX,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;SAChE;QAED,IAAI,OAAO,CAAC;QACZ,IAAI;YACF,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC;gBAC9C,MAAM;gBACN,kBAAkB;gBAClB,oBAAoB;aACrB,CAAC,CAAC;SACJ;QAAC,OAAO,KAAK,EAAE;YACd,MAAM,KAAK,YAAY,KAAK;gBAC1B,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;SAC9D;QAED,IAAI,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;YACzB,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;SAC/B;QACD,MAAM,SAAS,GAAG,KAAK,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,UAAU,EAAE,CAAC;QAC5D,MAAM,iBAAiB,GAAG,IAAA,oCAAqB,EAAC;YAC9C,IAAI;YACJ,SAAS;YACT,OAAO,EAAE,mCAAoB,CAAC,EAAE;SACjC,CAAC,CAAC;QACH,IACE,OAAO,CAAC,iBAAiB,CAAC,iBAAiB,CAAC;YAC5C,OAAO,CAAC,iBAAiB,CAAC,WAAW,CAAC,EACtC;YACA,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;SACzE;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,aAAa;QACX,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,YAAY;QACV,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,GAAG,IAAI,eAAK,EAAE,CAAC;IACzB,CAAC;;AA/cH,sCA8kBC;+HApgBwB,IAAyC;IAC9D,IAAI,uBAAA,IAAI,mEAAoB,MAAxB,IAAI,CAAsB,IAAI,IAAI,CAAC,cAAc,EAAE;QACrD,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE;YAClE,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG;gBAC7B,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,uBAAA,IAAI,gEAAiB,MAArB,IAAI,EAAkB,KAAK,CAAC;aACrC,CAAC;SACH;KACF;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IAC/D,6CAA6C;IAC7C,IAAI,CAAC,uBAAA,IAAI,mEAAoB,MAAxB,IAAI,CAAsB,EAAE;QAC/B,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAChC,IAAI;gBACF,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;gBAE/C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;oBAClB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG;wBACzB,KAAK,EAAE,KAAK;wBACZ,MAAM,EAAE,uBAAA,IAAI,gEAAiB,MAArB,IAAI,EAAkB,OAAO,CAAC;qBACvC,CAAC;iBACH;aACF;YAAC,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,GAAG,CAAC,6BAA6B,OAAO,EAAE,CAAC,CAAC;aACrD;QACH,CAAC,CAAC,CAAC;KACJ;AACH,CAAC,mCAwLD,KAAK,yCACH,OAAe,EACf,QAAgB,EAChB,aAE2C;IAE3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAE1D,IAAI,CAAC,MAAM,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;KACpE;IAED,IAAI,OAAO,CAAC;IACZ,IAAI;QACF,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC;YAChD,EAAE,EAAE,QAAQ;YACZ,MAAM;SACP,CAAC,CAAC;KACJ;IAAC,OAAO,KAAK,EAAE;QACd,MAAM,KAAK,YAAY,KAAK;YAC1B,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;KAClE;IAED,MAAM,cAAc,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,cAAc,CAAC,eAAe,EAAE,CAAC;IAC/C,IAAI,KAAK,EAAE;QACT,OAAO,cAAc,CAAC;KACvB;IACD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;AACpE,CAAC;AAqJD,qBAAqB;AACrB,KAAK,iCAAU,SAAiB;IAC9B,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC;IAEvB,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE;QAClB,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;KACf;IACD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;IAC5C,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC;IAE/B,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;IACpB,IAAI,QAAQ,CAAC;IACb,IAAI,uBAAA,IAAI,mEAAoB,MAAxB,IAAI,CAAsB,EAAE;QAC9B,QAAQ,GAAG,MAAM,uBAAA,IAAI,iEAAkB,MAAtB,IAAI,EAAmB,IAAI,EAAE,EAAE,CAAC,CAAC;KACnD;SAAM;QACL,QAAQ,GAAG,uBAAA,IAAI,kEAAmB,MAAvB,IAAI,EAAoB,IAAI,EAAE,EAAE,CAAC,CAAC;KAC9C;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,oCAED,KAAK,0CAAmB,IAAY,EAAE,EAAU;IAC9C,MAAM,QAAQ,GAIR,EAAE,CAAC;IAET,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,uBAAA,IAAI,gEAAiB,MAArB,IAAI,EAAkB,CAAC,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,kBAAkB;YACnC,CAAC,CAAC,MAAM,uBAAA,IAAI,wEAAyB,MAA7B,IAAI,EAA0B,OAAO,CAAC;YAC9C,CAAC,CAAC,IAAI,CAAC;QACT,QAAQ,CAAC,IAAI,CAAC;YACZ,OAAO;YACP,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC;SACT,CAAC,CAAC;QAEH,YAAY;QACZ,uDAAuD;QACvD,yDAAyD;QACzD,0DAA0D;QAC1D,IAAI,CAAC,KAAK,EAAE;YACV,MAAM;SACP;KACF;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,+EAEkB,IAAY,EAAE,EAAU;IACzC,MAAM,QAAQ,GAIR,EAAE,CAAC;IAET,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,OAAO,GAAG,uBAAA,IAAI,iEAAkB,MAAtB,IAAI,EAAmB,QAAQ,EAAE,CAAC,CAAC,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC;YACZ,OAAO;YACP,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC;SACT,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;KACpD;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC,6EAEiB,QAAgB,EAAE,CAAS;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,QAAQ,IAAI,CAAC,EAAE,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO;SACpB,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC;SACrC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO,OAAO,CAAC,iBAAiB,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;AACnD,CAAC,2EAEgB,OAAe;IAC9B,MAAM,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC9D,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC3C,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE;QAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE;YAClC,IAAI,kBAAkB,KAAK,uBAAA,IAAI,iEAAkB,MAAtB,IAAI,EAAmB,QAAQ,EAAE,CAAC,CAAC,EAAE;gBAC9D,KAAK,GAAG,CAAC,CAAC;gBACV,MAAM;aACP;SACF;KACF;IAED,IAAI,OAAO,KAAK,KAAK,WAAW,EAAE;QAChC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;KACpC;IACD,OAAO,uBAAA,IAAI,gEAAiB,MAArB,IAAI,EAAkB,KAAK,CAAC,CAAC;AACtC,CAAC,2EAEgB,KAAa;IAC5B,4CAA4C;IAC5C,OAAO,uBAAA,IAAI,mEAAoB,MAAxB,IAAI,CAAsB;QAC/B,CAAC,CAAC,aAAa,KAAK,OAAO;QAC3B,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;AAChC,CAAC;IAGC,OAAO,IAAI,CAAC,MAAM,KAAK,kBAAkB,CAAC;AAC5C,CAAC,qEAEa,IAAY;IACxB,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC,2CAED,KAAK,iDAA0B,OAAe;IAC5C,MAAM,MAAM,GAAG,uBAAA,IAAI,0DAAW,MAAf,IAAI,CAAa,CAAC;IACjC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CACjC,GAAG,MAAM,6CAA6C,OAAO,6BAA6B,CAC3F,CAAC;IACF,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC7C,IAAI,cAAc,CAAC,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;QACrE,OAAO,IAAI,CAAC;KACb;IACD,OAAO,KAAK,CAAC;AACf,CAAC;IAGC,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC;AA5kBM,kBAAI,GAAW,WAAW,CAAC","sourcesContent":["import { RLP } from '@ethereumjs/rlp';\nimport { TransactionFactory, TxData, TypedTransaction } from '@ethereumjs/tx';\nimport * as ethUtil from '@ethereumjs/util';\nimport type { MessageTypes, TypedMessage } from '@metamask/eth-sig-util';\nimport {\n recoverPersonalSignature,\n recoverTypedSignature,\n SignTypedDataVersion,\n TypedDataUtils,\n} from '@metamask/eth-sig-util';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { Buffer } from 'buffer';\nimport type OldEthJsTransaction from 'ethereumjs-tx';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { EventEmitter } from 'events';\nimport HDKey from 'hdkey';\n\nimport { LedgerBridge, LedgerBridgeOptions } from './ledger-bridge';\n\nconst pathBase = 'm';\nconst hdPathString = `${pathBase}/44'/60'/0'`;\nconst keyringType = 'Ledger Hardware';\n\nconst MAX_INDEX = 1000;\n\nenum NetworkApiUrls {\n Ropsten = 'http://api-ropsten.etherscan.io',\n Kovan = 'http://api-kovan.etherscan.io',\n Rinkeby = 'https://api-rinkeby.etherscan.io',\n Mainnet = 'https://api.etherscan.io',\n}\n\ntype SignTransactionPayload = Awaited<\n ReturnType['deviceSignTransaction']>\n>;\n\nexport type AccountDetails = {\n index?: number;\n bip44?: boolean;\n hdPath?: string;\n};\n\nexport type LedgerBridgeKeyringOptions = {\n hdPath: string;\n accounts: readonly string[];\n accountDetails: Readonly>;\n accountIndexes: Readonly>;\n implementFullBIP44: boolean;\n};\n\n/**\n * Check if the given transaction is made with ethereumjs-tx or @ethereumjs/tx\n *\n * Transactions built with older versions of ethereumjs-tx have a\n * getChainId method that newer versions do not.\n * Older versions are mutable\n * while newer versions default to being immutable.\n * Expected shape and type\n * of data for v, r and s differ (Buffer (old) vs BN (new)).\n *\n * @param tx - Transaction to check, instance of either ethereumjs-tx or @ethereumjs/tx.\n * @returns Returns `true` if tx is an old-style ethereumjs-tx transaction.\n */\nfunction isOldStyleEthereumjsTx(\n tx: TypedTransaction | OldEthJsTransaction,\n): tx is OldEthJsTransaction {\n return 'getChainId' in tx && typeof tx.getChainId === 'function';\n}\n\nexport class LedgerKeyring extends EventEmitter {\n static type: string = keyringType;\n\n readonly type: string = keyringType;\n\n page = 0;\n\n perPage = 5;\n\n unlockedAccount = 0;\n\n accounts: readonly string[] = [];\n\n accountDetails: Record = {};\n\n hdk = new HDKey();\n\n hdPath = hdPathString;\n\n paths: Record = {};\n\n network: NetworkApiUrls = NetworkApiUrls.Mainnet;\n\n implementFullBIP44 = false;\n\n bridge: LedgerBridge;\n\n constructor({ bridge }: { bridge: LedgerBridge }) {\n super();\n\n if (!bridge) {\n throw new Error('Bridge is a required dependency for the keyring');\n }\n\n this.bridge = bridge;\n }\n\n async init() {\n return this.bridge.init();\n }\n\n async destroy() {\n return this.bridge.destroy();\n }\n\n async serialize() {\n return {\n hdPath: this.hdPath,\n accounts: this.accounts,\n accountDetails: this.accountDetails,\n implementFullBIP44: false,\n };\n }\n\n async deserialize(opts: Partial = {}) {\n this.hdPath = opts.hdPath ?? hdPathString;\n this.accounts = opts.accounts ?? [];\n this.accountDetails = opts.accountDetails ?? {};\n\n if (!opts.accountDetails) {\n this.#migrateAccountDetails(opts);\n }\n\n this.implementFullBIP44 = opts.implementFullBIP44 ?? false;\n\n const keys = new Set(Object.keys(this.accountDetails));\n // Remove accounts that don't have corresponding account details\n this.accounts = this.accounts.filter((account) =>\n keys.has(ethUtil.toChecksumAddress(account)),\n );\n\n return Promise.resolve();\n }\n\n #migrateAccountDetails(opts: Partial) {\n if (this.#isLedgerLiveHdPath() && opts.accountIndexes) {\n for (const [account, index] of Object.entries(opts.accountIndexes)) {\n this.accountDetails[account] = {\n bip44: true,\n hdPath: this.#getPathForIndex(index),\n };\n }\n }\n const keys = new Set(Object.keys(this.accountDetails));\n // try to migrate non-LedgerLive accounts too\n if (!this.#isLedgerLiveHdPath()) {\n this.accounts.forEach((account) => {\n try {\n const key = ethUtil.toChecksumAddress(account);\n\n if (!keys.has(key)) {\n this.accountDetails[key] = {\n bip44: false,\n hdPath: this.#pathFromAddress(account),\n };\n }\n } catch (error) {\n console.log(`failed to migrate account ${account}`);\n }\n });\n }\n }\n\n isUnlocked() {\n return Boolean(this.hdk?.publicKey);\n }\n\n isConnected() {\n return this.bridge.isDeviceConnected;\n }\n\n setAccountToUnlock(index: number | string) {\n this.unlockedAccount =\n typeof index === 'number' ? index : parseInt(index, 10);\n }\n\n setHdPath(hdPath: string) {\n // Reset HDKey if the path changes\n if (this.hdPath !== hdPath) {\n this.hdk = new HDKey();\n }\n this.hdPath = hdPath;\n }\n\n async unlock(hdPath?: string, updateHdk = true): Promise {\n if (this.isUnlocked() && !hdPath) {\n return 'already unlocked';\n }\n const path = hdPath ? this.#toLedgerPath(hdPath) : this.hdPath;\n\n let payload;\n try {\n payload = await this.bridge.getPublicKey({\n hdPath: path,\n });\n } catch (error) {\n throw error instanceof Error ? error : new Error('Unknown error');\n }\n\n if (updateHdk && payload.chainCode) {\n this.hdk.publicKey = Buffer.from(payload.publicKey, 'hex');\n this.hdk.chainCode = Buffer.from(payload.chainCode, 'hex');\n }\n\n return payload.address;\n }\n\n async addAccounts(amount = 1): Promise {\n return new Promise((resolve, reject) => {\n this.unlock()\n .then(async (_) => {\n const from = this.unlockedAccount;\n const to = from + amount;\n for (let i = from; i < to; i++) {\n const path = this.#getPathForIndex(i);\n let address;\n if (this.#isLedgerLiveHdPath()) {\n address = await this.unlock(path);\n } else {\n address = this.#addressFromIndex(pathBase, i);\n }\n\n this.accountDetails[ethUtil.toChecksumAddress(address)] = {\n // TODO: consider renaming this property, as the current name is misleading\n // It's currently used to represent whether an account uses the Ledger Live path.\n bip44: this.#isLedgerLiveHdPath(),\n hdPath: path,\n };\n\n if (!this.accounts.includes(address)) {\n this.accounts = [...this.accounts, address];\n }\n this.page = 0;\n }\n resolve(this.accounts.slice());\n })\n .catch(reject);\n });\n }\n\n async getFirstPage() {\n this.page = 0;\n return this.#getPage(1);\n }\n\n async getNextPage() {\n return this.#getPage(1);\n }\n\n async getPreviousPage() {\n return this.#getPage(-1);\n }\n\n async getAccounts() {\n return Promise.resolve(this.accounts.slice());\n }\n\n removeAccount(address: string) {\n const filteredAccounts = this.accounts.filter(\n (a) => a.toLowerCase() !== address.toLowerCase(),\n );\n\n if (filteredAccounts.length === this.accounts.length) {\n throw new Error(`Address ${address} not found in this keyring`);\n }\n\n this.accounts = filteredAccounts;\n delete this.accountDetails[ethUtil.toChecksumAddress(address)];\n }\n\n async attemptMakeApp() {\n return this.bridge.attemptMakeApp();\n }\n\n async updateTransportMethod(transportType: string) {\n return this.bridge.updateTransportMethod(transportType);\n }\n\n // tx is an instance of the ethereumjs-transaction class.\n async signTransaction(\n address: string,\n tx: TypedTransaction | OldEthJsTransaction,\n ): Promise {\n let rawTxHex;\n // transactions built with older versions of ethereumjs-tx have a\n // getChainId method that newer versions do not. Older versions are mutable\n // while newer versions default to being immutable. Expected shape and type\n // of data for v, r and s differ (Buffer (old) vs BN (new))\n if (isOldStyleEthereumjsTx(tx)) {\n // In this version of ethereumjs-tx we must add the chainId in hex format\n // to the initial v value. The chainId must be included in the serialized\n // transaction which is only communicated to ethereumjs-tx in this\n // value. In newer versions the chainId is communicated via the 'Common'\n // object.\n // @ts-expect-error tx.v should be a Buffer but we are assigning a string\n tx.v = ethUtil.bufferToHex(tx.getChainId());\n // @ts-expect-error tx.r should be a Buffer but we are assigning a string\n tx.r = '0x00';\n // @ts-expect-error tx.s should be a Buffer but we are assigning a string\n tx.s = '0x00';\n\n rawTxHex = tx.serialize().toString('hex');\n\n return this.#signTransaction(address, rawTxHex, (payload) => {\n tx.v = Buffer.from(payload.v, 'hex');\n tx.r = Buffer.from(payload.r, 'hex');\n tx.s = Buffer.from(payload.s, 'hex');\n return tx;\n });\n }\n\n // The below `encode` call is only necessary for legacy transactions, as `getMessageToSign`\n // calls `rlp.encode` internally for non-legacy transactions. As per the \"Transaction Execution\"\n // section of the ethereum yellow paper, transactions need to be \"well-formed RLP, with no additional\n // trailing bytes\".\n\n // Note also that `getMessageToSign` will return valid RLP for all transaction types, whereas the\n // `serialize` method will not for any transaction type except legacy. This is because `serialize` includes\n // empty r, s and v values in the encoded rlp. This is why we use `getMessageToSign` here instead of `serialize`.\n const messageToSign = tx.getMessageToSign(false);\n\n rawTxHex = Buffer.isBuffer(messageToSign)\n ? messageToSign.toString('hex')\n : Buffer.from(RLP.encode(messageToSign)).toString('hex');\n\n return this.#signTransaction(address, rawTxHex, (payload) => {\n // Because tx will be immutable, first get a plain javascript object that\n // represents the transaction. Using txData here as it aligns with the\n // nomenclature of ethereumjs/tx.\n const txData: TxData = tx.toJSON();\n // The fromTxData utility expects a type to support transactions with a type other than 0\n txData.type = tx.type;\n // The fromTxData utility expects v,r and s to be hex prefixed\n txData.v = ethUtil.addHexPrefix(payload.v);\n txData.r = ethUtil.addHexPrefix(payload.r);\n txData.s = ethUtil.addHexPrefix(payload.s);\n // Adopt the 'common' option from the original transaction and set the\n // returned object to be frozen if the original is frozen.\n return TransactionFactory.fromTxData(txData, {\n common: tx.common,\n freeze: Object.isFrozen(tx),\n });\n });\n }\n\n async #signTransaction(\n address: string,\n rawTxHex: string,\n handleSigning: (\n payload: SignTransactionPayload,\n ) => TypedTransaction | OldEthJsTransaction,\n ): Promise {\n const hdPath = await this.unlockAccountByAddress(address);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing transaction');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignTransaction({\n tx: rawTxHex,\n hdPath,\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing transaction');\n }\n\n const newOrMutatedTx = handleSigning(payload);\n const valid = newOrMutatedTx.verifySignature();\n if (valid) {\n return newOrMutatedTx;\n }\n throw new Error('Ledger: The transaction signature is not valid');\n }\n\n async signMessage(withAccount: string, data: string) {\n return this.signPersonalMessage(withAccount, data);\n }\n\n // For personal_sign, we need to prefix the message:\n async signPersonalMessage(withAccount: string, message: string) {\n const hdPath = await this.unlockAccountByAddress(withAccount);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing message');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignMessage({\n hdPath,\n message: ethUtil.stripHexPrefix(message),\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing message');\n }\n\n let recoveryId = parseInt(String(payload.v), 10).toString(16);\n if (recoveryId.length < 2) {\n recoveryId = `0${recoveryId}`;\n }\n\n let modifiedV = parseInt(String(payload.v), 10).toString(16);\n if (modifiedV.length < 2) {\n modifiedV = `0${modifiedV}`;\n }\n\n const signature = `0x${payload.r}${payload.s}${modifiedV}`;\n const addressSignedWith = recoverPersonalSignature({\n data: message,\n signature,\n });\n if (\n ethUtil.toChecksumAddress(addressSignedWith) !==\n ethUtil.toChecksumAddress(withAccount)\n ) {\n throw new Error('Ledger: The signature doesnt match the right address');\n }\n return signature;\n }\n\n async unlockAccountByAddress(address: string) {\n const checksummedAddress = ethUtil.toChecksumAddress(address);\n const accountDetails = this.accountDetails[checksummedAddress];\n if (!accountDetails) {\n throw new Error(\n `Ledger: Account for address '${checksummedAddress}' not found`,\n );\n }\n const { hdPath } = accountDetails;\n const unlockedAddress = await this.unlock(hdPath, false);\n\n // unlock resolves to the address for the given hdPath as reported by the ledger device\n // if that address is not the requested address, then this account belongs to a different device or seed\n if (unlockedAddress.toLowerCase() !== address.toLowerCase()) {\n throw new Error(\n `Ledger: Account ${address} does not belong to the connected device`,\n );\n }\n return hdPath;\n }\n\n async signTypedData(\n withAccount: string,\n data: TypedMessage,\n options: { version?: string } = {},\n ) {\n const isV4 = options.version === 'V4';\n if (!isV4) {\n throw new Error(\n 'Ledger: Only version 4 of typed data signing is supported',\n );\n }\n\n const { domain, types, primaryType, message } =\n TypedDataUtils.sanitizeData(data);\n const domainSeparatorHex = TypedDataUtils.hashStruct(\n 'EIP712Domain',\n domain,\n types,\n SignTypedDataVersion.V4,\n ).toString('hex');\n const hashStructMessageHex = TypedDataUtils.hashStruct(\n primaryType.toString(),\n message,\n types,\n SignTypedDataVersion.V4,\n ).toString('hex');\n\n const hdPath = await this.unlockAccountByAddress(withAccount);\n\n if (!hdPath) {\n throw new Error('Ledger: Unknown error while signing message');\n }\n\n let payload;\n try {\n payload = await this.bridge.deviceSignTypedData({\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n });\n } catch (error) {\n throw error instanceof Error\n ? error\n : new Error('Ledger: Unknown error while signing message');\n }\n\n let recoveryId = parseInt(String(payload.v), 10).toString(16);\n if (recoveryId.length < 2) {\n recoveryId = `0${recoveryId}`;\n }\n const signature = `0x${payload.r}${payload.s}${recoveryId}`;\n const addressSignedWith = recoverTypedSignature({\n data,\n signature,\n version: SignTypedDataVersion.V4,\n });\n if (\n ethUtil.toChecksumAddress(addressSignedWith) !==\n ethUtil.toChecksumAddress(withAccount)\n ) {\n throw new Error('Ledger: The signature doesnt match the right address');\n }\n return signature;\n }\n\n exportAccount() {\n throw new Error('Not supported on this device');\n }\n\n forgetDevice() {\n this.accounts = [];\n this.page = 0;\n this.unlockedAccount = 0;\n this.paths = {};\n this.accountDetails = {};\n this.hdk = new HDKey();\n }\n\n /* PRIVATE METHODS */\n async #getPage(increment: number) {\n this.page += increment;\n\n if (this.page <= 0) {\n this.page = 1;\n }\n const from = (this.page - 1) * this.perPage;\n const to = from + this.perPage;\n\n await this.unlock();\n let accounts;\n if (this.#isLedgerLiveHdPath()) {\n accounts = await this.#getAccountsBIP44(from, to);\n } else {\n accounts = this.#getAccountsLegacy(from, to);\n }\n return accounts;\n }\n\n async #getAccountsBIP44(from: number, to: number) {\n const accounts: {\n address: string;\n balance: number | null;\n index: number;\n }[] = [];\n\n for (let i = from; i < to; i++) {\n const path = this.#getPathForIndex(i);\n const address = await this.unlock(path);\n const valid = this.implementFullBIP44\n ? await this.#hasPreviousTransactions(address)\n : true;\n accounts.push({\n address,\n balance: null,\n index: i,\n });\n\n // PER BIP44\n // \"Software should prevent a creation of an account if\n // a previous account does not have a transaction history\n // (meaning none of its addresses have been used before).\"\n if (!valid) {\n break;\n }\n }\n return accounts;\n }\n\n #getAccountsLegacy(from: number, to: number) {\n const accounts: {\n address: string;\n balance: number | null;\n index: number;\n }[] = [];\n\n for (let i = from; i < to; i++) {\n const address = this.#addressFromIndex(pathBase, i);\n accounts.push({\n address,\n balance: null,\n index: i,\n });\n this.paths[ethUtil.toChecksumAddress(address)] = i;\n }\n return accounts;\n }\n\n #addressFromIndex(basePath: string, i: number) {\n const dkey = this.hdk.derive(`${basePath}/${i}`);\n const address = ethUtil\n .publicToAddress(dkey.publicKey, true)\n .toString('hex');\n return ethUtil.toChecksumAddress(`0x${address}`);\n }\n\n #pathFromAddress(address: string) {\n const checksummedAddress = ethUtil.toChecksumAddress(address);\n let index = this.paths[checksummedAddress];\n if (typeof index === 'undefined') {\n for (let i = 0; i < MAX_INDEX; i++) {\n if (checksummedAddress === this.#addressFromIndex(pathBase, i)) {\n index = i;\n break;\n }\n }\n }\n\n if (typeof index === 'undefined') {\n throw new Error('Unknown address');\n }\n return this.#getPathForIndex(index);\n }\n\n #getPathForIndex(index: number) {\n // Check if the path is BIP 44 (Ledger Live)\n return this.#isLedgerLiveHdPath()\n ? `m/44'/60'/${index}'/0/0`\n : `${this.hdPath}/${index}`;\n }\n\n #isLedgerLiveHdPath() {\n return this.hdPath === `m/44'/60'/0'/0/0`;\n }\n\n #toLedgerPath(path: string) {\n return path.toString().replace('m/', '');\n }\n\n async #hasPreviousTransactions(address: string) {\n const apiUrl = this.#getApiUrl();\n const response = await window.fetch(\n `${apiUrl}/api?module=account&action=txlist&address=${address}&tag=latest&page=1&offset=1`,\n );\n const parsedResponse = await response.json();\n if (parsedResponse.status !== '0' && parsedResponse.result.length > 0) {\n return true;\n }\n return false;\n }\n\n #getApiUrl() {\n return this.network;\n }\n}\n"]} -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts -new file mode 100644 -index 0000000..d6f03f2 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.d.ts -@@ -0,0 +1,89 @@ -+import type Transport from '@ledgerhq/hw-transport'; -+import { GetPublicKeyParams, GetPublicKeyResponse, LedgerBridge, LedgerSignMessageParams, LedgerSignMessageResponse, LedgerSignTransactionParams, LedgerSignTransactionResponse, LedgerSignTypedDataParams, LedgerSignTypedDataResponse } from './ledger-bridge'; -+import { GetAppNameAndVersionResponse, LedgerMobileBridgeOptions, TransportMiddleware } from './ledger-mobile-bridge/'; -+export interface LedgerMobileBridge { -+ getAppNameAndVersion(): Promise; -+ openEthApp(): Promise; -+ closeApps(): Promise; -+} -+/** -+ * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware. -+ */ -+export declare class LedgerMobileBridge implements LedgerBridge { -+ #private; -+ isDeviceConnected: boolean; -+ constructor(transportMiddleware: TransportMiddleware, opts?: LedgerMobileBridgeOptions); -+ /** -+ * Method to initializes the keyring. -+ * Mobile ledger doesnt not require init. -+ */ -+ init(): Promise; -+ /** -+ * Method to destroy the keyring. -+ * It will dispose the transportmiddleware and set isDeviceConnected to false. -+ */ -+ destroy(): Promise; -+ /** -+ * Method to sign a string Message. -+ * Sending the string message to the device and returning the signed message. -+ * -+ * @param params - The descriptor to open the transport with. -+ * @param params.hdPath - The descriptor to open the transport with. -+ * @param params.message - An optional timeout for the transport connection. -+ * @returns Retrieve v, r, s from the signed message. -+ */ -+ deviceSignMessage({ hdPath, message, }: LedgerSignMessageParams): Promise; -+ /** -+ * Method to sign a EIP712 Message. -+ * Sending the typed data message to the device and returning the signed message. -+ * -+ * @param params - An object contains hdPath, domainSeparatorHex and hashStructMessageHex. -+ * @param params.hdPath - The BIP 32 path of the account. -+ * @param params.domainSeparatorHex - The domain separator. -+ * @param params.hashStructMessageHex - The hashed struct message. -+ * @returns Retrieve v, r, s from the signed message. -+ */ -+ deviceSignTypedData({ hdPath, domainSeparatorHex, hashStructMessageHex, }: LedgerSignTypedDataParams): Promise; -+ /** -+ * Method to sign a transaction -+ * Sending the hexadecimal transaction message to the device and returning the signed transaction. -+ * -+ * @param params - An object contains tx, hdPath. -+ * @param params.tx - The raw ethereum transaction in hexadecimal to sign. -+ * @param params.hdPath - The BIP 32 path of the account. -+ * @returns Retrieve v, r, s from the signed transaction. -+ */ -+ deviceSignTransaction({ tx, hdPath, }: LedgerSignTransactionParams): Promise; -+ /** -+ * Method to retrieve the ethereum address for a given BIP 32 path. -+ * -+ * @param params - An object contains hdPath. -+ * @param params.hdPath - The BIP 32 path of the account. -+ * @returns An object contains publicKey, address and chainCode. -+ */ -+ getPublicKey({ hdPath, }: GetPublicKeyParams): Promise; -+ /** -+ * Method to retrieve the current configuration. -+ * -+ * @returns Retrieve current configuration. -+ */ -+ getOptions(): Promise; -+ /** -+ * Method to set the current configuration. -+ * -+ * @param opts - An configuration object. -+ */ -+ setOptions(opts: LedgerMobileBridgeOptions): Promise; -+ /** -+ * Method to set the transport object to communicate with the device. -+ * -+ * @param transport - The communication interface with the Ledger hardware wallet. There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb). -+ * @returns Retrieve boolean. -+ */ -+ updateTransportMethod(transport: Transport): Promise; -+ /** -+ * Method to init eth app object on ledger device. -+ * This method is not supported on mobile. -+ */ -+ attemptMakeApp(): Promise; -+} -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js -new file mode 100644 -index 0000000..3fe54b8 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js -@@ -0,0 +1,171 @@ -+"use strict"; -+var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { -+ if (kind === "m") throw new TypeError("Private method is not writable"); -+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); -+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); -+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -+}; -+var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { -+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); -+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); -+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -+}; -+var __importDefault = (this && this.__importDefault) || function (mod) { -+ return (mod && mod.__esModule) ? mod : { "default": mod }; -+}; -+var _LedgerMobileBridge_instances, _LedgerMobileBridge_transportMiddleware, _LedgerMobileBridge_opts, _LedgerMobileBridge_getTransportMiddleWare, _LedgerMobileBridge_getEthApp; -+Object.defineProperty(exports, "__esModule", { value: true }); -+exports.LedgerMobileBridge = void 0; -+const ledger_1 = __importDefault(require("@ledgerhq/hw-app-eth/lib/services/ledger")); -+/** -+ * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware. -+ */ -+class LedgerMobileBridge { -+ constructor(transportMiddleware, opts = {}) { -+ _LedgerMobileBridge_instances.add(this); -+ _LedgerMobileBridge_transportMiddleware.set(this, void 0); -+ _LedgerMobileBridge_opts.set(this, void 0); -+ this.isDeviceConnected = false; -+ __classPrivateFieldSet(this, _LedgerMobileBridge_opts, opts, "f"); -+ __classPrivateFieldSet(this, _LedgerMobileBridge_transportMiddleware, transportMiddleware, "f"); -+ } -+ /** -+ * Method to initializes the keyring. -+ * Mobile ledger doesnt not require init. -+ */ -+ async init() { -+ return Promise.resolve(); -+ } -+ /** -+ * Method to destroy the keyring. -+ * It will dispose the transportmiddleware and set isDeviceConnected to false. -+ */ -+ async destroy() { -+ try { -+ await __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getTransportMiddleWare).call(this).dispose(); -+ } -+ catch (error) { -+ // eslint-disable-next-line no-console -+ console.error(error); -+ } -+ this.isDeviceConnected = false; -+ } -+ /** -+ * Method to sign a string Message. -+ * Sending the string message to the device and returning the signed message. -+ * -+ * @param params - The descriptor to open the transport with. -+ * @param params.hdPath - The descriptor to open the transport with. -+ * @param params.message - An optional timeout for the transport connection. -+ * @returns Retrieve v, r, s from the signed message. -+ */ -+ async deviceSignMessage({ hdPath, message, }) { -+ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).signPersonalMessage(hdPath, message); -+ } -+ /** -+ * Method to sign a EIP712 Message. -+ * Sending the typed data message to the device and returning the signed message. -+ * -+ * @param params - An object contains hdPath, domainSeparatorHex and hashStructMessageHex. -+ * @param params.hdPath - The BIP 32 path of the account. -+ * @param params.domainSeparatorHex - The domain separator. -+ * @param params.hashStructMessageHex - The hashed struct message. -+ * @returns Retrieve v, r, s from the signed message. -+ */ -+ async deviceSignTypedData({ hdPath, domainSeparatorHex, hashStructMessageHex, }) { -+ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).signEIP712HashedMessage(hdPath, domainSeparatorHex, hashStructMessageHex); -+ } -+ /** -+ * Method to sign a transaction -+ * Sending the hexadecimal transaction message to the device and returning the signed transaction. -+ * -+ * @param params - An object contains tx, hdPath. -+ * @param params.tx - The raw ethereum transaction in hexadecimal to sign. -+ * @param params.hdPath - The BIP 32 path of the account. -+ * @returns Retrieve v, r, s from the signed transaction. -+ */ -+ async deviceSignTransaction({ tx, hdPath, }) { -+ const resolution = await ledger_1.default.resolveTransaction(tx, {}, {}); -+ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).signTransaction(hdPath, tx, resolution); -+ } -+ /** -+ * Method to retrieve the ethereum address for a given BIP 32 path. -+ * -+ * @param params - An object contains hdPath. -+ * @param params.hdPath - The BIP 32 path of the account. -+ * @returns An object contains publicKey, address and chainCode. -+ */ -+ async getPublicKey({ hdPath, }) { -+ return await __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).getAddress(hdPath, false, true); -+ } -+ /** -+ * Method to retrieve the current configuration. -+ * -+ * @returns Retrieve current configuration. -+ */ -+ async getOptions() { -+ return __classPrivateFieldGet(this, _LedgerMobileBridge_opts, "f"); -+ } -+ /** -+ * Method to set the current configuration. -+ * -+ * @param opts - An configuration object. -+ */ -+ async setOptions(opts) { -+ __classPrivateFieldSet(this, _LedgerMobileBridge_opts, opts, "f"); -+ } -+ /** -+ * Method to set the transport object to communicate with the device. -+ * -+ * @param transport - The communication interface with the Ledger hardware wallet. There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb). -+ * @returns Retrieve boolean. -+ */ -+ async updateTransportMethod(transport) { -+ var _a; -+ if (!((_a = transport.deviceModel) === null || _a === void 0 ? void 0 : _a.id)) { -+ throw new Error('Property `deviceModel.id` is not defined in `transport`.'); -+ } -+ __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getTransportMiddleWare).call(this).setTransport(transport); -+ this.isDeviceConnected = true; -+ return Promise.resolve(true); -+ } -+ /** -+ * Method to init eth app object on ledger device. -+ * This method is not supported on mobile. -+ */ -+ async attemptMakeApp() { -+ throw new Error('Method not supported.'); -+ } -+ /** -+ * Method to open ethereum application on ledger device. -+ * -+ */ -+ async openEthApp() { -+ await __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).openEthApp(); -+ } -+ /** -+ * Method to close all running application on ledger device. -+ * -+ */ -+ async closeApps() { -+ await __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).closeApps(); -+ } -+ /** -+ * Method to retrieve the name and version of the running application in ledger device. -+ * -+ * @returns An object contains appName and version. -+ */ -+ async getAppNameAndVersion() { -+ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getEthApp).call(this).getAppNameAndVersion(); -+ } -+} -+exports.LedgerMobileBridge = LedgerMobileBridge; -+_LedgerMobileBridge_transportMiddleware = new WeakMap(), _LedgerMobileBridge_opts = new WeakMap(), _LedgerMobileBridge_instances = new WeakSet(), _LedgerMobileBridge_getTransportMiddleWare = function _LedgerMobileBridge_getTransportMiddleWare() { -+ if (__classPrivateFieldGet(this, _LedgerMobileBridge_transportMiddleware, "f")) { -+ return __classPrivateFieldGet(this, _LedgerMobileBridge_transportMiddleware, "f"); -+ } -+ throw new Error('Instance `transportMiddleware` is not initialized.'); -+}, _LedgerMobileBridge_getEthApp = function _LedgerMobileBridge_getEthApp() { -+ return __classPrivateFieldGet(this, _LedgerMobileBridge_instances, "m", _LedgerMobileBridge_getTransportMiddleWare).call(this).getEthApp(); -+}; -+//# sourceMappingURL=ledger-mobile-bridge.js.map -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map -new file mode 100644 -index 0000000..f3dc579 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge.js.map -@@ -0,0 +1 @@ -+{"version":3,"file":"ledger-mobile-bridge.js","sourceRoot":"","sources":["../src/ledger-mobile-bridge.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,sFAAqE;AA6BrE;;GAEG;AACH,MAAa,kBAAkB;IAS7B,YACE,mBAAwC,EACxC,OAAkC,EAAE;;QARtC,0DAA2C;QAE3C,2CAAiC;QAEjC,sBAAiB,GAAG,KAAK,CAAC;QAMxB,uBAAA,IAAI,4BAAS,IAAI,MAAA,CAAC;QAClB,uBAAA,IAAI,2CAAwB,mBAAmB,MAAA,CAAC;IAClD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI;QACR,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,IAAI;YACF,MAAM,uBAAA,IAAI,iFAAwB,MAA5B,IAAI,CAA0B,CAAC,OAAO,EAAE,CAAC;SAChD;QAAC,OAAO,KAAK,EAAE;YACd,sCAAsC;YACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SACtB;QACD,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;IACjC,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,iBAAiB,CAAC,EACtB,MAAM,EACN,OAAO,GACiB;QACxB,OAAO,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,mBAAmB,CAAC,EACxB,MAAM,EACN,kBAAkB,EAClB,oBAAoB,GACM;QAC1B,OAAO,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,uBAAuB,CAC9C,MAAM,EACN,kBAAkB,EAClB,oBAAoB,CACrB,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,qBAAqB,CAAC,EAC1B,EAAE,EACF,MAAM,GACsB;QAC5B,MAAM,UAAU,GAAG,MAAM,gBAAa,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACtE,OAAO,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,YAAY,CAAC,EACjB,MAAM,GACa;QACnB,OAAO,MAAM,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACjE,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,uBAAA,IAAI,gCAAM,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU,CAAC,IAA+B;QAC9C,uBAAA,IAAI,4BAAS,IAAI,MAAA,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,qBAAqB,CAAC,SAAoB;;QAC9C,IAAI,CAAC,CAAA,MAAA,SAAS,CAAC,WAAW,0CAAE,EAAE,CAAA,EAAE;YAC9B,MAAM,IAAI,KAAK,CACb,0DAA0D,CAC3D,CAAC;SACH;QACD,uBAAA,IAAI,iFAAwB,MAA5B,IAAI,CAA0B,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC9B,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,UAAU,EAAE,CAAC;IACvC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,SAAS,EAAE,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,oBAAoB;QACxB,OAAO,uBAAA,IAAI,oEAAW,MAAf,IAAI,CAAa,CAAC,oBAAoB,EAAE,CAAC;IAClD,CAAC;CAsBF;AAnMD,gDAmMC;;IAdG,IAAI,uBAAA,IAAI,+CAAqB,EAAE;QAC7B,OAAO,uBAAA,IAAI,+CAAqB,CAAC;KAClC;IACD,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;AACxE,CAAC;IAQC,OAAO,uBAAA,IAAI,iFAAwB,MAA5B,IAAI,CAA0B,CAAC,SAAS,EAAE,CAAC;AACpD,CAAC","sourcesContent":["import ledgerService from '@ledgerhq/hw-app-eth/lib/services/ledger';\nimport type Transport from '@ledgerhq/hw-transport';\n\n// eslint-disable-next-line import/no-nodejs-modules\nimport {\n GetPublicKeyParams,\n GetPublicKeyResponse,\n LedgerBridge,\n LedgerSignMessageParams,\n LedgerSignMessageResponse,\n LedgerSignTransactionParams,\n LedgerSignTransactionResponse,\n LedgerSignTypedDataParams,\n LedgerSignTypedDataResponse,\n} from './ledger-bridge';\nimport {\n GetAppNameAndVersionResponse,\n LedgerMobileBridgeOptions,\n TransportMiddleware,\n type MetaMaskLedgerHwAppEth,\n} from './ledger-mobile-bridge/';\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface LedgerMobileBridge {\n getAppNameAndVersion(): Promise;\n openEthApp(): Promise;\n closeApps(): Promise;\n}\n\n/**\n * LedgerMobileBridge is a bridge between the LedgerKeyring and the LedgerTransportMiddleware.\n */\nexport class LedgerMobileBridge\n implements LedgerBridge\n{\n #transportMiddleware?: TransportMiddleware;\n\n #opts: LedgerMobileBridgeOptions;\n\n isDeviceConnected = false;\n\n constructor(\n transportMiddleware: TransportMiddleware,\n opts: LedgerMobileBridgeOptions = {},\n ) {\n this.#opts = opts;\n this.#transportMiddleware = transportMiddleware;\n }\n\n /**\n * Method to initializes the keyring.\n * Mobile ledger doesnt not require init.\n */\n async init(): Promise {\n return Promise.resolve();\n }\n\n /**\n * Method to destroy the keyring.\n * It will dispose the transportmiddleware and set isDeviceConnected to false.\n */\n async destroy(): Promise {\n try {\n await this.#getTransportMiddleWare().dispose();\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(error);\n }\n this.isDeviceConnected = false;\n }\n\n /**\n * Method to sign a string Message.\n * Sending the string message to the device and returning the signed message.\n *\n * @param params - The descriptor to open the transport with.\n * @param params.hdPath - The descriptor to open the transport with.\n * @param params.message - An optional timeout for the transport connection.\n * @returns Retrieve v, r, s from the signed message.\n */\n async deviceSignMessage({\n hdPath,\n message,\n }: LedgerSignMessageParams): Promise {\n return this.#getEthApp().signPersonalMessage(hdPath, message);\n }\n\n /**\n * Method to sign a EIP712 Message.\n * Sending the typed data message to the device and returning the signed message.\n *\n * @param params - An object contains hdPath, domainSeparatorHex and hashStructMessageHex.\n * @param params.hdPath - The BIP 32 path of the account.\n * @param params.domainSeparatorHex - The domain separator.\n * @param params.hashStructMessageHex - The hashed struct message.\n * @returns Retrieve v, r, s from the signed message.\n */\n async deviceSignTypedData({\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n }: LedgerSignTypedDataParams): Promise {\n return this.#getEthApp().signEIP712HashedMessage(\n hdPath,\n domainSeparatorHex,\n hashStructMessageHex,\n );\n }\n\n /**\n * Method to sign a transaction\n * Sending the hexadecimal transaction message to the device and returning the signed transaction.\n *\n * @param params - An object contains tx, hdPath.\n * @param params.tx - The raw ethereum transaction in hexadecimal to sign.\n * @param params.hdPath - The BIP 32 path of the account.\n * @returns Retrieve v, r, s from the signed transaction.\n */\n async deviceSignTransaction({\n tx,\n hdPath,\n }: LedgerSignTransactionParams): Promise {\n const resolution = await ledgerService.resolveTransaction(tx, {}, {});\n return this.#getEthApp().signTransaction(hdPath, tx, resolution);\n }\n\n /**\n * Method to retrieve the ethereum address for a given BIP 32 path.\n *\n * @param params - An object contains hdPath.\n * @param params.hdPath - The BIP 32 path of the account.\n * @returns An object contains publicKey, address and chainCode.\n */\n async getPublicKey({\n hdPath,\n }: GetPublicKeyParams): Promise {\n return await this.#getEthApp().getAddress(hdPath, false, true);\n }\n\n /**\n * Method to retrieve the current configuration.\n *\n * @returns Retrieve current configuration.\n */\n async getOptions(): Promise {\n return this.#opts;\n }\n\n /**\n * Method to set the current configuration.\n *\n * @param opts - An configuration object.\n */\n async setOptions(opts: LedgerMobileBridgeOptions): Promise {\n this.#opts = opts;\n }\n\n /**\n * Method to set the transport object to communicate with the device.\n *\n * @param transport - The communication interface with the Ledger hardware wallet. There are different kind of transports based on the technology (channels like U2F, HID, Bluetooth, Webusb).\n * @returns Retrieve boolean.\n */\n async updateTransportMethod(transport: Transport): Promise {\n if (!transport.deviceModel?.id) {\n throw new Error(\n 'Property `deviceModel.id` is not defined in `transport`.',\n );\n }\n this.#getTransportMiddleWare().setTransport(transport);\n this.isDeviceConnected = true;\n return Promise.resolve(true);\n }\n\n /**\n * Method to init eth app object on ledger device.\n * This method is not supported on mobile.\n */\n async attemptMakeApp(): Promise {\n throw new Error('Method not supported.');\n }\n\n /**\n * Method to open ethereum application on ledger device.\n *\n */\n async openEthApp(): Promise {\n await this.#getEthApp().openEthApp();\n }\n\n /**\n * Method to close all running application on ledger device.\n *\n */\n async closeApps(): Promise {\n await this.#getEthApp().closeApps();\n }\n\n /**\n * Method to retrieve the name and version of the running application in ledger device.\n *\n * @returns An object contains appName and version.\n */\n async getAppNameAndVersion(): Promise {\n return this.#getEthApp().getAppNameAndVersion();\n }\n\n /**\n * Method to retrieve the transport middleWare object.\n *\n * @returns The TransportMiddleware object.\n */\n #getTransportMiddleWare(): TransportMiddleware {\n if (this.#transportMiddleware) {\n return this.#transportMiddleware;\n }\n throw new Error('Instance `transportMiddleware` is not initialized.');\n }\n\n /**\n * Method to retrieve the ledger Eth App object.\n *\n * @returns The ledger Eth App object.\n */\n #getEthApp(): MetaMaskLedgerHwAppEth {\n return this.#getTransportMiddleWare().getEthApp();\n }\n}\n"]} -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.d.ts -new file mode 100644 -index 0000000..e02de28 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.d.ts -@@ -0,0 +1,3 @@ -+export * from './middleware'; -+export * from './type'; -+export * from './ledger-hw-app'; -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js -new file mode 100644 -index 0000000..b8d180e ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js -@@ -0,0 +1,20 @@ -+"use strict"; -+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { -+ if (k2 === undefined) k2 = k; -+ var desc = Object.getOwnPropertyDescriptor(m, k); -+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { -+ desc = { enumerable: true, get: function() { return m[k]; } }; -+ } -+ Object.defineProperty(o, k2, desc); -+}) : (function(o, m, k, k2) { -+ if (k2 === undefined) k2 = k; -+ o[k2] = m[k]; -+})); -+var __exportStar = (this && this.__exportStar) || function(m, exports) { -+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -+}; -+Object.defineProperty(exports, "__esModule", { value: true }); -+__exportStar(require("./middleware"), exports); -+__exportStar(require("./type"), exports); -+__exportStar(require("./ledger-hw-app"), exports); -+//# sourceMappingURL=index.js.map -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js.map -new file mode 100644 -index 0000000..dec127e ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/index.js.map -@@ -0,0 +1 @@ -+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ledger-mobile-bridge/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,yCAAuB;AACvB,kDAAgC","sourcesContent":["export * from './middleware';\nexport * from './type';\nexport * from './ledger-hw-app';\n"]} -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.d.ts -new file mode 100644 -index 0000000..4570615 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.d.ts -@@ -0,0 +1,12 @@ -+import LedgerHwAppEth from '@ledgerhq/hw-app-eth'; -+import { GetAppNameAndVersionResponse } from './type'; -+export interface MetaMaskLedgerHwAppEth extends LedgerHwAppEth { -+ openEthApp(): void; -+ closeApps(): void; -+ getAppNameAndVersion(): Promise; -+} -+export declare class MetaMaskLedgerHwAppEth extends LedgerHwAppEth implements MetaMaskLedgerHwAppEth { -+ readonly mainAppName = "BOLOS"; -+ readonly ethAppName = "Ethereum"; -+ readonly transportEncoding = "ascii"; -+} -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js -new file mode 100644 -index 0000000..a2fa515 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js -@@ -0,0 +1,60 @@ -+"use strict"; -+var __importDefault = (this && this.__importDefault) || function (mod) { -+ return (mod && mod.__esModule) ? mod : { "default": mod }; -+}; -+Object.defineProperty(exports, "__esModule", { value: true }); -+exports.MetaMaskLedgerHwAppEth = void 0; -+const hw_app_eth_1 = __importDefault(require("@ledgerhq/hw-app-eth")); -+// eslint-disable-next-line import/no-nodejs-modules -+const buffer_1 = require("buffer"); -+class MetaMaskLedgerHwAppEth extends hw_app_eth_1.default { -+ constructor() { -+ super(...arguments); -+ this.mainAppName = 'BOLOS'; -+ this.ethAppName = 'Ethereum'; -+ this.transportEncoding = 'ascii'; -+ } -+ /** -+ * Method to open ethereum application on ledger device. -+ * -+ */ -+ async openEthApp() { -+ await this.transport.send(0xe0, 0xd8, 0x00, 0x00, buffer_1.Buffer.from(this.ethAppName, this.transportEncoding)); -+ } -+ /** -+ * Method to close all running application on ledger device. -+ * -+ */ -+ async closeApps() { -+ await this.transport.send(0xb0, 0xa7, 0x00, 0x00); -+ } -+ /** -+ * Method to retrieve the name and version of the running application in ledger device. -+ * -+ * @returns An object contains appName and version. -+ */ -+ async getAppNameAndVersion() { -+ var _a, _b; -+ const response = await this.transport.send(0xb0, 0x01, 0x00, 0x00); -+ if (response[0] !== 1) { -+ throw new Error('Incorrect format return from getAppNameAndVersion.'); -+ } -+ let i = 1; -+ const nameLength = (_a = response[i]) !== null && _a !== void 0 ? _a : 0; -+ i += 1; -+ const appName = response -+ .slice(i, (i += nameLength)) -+ .toString(this.transportEncoding); -+ const versionLength = (_b = response[i]) !== null && _b !== void 0 ? _b : 0; -+ i += 1; -+ const version = response -+ .slice(i, (i += versionLength)) -+ .toString(this.transportEncoding); -+ return { -+ appName, -+ version, -+ }; -+ } -+} -+exports.MetaMaskLedgerHwAppEth = MetaMaskLedgerHwAppEth; -+//# sourceMappingURL=ledger-hw-app.js.map -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js.map -new file mode 100644 -index 0000000..e19f879 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/ledger-hw-app.js.map -@@ -0,0 +1 @@ -+{"version":3,"file":"ledger-hw-app.js","sourceRoot":"","sources":["../../src/ledger-mobile-bridge/ledger-hw-app.ts"],"names":[],"mappings":";;;;;;AAAA,sEAAkD;AAClD,oDAAoD;AACpD,mCAAgC;AAWhC,MAAa,sBACX,SAAQ,oBAAc;IADxB;;QAIW,gBAAW,GAAG,OAAO,CAAC;QAEtB,eAAU,GAAG,UAAU,CAAC;QAExB,sBAAiB,GAAG,OAAO,CAAC;IAuDvC,CAAC;IArDC;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACvB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,eAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,iBAAiB,CAAC,CACrD,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,oBAAoB;;QACxB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACnE,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE;YACrB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;SACvE;QAED,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,UAAU,GAAG,MAAA,QAAQ,CAAC,CAAC,CAAC,mCAAI,CAAC,CAAC;QACpC,CAAC,IAAI,CAAC,CAAC;QAEP,MAAM,OAAO,GAAG,QAAQ;aACrB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC;aAC3B,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAEpC,MAAM,aAAa,GAAG,MAAA,QAAQ,CAAC,CAAC,CAAC,mCAAI,CAAC,CAAC;QACvC,CAAC,IAAI,CAAC,CAAC;QAEP,MAAM,OAAO,GAAG,QAAQ;aACrB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC;aAC9B,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAEpC,OAAO;YACL,OAAO;YACP,OAAO;SACR,CAAC;IACJ,CAAC;CACF;AA/DD,wDA+DC","sourcesContent":["import LedgerHwAppEth from '@ledgerhq/hw-app-eth';\n// eslint-disable-next-line import/no-nodejs-modules\nimport { Buffer } from 'buffer';\n\nimport { GetAppNameAndVersionResponse } from './type';\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface MetaMaskLedgerHwAppEth extends LedgerHwAppEth {\n openEthApp(): void;\n closeApps(): void;\n getAppNameAndVersion(): Promise;\n}\n\nexport class MetaMaskLedgerHwAppEth\n extends LedgerHwAppEth\n implements MetaMaskLedgerHwAppEth\n{\n readonly mainAppName = 'BOLOS';\n\n readonly ethAppName = 'Ethereum';\n\n readonly transportEncoding = 'ascii';\n\n /**\n * Method to open ethereum application on ledger device.\n *\n */\n async openEthApp(): Promise {\n await this.transport.send(\n 0xe0,\n 0xd8,\n 0x00,\n 0x00,\n Buffer.from(this.ethAppName, this.transportEncoding),\n );\n }\n\n /**\n * Method to close all running application on ledger device.\n *\n */\n async closeApps(): Promise {\n await this.transport.send(0xb0, 0xa7, 0x00, 0x00);\n }\n\n /**\n * Method to retrieve the name and version of the running application in ledger device.\n *\n * @returns An object contains appName and version.\n */\n async getAppNameAndVersion(): Promise {\n const response = await this.transport.send(0xb0, 0x01, 0x00, 0x00);\n if (response[0] !== 1) {\n throw new Error('Incorrect format return from getAppNameAndVersion.');\n }\n\n let i = 1;\n const nameLength = response[i] ?? 0;\n i += 1;\n\n const appName = response\n .slice(i, (i += nameLength))\n .toString(this.transportEncoding);\n\n const versionLength = response[i] ?? 0;\n i += 1;\n\n const version = response\n .slice(i, (i += versionLength))\n .toString(this.transportEncoding);\n\n return {\n appName,\n version,\n };\n }\n}\n"]} -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.d.ts -new file mode 100644 -index 0000000..7d3ab3d ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.d.ts -@@ -0,0 +1,41 @@ -+import type Transport from '@ledgerhq/hw-transport'; -+import { MetaMaskLedgerHwAppEth } from './ledger-hw-app'; -+export interface TransportMiddleware { -+ setTransport(transport: Transport): void; -+ getTransport(): Transport; -+ getEthApp(): MetaMaskLedgerHwAppEth; -+ dispose(): Promise; -+} -+/** -+ * LedgerTransportMiddleware is a middleware to communicate with the Ledger device via transport or LedgerHwAppEth -+ */ -+export declare class LedgerTransportMiddleware implements TransportMiddleware { -+ #private; -+ readonly mainAppName = "BOLOS"; -+ readonly ethAppName = "Ethereum"; -+ readonly transportEncoding = "ascii"; -+ /** -+ * Method to close the transport connection. -+ * -+ */ -+ dispose(): Promise; -+ /** -+ * Method to set the transport object. -+ * -+ * @param transport - The transport object for communicating with a Ledger hardware wallet. -+ */ -+ setTransport(transport: Transport): void; -+ /** -+ * Method to retrieve the transport object. -+ * -+ * @returns An generic interface for communicating with a Ledger hardware wallet. -+ */ -+ getTransport(): Transport; -+ /** -+ * Method to retrieve the eth app object. -+ * it create a new eth app instance if not exist. -+ * -+ * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation. -+ */ -+ getEthApp(): MetaMaskLedgerHwAppEth; -+} -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js -new file mode 100644 -index 0000000..4a3b05e ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js -@@ -0,0 +1,70 @@ -+"use strict"; -+var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { -+ if (kind === "m") throw new TypeError("Private method is not writable"); -+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); -+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); -+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -+}; -+var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { -+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); -+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); -+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -+}; -+var _LedgerTransportMiddleware_app, _LedgerTransportMiddleware_transport; -+Object.defineProperty(exports, "__esModule", { value: true }); -+exports.LedgerTransportMiddleware = void 0; -+const ledger_hw_app_1 = require("./ledger-hw-app"); -+/** -+ * LedgerTransportMiddleware is a middleware to communicate with the Ledger device via transport or LedgerHwAppEth -+ */ -+class LedgerTransportMiddleware { -+ constructor() { -+ this.mainAppName = 'BOLOS'; -+ this.ethAppName = 'Ethereum'; -+ this.transportEncoding = 'ascii'; -+ _LedgerTransportMiddleware_app.set(this, void 0); -+ _LedgerTransportMiddleware_transport.set(this, void 0); -+ } -+ /** -+ * Method to close the transport connection. -+ * -+ */ -+ async dispose() { -+ const transport = this.getTransport(); -+ await transport.close(); -+ } -+ /** -+ * Method to set the transport object. -+ * -+ * @param transport - The transport object for communicating with a Ledger hardware wallet. -+ */ -+ setTransport(transport) { -+ __classPrivateFieldSet(this, _LedgerTransportMiddleware_transport, transport, "f"); -+ } -+ /** -+ * Method to retrieve the transport object. -+ * -+ * @returns An generic interface for communicating with a Ledger hardware wallet. -+ */ -+ getTransport() { -+ if (!__classPrivateFieldGet(this, _LedgerTransportMiddleware_transport, "f")) { -+ throw new Error('Instance `transport` is not initialized.'); -+ } -+ return __classPrivateFieldGet(this, _LedgerTransportMiddleware_transport, "f"); -+ } -+ /** -+ * Method to retrieve the eth app object. -+ * it create a new eth app instance if not exist. -+ * -+ * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation. -+ */ -+ getEthApp() { -+ if (!__classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f")) { -+ __classPrivateFieldSet(this, _LedgerTransportMiddleware_app, new ledger_hw_app_1.MetaMaskLedgerHwAppEth(this.getTransport()), "f"); -+ } -+ return __classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f"); -+ } -+} -+exports.LedgerTransportMiddleware = LedgerTransportMiddleware; -+_LedgerTransportMiddleware_app = new WeakMap(), _LedgerTransportMiddleware_transport = new WeakMap(); -+//# sourceMappingURL=middleware.js.map -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js.map -new file mode 100644 -index 0000000..18d0992 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/middleware.js.map -@@ -0,0 +1 @@ -+{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/ledger-mobile-bridge/middleware.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAEA,mDAAyD;AAUzD;;GAEG;AACH,MAAa,yBAAyB;IAAtC;QACW,gBAAW,GAAG,OAAO,CAAC;QAEtB,eAAU,GAAG,UAAU,CAAC;QAExB,sBAAiB,GAAG,OAAO,CAAC;QAErC,iDAA8B;QAE9B,uDAAuB;IA4CzB,CAAC;IA1CC;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACtC,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,SAAoB;QAC/B,uBAAA,IAAI,wCAAc,SAAS,MAAA,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,YAAY;QACV,IAAI,CAAC,uBAAA,IAAI,4CAAW,EAAE;YACpB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;SAC7D;QACD,OAAO,uBAAA,IAAI,4CAAW,CAAC;IACzB,CAAC;IAED;;;;;OAKG;IACH,SAAS;QACP,IAAI,CAAC,uBAAA,IAAI,sCAAK,EAAE;YACd,uBAAA,IAAI,kCAAQ,IAAI,sCAAsB,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,MAAA,CAAC;SAC7D;QACD,OAAO,uBAAA,IAAI,sCAAK,CAAC;IACnB,CAAC;CACF;AArDD,8DAqDC","sourcesContent":["import type Transport from '@ledgerhq/hw-transport';\n\nimport { MetaMaskLedgerHwAppEth } from './ledger-hw-app';\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface TransportMiddleware {\n setTransport(transport: Transport): void;\n getTransport(): Transport;\n getEthApp(): MetaMaskLedgerHwAppEth;\n dispose(): Promise;\n}\n\n/**\n * LedgerTransportMiddleware is a middleware to communicate with the Ledger device via transport or LedgerHwAppEth\n */\nexport class LedgerTransportMiddleware implements TransportMiddleware {\n readonly mainAppName = 'BOLOS';\n\n readonly ethAppName = 'Ethereum';\n\n readonly transportEncoding = 'ascii';\n\n #app?: MetaMaskLedgerHwAppEth;\n\n #transport?: Transport;\n\n /**\n * Method to close the transport connection.\n *\n */\n async dispose(): Promise {\n const transport = this.getTransport();\n await transport.close();\n }\n\n /**\n * Method to set the transport object.\n *\n * @param transport - The transport object for communicating with a Ledger hardware wallet.\n */\n setTransport(transport: Transport): void {\n this.#transport = transport;\n }\n\n /**\n * Method to retrieve the transport object.\n *\n * @returns An generic interface for communicating with a Ledger hardware wallet.\n */\n getTransport(): Transport {\n if (!this.#transport) {\n throw new Error('Instance `transport` is not initialized.');\n }\n return this.#transport;\n }\n\n /**\n * Method to retrieve the eth app object.\n * it create a new eth app instance if not exist.\n *\n * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation.\n */\n getEthApp(): MetaMaskLedgerHwAppEth {\n if (!this.#app) {\n this.#app = new MetaMaskLedgerHwAppEth(this.getTransport());\n }\n return this.#app;\n }\n}\n"]} -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.d.ts b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.d.ts -new file mode 100644 -index 0000000..2deabaf ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.d.ts -@@ -0,0 +1,5 @@ -+export declare type GetAppNameAndVersionResponse = { -+ appName: string; -+ version: string; -+}; -+export declare type LedgerMobileBridgeOptions = Record; -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js -new file mode 100644 -index 0000000..cd671e5 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js -@@ -0,0 +1,3 @@ -+"use strict"; -+Object.defineProperty(exports, "__esModule", { value: true }); -+//# sourceMappingURL=type.js.map -\ No newline at end of file -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js.map b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js.map -new file mode 100644 -index 0000000..5f32af5 ---- /dev/null -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-mobile-bridge/type.js.map -@@ -0,0 +1 @@ -+{"version":3,"file":"type.js","sourceRoot":"","sources":["../../src/ledger-mobile-bridge/type.ts"],"names":[],"mappings":"","sourcesContent":["export type GetAppNameAndVersionResponse = {\n appName: string;\n version: string;\n};\n\nexport type LedgerMobileBridgeOptions = Record;\n"]} -\ No newline at end of file diff --git a/yarn.lock b/yarn.lock index bbd20e78ead..da3b4146c29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1863,7 +1863,7 @@ "@ethereumjs/common" "^2.6.4" ethereumjs-util "^7.1.5" -"@ethereumjs/tx@^4.0.2", "@ethereumjs/tx@^4.1.1", "@ethereumjs/tx@^4.1.2", "@ethereumjs/tx@^4.2.0": +"@ethereumjs/tx@^4.0.2", "@ethereumjs/tx@^4.1.2", "@ethereumjs/tx@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-4.2.0.tgz#5988ae15daf5a3b3c815493bc6b495e76009e853" integrity sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw== @@ -1882,7 +1882,7 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -2119,7 +2119,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.5.0", "@ethersproject/rlp@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== @@ -3378,6 +3378,13 @@ dependencies: invariant "2" +"@ledgerhq/cryptoassets@^6.26.1": + version "6.37.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.37.0.tgz#302833777bcd210809ca7820afb82cff8da5c296" + integrity sha512-xwrDKTS9koQBNNzc7CqgV6zfGHvNFWJjlIL0Kc4O4DVWYR2vUdztUHcvwHD1KPjxNYhVnsgIopmtq47fHt3nMg== + dependencies: + invariant "2" + "@ledgerhq/devices@^5.26.0", "@ledgerhq/devices@^5.51.1": version "5.51.1" resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.51.1.tgz#d741a4a5d8f17c2f9d282fd27147e6fe1999edb7" @@ -3388,12 +3395,12 @@ rxjs "6" semver "^7.3.5" -"@ledgerhq/devices@^8.2.1", "@ledgerhq/devices@^8.2.2": - version "8.2.2" - resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.2.2.tgz#d6d758182d690ad66e14f88426c448e8c54d259d" - integrity sha512-SKahGA4p0mZ3ovypOJ2wa5mUvUkArE3HBrwWKYf+cRs+t/Licp3OJfhj+DHIxP3AfyH2xR6CFFWECYHeKwGsDQ== +"@ledgerhq/devices@^8.2.1", "@ledgerhq/devices@^8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.3.0.tgz#a1e1a21608e162fb3a512f57863bf9842b29493f" + integrity sha512-h5Scr+yIae8yjPOViCHLdMjpqn4oC2Whrsq8LinRxe48LEGMdPqSV1yY7+3Ch827wtzNpMv+/ilKnd8rY+rTlg== dependencies: - "@ledgerhq/errors" "^6.16.3" + "@ledgerhq/errors" "^6.16.4" "@ledgerhq/logs" "^6.12.0" rxjs "^7.8.1" semver "^7.3.5" @@ -3403,10 +3410,10 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== -"@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.3": - version "6.16.3" - resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.3.tgz#646f68cc7e6e8d5126bce1ca06140c5ad963bee8" - integrity sha512-3w7/SJVXOPa9mpzyll7VKoKnGwDD3BzWgN1Nom8byR40DiQvOKjHX+kKQausCedTHVNBn9euzPCNsftZ9+mxfw== +"@ledgerhq/errors@^6.10.0", "@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.4": + version "6.16.4" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.4.tgz#a38baffe8b096d9fff3ad839cadb55704c8d8e7b" + integrity sha512-M57yFaLYSN+fZCX0E0zUqOmrV6eipK+s5RhijHoUNlHUqrsvUz7iRQgpd5gRgHB5VkIjav7KdaZjKiWGcHovaQ== "@ledgerhq/hw-app-eth@5.27.2": version "5.27.2" @@ -3419,6 +3426,20 @@ bignumber.js "^9.0.1" rlp "^2.2.6" +"@ledgerhq/hw-app-eth@6.26.1": + version "6.26.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.26.1.tgz#c807087a563c4e1fb539116344ce114f5c84286b" + integrity sha512-maA5h+i92uoB+LXhkix1ZZWqI1qAxLBI5vEncuAbJubIQaJVv/BIlR3oAlZ+slNcq+9QUZ8vnzjRksn67kvoYA== + dependencies: + "@ethersproject/abi" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ledgerhq/cryptoassets" "^6.26.1" + "@ledgerhq/errors" "^6.10.0" + "@ledgerhq/hw-transport" "^6.24.1" + "@ledgerhq/logs" "^6.10.0" + axios "^0.26.0" + bignumber.js "^9.0.2" + "@ledgerhq/hw-transport-node-hid-noevents@^5.26.0": version "5.51.1" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz#71f37f812e448178ad0bcc2258982150d211c1ab" @@ -3472,13 +3493,13 @@ "@ledgerhq/errors" "^5.50.0" events "^3.3.0" -"@ledgerhq/hw-transport@^6.30.4": - version "6.30.5" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.5.tgz#841c9e4bb3849536db110ca2894d693d55bf54fd" - integrity sha512-JMl//7BgPBvWxrWyMu82jj6JEYtsQyOyhYtonWNgtxn6KUZWht3gU4gxmLpeIRr+DiS7e50mW7m3GA+EudZmmA== +"@ledgerhq/hw-transport@^6.24.1", "@ledgerhq/hw-transport@^6.30.4": + version "6.30.6" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.6.tgz#c6d84672ac4828f311831998f4101ea205215a6d" + integrity sha512-fT0Z4IywiuJuZrZE/+W0blkV5UCotDPFTYKLkKCLzYzuE6javva7D/ajRaIeR+hZ4kTmKF4EqnsmDCXwElez+w== dependencies: - "@ledgerhq/devices" "^8.2.2" - "@ledgerhq/errors" "^6.16.3" + "@ledgerhq/devices" "^8.3.0" + "@ledgerhq/errors" "^6.16.4" "@ledgerhq/logs" "^6.12.0" events "^3.3.0" @@ -3487,7 +3508,7 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== -"@ledgerhq/logs@^6.12.0": +"@ledgerhq/logs@^6.10.0", "@ledgerhq/logs@^6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.12.0.tgz#ad903528bf3687a44da435d7b2479d724d374f5d" integrity sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA== @@ -3856,15 +3877,15 @@ "@metamask/obs-store" "^8.1.0" "@metamask/utils" "^8.2.0" -"@metamask/eth-ledger-bridge-keyring@3.0.0": +"@metamask/eth-ledger-bridge-keyring@file:./package.tgz": version "3.0.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-ledger-bridge-keyring/-/eth-ledger-bridge-keyring-3.0.0.tgz#cfa5d406bfb3d50397d7fc890ceaae614eb2245a" - integrity sha512-22hMOT8wle7FI9n5kB2rkI2TsGn7uqul6eaXInJmxu+IBGxdn81Doduy6g+WYDNUAj1XEfxfK2LOzGPKBqNIFQ== + resolved "file:./package.tgz#1d02854e64b81aeda8de597ecd9d02400318022c" dependencies: "@ethereumjs/rlp" "^4.0.0" - "@ethereumjs/tx" "^4.1.1" + "@ethereumjs/tx" "^4.2.0" "@ethereumjs/util" "^8.0.0" - "@metamask/eth-sig-util" "^7.0.0" + "@ledgerhq/hw-app-eth" "6.26.1" + "@metamask/eth-sig-util" "^7.0.1" hdkey "^2.1.0" "@metamask/eth-query@^3.0.1": @@ -11600,7 +11621,7 @@ axios-retry@^3.1.2: "@babel/runtime" "^7.15.4" is-retry-allowed "^2.2.0" -axios@1.4.0, axios@1.6.0, axios@^0.27.0, axios@^0.x, axios@^1.6.7: +axios@1.4.0, axios@1.6.0, axios@^0.26.0, axios@^0.27.0, axios@^0.x, axios@^1.6.7: version "1.6.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102" integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg== @@ -11909,7 +11930,7 @@ bignumber.js@^7.2.1: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== -bignumber.js@^9.0.1: +bignumber.js@^9.0.1, bignumber.js@^9.0.2: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== From ee7efd4e10a39c5595cf93534c8ff76773547c15 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 22 Apr 2024 14:18:37 +0100 Subject: [PATCH 06/75] feat: revert the ledger.ts code in hardwareWallet folder. --- app/core/Ledger/Ledger.ts | 2 +- app/util/hardwareWallet/hardwareWallets/ledger.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index c3e24905bfa..92d22ff728f 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -120,7 +120,7 @@ export const forgetLedger = async (): Promise => { */ export const getDeviceId = async (): Promise => { const ledgerKeyring = await getLedgerKeyring(); - return ledgerKeyring.deviceId; + return ledgerKeyring.getDeviceId(); }; /** diff --git a/app/util/hardwareWallet/hardwareWallets/ledger.ts b/app/util/hardwareWallet/hardwareWallets/ledger.ts index d4d43321266..66aeed541cd 100644 --- a/app/util/hardwareWallet/hardwareWallets/ledger.ts +++ b/app/util/hardwareWallet/hardwareWallets/ledger.ts @@ -1,5 +1,6 @@ import { createNavigationDetails } from '../../navigation/navUtils'; import Routes from '../../../constants/navigation/Routes'; +import { getDeviceId } from '../../../core/Ledger/Ledger'; export interface LedgerSignModelNavParams { messageParams: any; @@ -8,9 +9,16 @@ export interface LedgerSignModelNavParams { type: any; } -export const signModalNavDetail = async (params: LedgerSignModelNavParams) => - createNavigationDetails( +export interface LedgerMessageSignModalParams extends LedgerSignModelNavParams { + deviceId: any; +} + +export const signModalNavDetail = async (params: LedgerSignModelNavParams) => { + const deviceId = await getDeviceId(); + return createNavigationDetails( Routes.LEDGER_MESSAGE_SIGN_MODAL, )({ ...params, + deviceId, }); +}; From 56f9229f21641247b8a6a3a14619445954de38d4 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 23 Apr 2024 16:10:43 +0100 Subject: [PATCH 07/75] feat: workable version with add multiple accounts and forget devices. Still need to refactory the code to remove unused functions. --- app/components/Nav/App/index.js | 7 +- .../LedgerConfirmationModal.test.tsx | 9 +- .../LedgerModals/LedgerConfirmationModal.tsx | 2 - .../ConnectHardware/SelectHardware/index.tsx | 15 +- .../LedgerAccountInfo/LedgerSelectAccount.tsx | 187 ++++++++++++++++++ app/components/Views/LedgerConnect/index.tsx | 41 ++-- app/core/Ledger/Ledger.test.ts | 63 +++--- app/core/Ledger/Ledger.ts | 56 +++--- package.tgz | Bin 33606 -> 0 bytes .../@metamask+keyring-controller+9.0.0.patch | 46 ++++- 10 files changed, 314 insertions(+), 112 deletions(-) create mode 100644 app/components/Views/LedgerAccountInfo/LedgerSelectAccount.tsx diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 52c27afd368..774f4e410b5 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -104,6 +104,7 @@ import { MetaMetrics } from '../../../core/Analytics'; import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics'; import generateDeviceAnalyticsMetaData from '../../../util/metrics/DeviceAnalyticsMetaData/generateDeviceAnalyticsMetaData'; import generateUserSettingsAnalyticsMetaData from '../../../util/metrics/UserSettingsAnalyticsMetaData/generateUserProfileAnalyticsMetaData'; +import LedgerSelectAccount from '../../Views/LedgerAccountInfo/LedgerSelectAccount'; const clearStackNavigatorOptions = { headerShown: false, @@ -408,6 +409,7 @@ const App = ({ userLoggedIn }) => { } } } + initSDKConnect().catch((err) => { Logger.error(err, 'Error initializing SDKConnect'); }); @@ -649,7 +651,10 @@ const App = ({ userLoggedIn }) => { const LedgerConnectFlow = () => ( - + ); diff --git a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx index 5f5dbe702a0..d21e716827e 100644 --- a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx +++ b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx @@ -15,16 +15,11 @@ import { BluetoothPermissionErrors, LedgerCommunicationErrors, } from '../../../core/Ledger/ledgerErrors'; -import { unlockLedgerDefaultAccount } from '../../../core/Ledger/Ledger'; import { strings } from '../../../../locales/i18n'; import { useMetrics } from '../../hooks/useMetrics'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { fireEvent } from '@testing-library/react-native'; -jest.mock('../../../core/Ledger/Ledger', () => ({ - unlockLedgerDefaultAccount: jest.fn(), -})); - jest.mock('../../hooks/Ledger/useBluetooth', () => ({ __esModule: true, default: jest.fn(), @@ -340,7 +335,6 @@ describe('LedgerConfirmationModal', () => { it('calls onConfirmation when ledger commands are being sent and confirmed have been received.', async () => { const onConfirmation = jest.fn(); - unlockLedgerDefaultAccount.mockReturnValue(Promise.resolve(true)); useLedgerBluetooth.mockReturnValue({ isSendingLedgerCommands: true, isAppLaunchConfirmationNeeded: false, @@ -359,11 +353,10 @@ describe('LedgerConfirmationModal', () => { // eslint-disable-next-line @typescript-eslint/no-empty-function await act(async () => {}); - expect(unlockLedgerDefaultAccount).toHaveBeenCalled(); expect(onConfirmation).toHaveBeenCalled(); }); - it('logs LEDGER_HARDWARE_WALLET_ERROR thrown by unlockLedgerDefaultAccount', async () => { + it('logs LEDGER_HARDWARE_WALLET_ERROR thrown', async () => { const onConfirmation = jest.fn(); const ledgerLogicToRun = jest.fn(); diff --git a/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx b/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx index d6bc54187f6..ad7df903de6 100644 --- a/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx +++ b/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx @@ -10,7 +10,6 @@ import ConfirmationStep from './Steps/ConfirmationStep'; import ErrorStep from './Steps/ErrorStep'; import OpenETHAppStep from './Steps/OpenETHAppStep'; import SearchingForDeviceStep from './Steps/SearchingForDeviceStep'; -import { unlockLedgerDefaultAccount } from '../../../core/Ledger/Ledger'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { useMetrics } from '../../../components/hooks/useMetrics'; import { @@ -71,7 +70,6 @@ const LedgerConfirmationModal = ({ const connectLedger = () => { try { ledgerLogicToRun(async () => { - await unlockLedgerDefaultAccount(false); await onConfirmation(); }); } catch (_e) { diff --git a/app/components/Views/ConnectHardware/SelectHardware/index.tsx b/app/components/Views/ConnectHardware/SelectHardware/index.tsx index 86152efcb32..a259d92f200 100644 --- a/app/components/Views/ConnectHardware/SelectHardware/index.tsx +++ b/app/components/Views/ConnectHardware/SelectHardware/index.tsx @@ -16,7 +16,6 @@ import Text, { } from '../../../../component-library/components/Texts/Text'; import Routes from '../../../../constants/navigation/Routes'; import { MetaMetricsEvents } from '../../../../core/Analytics'; -import { getLedgerKeyring } from '../../../../core/Ledger/Ledger'; import { fontStyles } from '../../../../styles/common'; import { mockTheme, @@ -105,23 +104,11 @@ const SelectHardwareWallet = () => { }; const navigateToConnectLedger = async () => { - const ledgerKeyring = await getLedgerKeyring(); - const accounts = await ledgerKeyring.getAccounts(); - trackEvent(MetaMetricsEvents.CONNECT_LEDGER, { device_type: 'Ledger', }); - if (accounts.length === 0) { - navigation.navigate(Routes.HW.CONNECT_LEDGER); - } else { - navigation.navigate(Routes.HW.LEDGER_ACCOUNT, { - screen: Routes.HW.LEDGER_ACCOUNT, - params: { - accounts, - }, - }); - } + navigation.navigate(Routes.HW.CONNECT_LEDGER); }; const renderHardwareButton = (image: any, onPress: any) => ( diff --git a/app/components/Views/LedgerAccountInfo/LedgerSelectAccount.tsx b/app/components/Views/LedgerAccountInfo/LedgerSelectAccount.tsx new file mode 100644 index 00000000000..f9802c9f372 --- /dev/null +++ b/app/components/Views/LedgerAccountInfo/LedgerSelectAccount.tsx @@ -0,0 +1,187 @@ +import React, { + Fragment, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; +import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import Engine from '../../../core/Engine'; +import AccountSelector from '../../UI/HardwareWallet/AccountSelector'; +import BlockingActionModal from '../../UI/BlockingActionModal'; +import { strings } from '../../../../locales/i18n'; +import { MetaMetricsEvents } from '../../../core/Analytics'; +import Device from '../../../util/device'; +import { fontStyles } from '../../../styles/common'; +import Logger from '../../../util/Logger'; +import { useAssetFromTheme, useTheme } from '../../../util/theme'; +import useMetrics from '../../hooks/useMetrics/useMetrics'; +import ledgerDeviceLightImage from 'images/ledger-device-light.png'; +import ledgerDeviceDarkImage from 'images/ledger-device-dark.png'; +import { + forgetLedger, + getLedgerAccountsByPage, + getLedgerKeyring, +} from '../../../core/Ledger/Ledger'; +import LedgerConnect from '../LedgerConnect'; +import { setReloadAccounts } from '../../../actions/accounts'; +import { StackActions } from '@react-navigation/native'; +import { useDispatch } from 'react-redux'; + +interface ILedgerSelectAccountProps { + navigation: any; +} + +const createStyles = (colors: any) => + StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'column', + alignItems: 'center', + }, + ledgerIcon: { + width: 60, + height: 60, + }, + header: { + marginTop: Device.isIphoneX() ? 50 : 20, + flexDirection: 'row', + width: '100%', + paddingHorizontal: 32, + alignItems: 'center', + }, + navbarRightButton: { + flexDirection: 'row', + justifyContent: 'flex-end', + height: 48, + width: 48, + flex: 1, + }, + closeIcon: { + fontSize: 28, + color: colors.text.default, + }, + error: { + ...fontStyles.normal, + fontSize: 14, + color: colors.red, + }, + text: { + color: colors.text.default, + fontSize: 14, + ...fontStyles.normal, + }, + }); + +const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { + const dispatch = useDispatch(); + const { colors } = useTheme(); + const { trackEvent } = useMetrics(); + const styles = createStyles(colors); + const ledgerThemedImage = useAssetFromTheme( + ledgerDeviceLightImage, + ledgerDeviceDarkImage, + ); + + const KeyringController = useMemo(() => { + const { KeyringController: controller } = Engine.context as any; + return controller; + }, []); + + const [blockingModalVisible, setBlockingModalVisible] = useState(false); + const [accounts, setAccounts] = useState< + { address: string; index: number; balance: string }[] + >([]); + + const [existingAccounts, setExistingAccounts] = useState([]); + + useEffect(() => { + KeyringController.getAccounts().then((value: string[]) => { + setExistingAccounts(value); + }); + }, [KeyringController]); + + const onConnectHardware = useCallback(async () => { + trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { + device_type: 'Ledger', + }); + const _accounts = await getLedgerAccountsByPage(0); + setAccounts(_accounts); + }, [trackEvent]); + + const nextPage = useCallback(async () => { + const _accounts = await getLedgerAccountsByPage(1); + setAccounts(_accounts); + }, []); + + const prevPage = useCallback(async () => { + const _accounts = await getLedgerAccountsByPage(-1); + setAccounts(_accounts); + }, []); + + // eslint-disable-next-line @typescript-eslint/no-empty-function + const onToggle = useCallback(() => {}, []); + + const onUnlock = useCallback( + async (accountIndexes: number[]) => { + setBlockingModalVisible(true); + const keyring = await getLedgerKeyring(); + try { + for (const index of accountIndexes) { + await KeyringController.unlockLedgerWalletAccount(index, keyring); + } + } catch (err) { + Logger.log('Error: Connecting QR hardware wallet', err); + } + setBlockingModalVisible(false); + navigation.pop(2); + }, + [KeyringController, navigation], + ); + + const onForget = useCallback(async () => { + await forgetLedger(); + dispatch(setReloadAccounts(true)); + trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { + device_type: 'Ledger', + }); + navigation.dispatch(StackActions.pop(2)); + }, [dispatch, navigation, trackEvent]); + + return accounts.length <= 0 ? ( + + ) : ( + + + + + + + + + + + {strings('connect_qr_hardware.please_wait')} + + + + ); +}; + +export default LedgerSelectAccount; diff --git a/app/components/Views/LedgerConnect/index.tsx b/app/components/Views/LedgerConnect/index.tsx index f5901ee06e7..3e5ed5416af 100644 --- a/app/components/Views/LedgerConnect/index.tsx +++ b/app/components/Views/LedgerConnect/index.tsx @@ -1,17 +1,16 @@ import React, { useEffect, useMemo, useState } from 'react'; import { - View, - StyleSheet, + ActivityIndicator, Image, SafeAreaView, + StyleSheet, TextStyle, - ActivityIndicator, + View, } from 'react-native'; -import { StackActions, useNavigation } from '@react-navigation/native'; +import { useNavigation } from '@react-navigation/native'; import { Device as NanoDevice } from '@ledgerhq/react-native-hw-transport-ble/lib/types'; import { useDispatch } from 'react-redux'; import { strings } from '../../../../locales/i18n'; -import Engine from '../../../core/Engine'; import StyledButton from '../../../components/UI/StyledButton'; import Text from '../../../components/Base/Text'; import { @@ -28,15 +27,12 @@ import LedgerConnectionError, { LedgerConnectionErrorProps, } from './LedgerConnectionError'; import { getNavigationOptionsTitle } from '../../UI/Navbar'; -import { unlockLedgerDefaultAccount } from '../../../core/Ledger/Ledger'; -import { MetaMetricsEvents } from '../../../core/Analytics'; import { LEDGER_SUPPORT_LINK } from '../../../constants/urls'; import ledgerDeviceDarkImage from '../../../images/ledger-device-dark.png'; import ledgerDeviceLightImage from '../../../images/ledger-device-light.png'; import ledgerConnectLightImage from '../../../images/ledger-connect-light.png'; import ledgerConnectDarkImage from '../../../images/ledger-connect-dark.png'; -import { useMetrics } from '../../../components/hooks/useMetrics'; import { getSystemVersion } from 'react-native-device-info'; import { LedgerCommunicationErrors } from '../../../core/Ledger/ledgerErrors'; @@ -111,10 +107,14 @@ const createStyles = (theme: any) => }, }); -const LedgerConnect = () => { - const { AccountTrackerController } = Engine.context as any; +interface LedgerConnectProps { + onConnectLedger: () => void; +} + +const LedgerConnect = ({ onConnectLedger }: LedgerConnectProps) => { + // const { AccountTrackerController } = Engine.context as any; const theme = useAppThemeFromContext() ?? mockTheme; - const { trackEvent } = useMetrics(); + // const { trackEvent } = useMetrics(); const navigation = useNavigation(); const styles = useMemo(() => createStyles(theme), [theme]); const [selectedDevice, setSelectedDevice] = useState(null); @@ -140,16 +140,17 @@ const LedgerConnect = () => { const connectLedger = () => { setLoading(true); - trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { - device_type: 'Ledger', - }); + // trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { + // device_type: 'Ledger', + // }); ledgerLogicToRun(async () => { - const account = await unlockLedgerDefaultAccount(true); - await AccountTrackerController.syncBalanceWithAddresses([account]); - trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { - device_type: 'Ledger', - }); - navigation.dispatch(StackActions.pop(2)); + // const account = await unlockLedgerDefaultAccount(true); + // await AccountTrackerController.syncBalanceWithAddresses([account]); + // trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { + // device_type: 'Ledger', + // }); + // navigation.dispatch(StackActions.pop(2)); + onConnectLedger(); }); }; diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index a8f2c6671d6..d14b8b33757 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -6,22 +6,28 @@ import { closeRunningAppOnLedger, forgetLedger, ledgerSignTypedMessage, - unlockLedgerDefaultAccount, } from './Ledger'; import Engine from '../../core/Engine'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; const ledgerKeyring = { - setTransport: jest.fn(), getAppAndVersion: jest.fn().mockResolvedValue({ appName: 'appName' }), getDefaultAccount: jest.fn().mockResolvedValue('defaultAccount'), openEthApp: jest.fn(), quitApp: jest.fn(), forgetDevice: jest.fn(), deserialize: jest.fn(), + setHdPath: jest.fn(), + setDeviceId: jest.fn(), + bridge: { + updateTransportMethod: jest.fn(), + getAppNameAndVersion: jest.fn().mockResolvedValue({ appName: 'appName' }), + openEthApp: jest.fn(), + closeApps: jest.fn(), + }, + deviceId: 'deviceId', getName: jest.fn().mockResolvedValue('name'), - openEthereumAppOnLedger: jest.fn(), }; describe('Ledger core', () => { @@ -78,57 +84,42 @@ describe('Ledger core', () => { describe('connectLedgerHardware', () => { const mockTransport = 'foo' as unknown as BleTransport; - it('should call keyring.setTransport', async () => { - await connectLedgerHardware(mockTransport); - expect(ledgerKeyring.setTransport).toHaveBeenCalled(); + it('should call keyring.bridge.updateTransportMethod', async () => { + await connectLedgerHardware(mockTransport, 'bar'); + expect(ledgerKeyring.setHdPath).toHaveBeenCalledWith("m/44'/60'/0'/0"); + expect(ledgerKeyring.setDeviceId).toHaveBeenCalledWith('bar'); + expect(ledgerKeyring.bridge.updateTransportMethod).toHaveBeenCalledWith( + mockTransport, + ); }); - it('should call keyring.getAppAndVersion', async () => { - await connectLedgerHardware(mockTransport); - expect(ledgerKeyring.getAppAndVersion).toHaveBeenCalled(); + it('should call keyring.bridge.getAppAndVersion', async () => { + await connectLedgerHardware(mockTransport, 'bar'); + expect(ledgerKeyring.setHdPath).toHaveBeenCalledWith("m/44'/60'/0'/0"); + expect(ledgerKeyring.setDeviceId).toHaveBeenCalledWith('bar'); + expect(ledgerKeyring.bridge.updateTransportMethod).toHaveBeenCalledWith( + mockTransport, + ); + expect(ledgerKeyring.bridge.getAppNameAndVersion).toHaveBeenCalled(); }); it('should return app name', async () => { - const value = await connectLedgerHardware(mockTransport); + const value = await connectLedgerHardware(mockTransport, 'bar'); expect(value).toBe('appName'); }); }); - describe('unlockLedgerDefaultAccount', () => { - it('should not call KeyringController.addNewAccountForKeyring if isAccountImportReq is false', async () => { - const account = await unlockLedgerDefaultAccount(false); - expect(mockAddNewAccountForKeyring).not.toHaveBeenCalled(); - expect(ledgerKeyring.getDefaultAccount).toHaveBeenCalled(); - - expect(account).toEqual({ - address: 'defaultAccount', - balance: '0x0', - }); - }); - - it('should call KeyringController.addNewAccountForKeyring if isAccountImportReq is true', async () => { - const account = await unlockLedgerDefaultAccount(true); - expect(mockAddNewAccountForKeyring).toHaveBeenCalledWith(ledgerKeyring); - expect(ledgerKeyring.getDefaultAccount).toHaveBeenCalled(); - - expect(account).toEqual({ - address: 'defaultAccount', - balance: '0x0', - }); - }); - }); - describe('openEthereumAppOnLedger', () => { it('should call keyring.openEthApp', async () => { await openEthereumAppOnLedger(); - expect(ledgerKeyring.openEthApp).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.openEthApp).toHaveBeenCalled(); }); }); describe('closeRunningAppOnLedger', () => { it('should call keyring.quitApp', async () => { await closeRunningAppOnLedger(); - expect(ledgerKeyring.quitApp).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.closeApps).toHaveBeenCalled(); }); }); diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 92d22ff728f..8fdaa1cd173 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -45,7 +45,6 @@ export const connectLedgerHardware = async ( transport: BleTransport, deviceId: string, ): Promise => { - console.warn('connectLedgerHardware'); const keyring = await getLedgerKeyring(); keyring.setHdPath("m/44'/60'/0'/0"); keyring.setDeviceId(deviceId); @@ -56,32 +55,6 @@ export const connectLedgerHardware = async ( return appName; }; -/** - * Retrieve the first account from the Ledger device. - * @param isAccountImportReq - Whether we need to import a ledger account by calling addNewAccountForKeyring - * @returns The default (first) account on the device - */ -export const unlockLedgerDefaultAccount = async ( - isAccountImportReq: boolean, -): Promise<{ - address: string; - balance: string; -}> => { - const keyringController = Engine.context.KeyringController; - - const keyring = await getLedgerKeyring(); - - if (isAccountImportReq) { - await keyringController.addNewAccountForKeyring(keyring); - } - const address = await keyring.getFirstPage(); - - return { - address: address[0].address, - balance: `0x0`, - }; -}; - /** * Automatically opens the Ethereum app on the Ledger device. */ @@ -101,7 +74,7 @@ export const closeRunningAppOnLedger = async (): Promise => { }; /** - * Forgets the ledger keyring's previous device specific state. + * Forgets the ledger keyring previous device specific state. */ export const forgetLedger = async (): Promise => { const { KeyringController, PreferencesController } = Engine.context; @@ -123,6 +96,33 @@ export const getDeviceId = async (): Promise => { return ledgerKeyring.getDeviceId(); }; +export const getLedgerAccountsByPage = async ( + page: number, +): Promise<{ balance: string; address: string; index: number }[]> => { + try { + const keyring = await getLedgerKeyring(); + let accounts; + switch (page) { + case -1: + accounts = await keyring.getPreviousPage(); + break; + case 1: + accounts = await keyring.getNextPage(); + break; + default: + accounts = await keyring.getFirstPage(); + } + return accounts.map((account) => ({ + ...account, + balance: '0x0', + })); + } catch (e) { + // TODO: Add test case for when keyring throws + /* istanbul ignore next */ + throw new Error(`Unspecified error when connect QR Hardware, ${e}`); + } +}; + /** * signTypedMessage from Ledger Keyring * diff --git a/package.tgz b/package.tgz index dc88d4864a78e44146950c95d3d0426f858606a5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 33606 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 { ++ if (!oldAccounts.includes(address)) { ++ if (this.setAccountLabel) { ++ this.setAccountLabel(address, `Ledger: ${index}`); ++ } ++ this.setSelectedAddress(address); ++ } ++ }); ++ yield __classPrivateFieldGet(this, _KeyringController_keyring, "f").persistAllKeyrings(); ++ }); ++ } ++ + unlockQRHardwareWalletAccount(index) { + return __awaiter(this, void 0, void 0, function* () { + const keyring = yield this.getOrAddQRKeyring(); +@@ -785,15 +818,25 @@ class KeyringController extends base_controller_1.BaseControllerV2 { return (yield __classPrivateFieldGet(this, _KeyringController_keyring, "f").getKeyringForAccount(account)).type; }); } @@ -11,7 +51,7 @@ index da9523a..2cb136d 100644 + * This patch addresses an issue regarding the forget device functionality. It + * improves the logic to correctly remove the QR accounts and update the + * identities as needed. -+ * ++ * + * Solved in https://github.com/MetaMask/core/pull/3641 + * =============================================================================== + */ From f22b625f6b5c901bfefbf1083bf038da10dc8e5c Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 23 Apr 2024 16:11:38 +0100 Subject: [PATCH 08/75] feat: workable version with add multiple accounts and forget devices. Still need to refactory the code to remove unused functions. --- app/core/Ledger/Ledger.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 8fdaa1cd173..74eada9a390 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -117,9 +117,8 @@ export const getLedgerAccountsByPage = async ( balance: '0x0', })); } catch (e) { - // TODO: Add test case for when keyring throws /* istanbul ignore next */ - throw new Error(`Unspecified error when connect QR Hardware, ${e}`); + throw new Error(`Unspecified error when connect Ledger Hardware, ${e}`); } }; From fe9a06a2a15fa321c047f626432ba15819f00f43 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 24 Apr 2024 01:54:32 +0100 Subject: [PATCH 09/75] feat: Fix the unit tests in LedgerConnect --- .../Views/LedgerConnect/index.test.tsx | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/app/components/Views/LedgerConnect/index.test.tsx b/app/components/Views/LedgerConnect/index.test.tsx index 27d86e2318d..afc604fa44c 100644 --- a/app/components/Views/LedgerConnect/index.test.tsx +++ b/app/components/Views/LedgerConnect/index.test.tsx @@ -72,6 +72,8 @@ jest.mock('react-native-permissions', () => ({ })); describe('LedgerConnect', () => { + const onConfirmationComplete = jest.fn(); + const checkLedgerCommunicationErrorFlow = function ( ledgerCommunicationError: LedgerCommunicationErrors, expectedTitle: string, @@ -84,7 +86,9 @@ describe('LedgerConnect', () => { error: ledgerCommunicationError, }); - const { getByText } = renderWithProvider(); + const { getByText } = renderWithProvider( + , + ); expect(getByText(expectedTitle)).toBeTruthy(); expect(getByText(expectedErrorBody)).toBeTruthy(); @@ -130,7 +134,9 @@ describe('LedgerConnect', () => { }); it('render matches latest snapshot', () => { - const wrapper = renderWithProvider(); + const wrapper = renderWithProvider( + , + ); expect(wrapper).toMatchSnapshot(); }); @@ -146,7 +152,9 @@ describe('LedgerConnect', () => { ledgerLogicToRun.mockImplementation((callback) => callback()); - const { getByTestId } = renderWithProvider(); + const { getByTestId } = renderWithProvider( + , + ); const continueButton = getByTestId('add-network-button'); fireEvent.press(continueButton); @@ -201,14 +209,18 @@ describe('LedgerConnect', () => { error: LedgerCommunicationErrors.LedgerHasPendingConfirmation, }); - renderWithProvider(); + renderWithProvider( + , + ); expect(navigate).toHaveBeenNthCalledWith(1, 'SelectHardwareWallet'); }); it('displays android 12+ permission text on android 12+ device', () => { getSystemVersion.mockReturnValue('13'); - const { getByText } = renderWithProvider(); + const { getByText } = renderWithProvider( + , + ); expect( getByText( strings('ledger.ledger_reminder_message_step_four_Androidv12plus'), @@ -218,7 +230,9 @@ describe('LedgerConnect', () => { it('displays android 11 permission text on android 11 device', () => { getSystemVersion.mockReturnValue('11'); - const { getByText } = renderWithProvider(); + const { getByText } = renderWithProvider( + , + ); expect( getByText(strings('ledger.ledger_reminder_message_step_four')), ).toBeTruthy(); @@ -232,7 +246,9 @@ describe('LedgerConnect', () => { dispatch: jest.fn(), }); - const { getByText } = renderWithProvider(); + const { getByText } = renderWithProvider( + , + ); const installInstructionsLink = getByText( strings('ledger.how_to_install_eth_app'), ); @@ -257,9 +273,13 @@ describe('LedgerConnect', () => { error: LedgerCommunicationErrors.FailedToOpenApp, }); - renderWithProvider(); + renderWithProvider( + , + ); - const { getByTestId } = renderWithProvider(); + const { getByTestId } = renderWithProvider( + , + ); const retryButton = getByTestId('add-network-button'); fireEvent.press(retryButton); From 0cd5536e6081bd9046237b06eb245488bff3b927 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 24 Apr 2024 02:27:32 +0100 Subject: [PATCH 10/75] feat: Fix the lint error and redundant imports --- app/components/Nav/App/index.js | 1 - app/util/Logger/index.test.ts | 6 ------ 2 files changed, 7 deletions(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 774f4e410b5..8391a675573 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -68,7 +68,6 @@ import ImportPrivateKeySuccess from '../../Views/ImportPrivateKeySuccess'; import ConnectQRHardware from '../../Views/ConnectQRHardware'; import SelectHardwareWallet from '../../Views/ConnectHardware/SelectHardware'; import LedgerAccountInfo from '../../Views/LedgerAccountInfo'; -import LedgerConnect from '../../Views/LedgerConnect'; import { AUTHENTICATION_APP_TRIGGERED_AUTH_NO_CREDENTIALS } from '../../../constants/error'; import { UpdateNeeded } from '../../../components/UI/UpdateNeeded'; import { EnableAutomaticSecurityChecksModal } from '../../../components/UI/EnableAutomaticSecurityChecksModal'; diff --git a/app/util/Logger/index.test.ts b/app/util/Logger/index.test.ts index 44a9214da6d..38943d5ddfc 100644 --- a/app/util/Logger/index.test.ts +++ b/app/util/Logger/index.test.ts @@ -65,12 +65,6 @@ describe('Logger', () => { expect(mockedWithScope).toHaveBeenCalledTimes(1); }); - it('calls withScope if extra is passed in', async () => { - const testError = new Error('testError'); - await Logger.error(testError, 'extraMessage'); - expect(mockedWithScope).toHaveBeenCalledTimes(1); - }); - it('calls captureException when string is passed instead of Error object', async () => { const testError = 'testError' as any; await Logger.error(testError); From bd47961c52fa54d1ff24856016ff0f38584034bc Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Thu, 25 Apr 2024 04:07:29 +0100 Subject: [PATCH 11/75] feat: Try to fix the hash unmatch issue in @metamask/eth-ledger-keyring --- yarn.lock | 50 +++++++++++--------------------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/yarn.lock b/yarn.lock index da3b4146c29..27b57db86fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1842,7 +1842,7 @@ "@ethereumjs/util" "^8.1.0" crc-32 "^1.2.0" -"@ethereumjs/rlp@^4.0.0", "@ethereumjs/rlp@^4.0.1": +"@ethereumjs/rlp@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== @@ -1882,7 +1882,7 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -2119,7 +2119,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.5.0", "@ethersproject/rlp@^5.7.0": +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== @@ -3378,13 +3378,6 @@ dependencies: invariant "2" -"@ledgerhq/cryptoassets@^6.26.1": - version "6.37.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.37.0.tgz#302833777bcd210809ca7820afb82cff8da5c296" - integrity sha512-xwrDKTS9koQBNNzc7CqgV6zfGHvNFWJjlIL0Kc4O4DVWYR2vUdztUHcvwHD1KPjxNYhVnsgIopmtq47fHt3nMg== - dependencies: - invariant "2" - "@ledgerhq/devices@^5.26.0", "@ledgerhq/devices@^5.51.1": version "5.51.1" resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.51.1.tgz#d741a4a5d8f17c2f9d282fd27147e6fe1999edb7" @@ -3410,7 +3403,7 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== -"@ledgerhq/errors@^6.10.0", "@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.4": +"@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.4": version "6.16.4" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.4.tgz#a38baffe8b096d9fff3ad839cadb55704c8d8e7b" integrity sha512-M57yFaLYSN+fZCX0E0zUqOmrV6eipK+s5RhijHoUNlHUqrsvUz7iRQgpd5gRgHB5VkIjav7KdaZjKiWGcHovaQ== @@ -3426,20 +3419,6 @@ bignumber.js "^9.0.1" rlp "^2.2.6" -"@ledgerhq/hw-app-eth@6.26.1": - version "6.26.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.26.1.tgz#c807087a563c4e1fb539116344ce114f5c84286b" - integrity sha512-maA5h+i92uoB+LXhkix1ZZWqI1qAxLBI5vEncuAbJubIQaJVv/BIlR3oAlZ+slNcq+9QUZ8vnzjRksn67kvoYA== - dependencies: - "@ethersproject/abi" "^5.5.0" - "@ethersproject/rlp" "^5.5.0" - "@ledgerhq/cryptoassets" "^6.26.1" - "@ledgerhq/errors" "^6.10.0" - "@ledgerhq/hw-transport" "^6.24.1" - "@ledgerhq/logs" "^6.10.0" - axios "^0.26.0" - bignumber.js "^9.0.2" - "@ledgerhq/hw-transport-node-hid-noevents@^5.26.0": version "5.51.1" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz#71f37f812e448178ad0bcc2258982150d211c1ab" @@ -3493,7 +3472,7 @@ "@ledgerhq/errors" "^5.50.0" events "^3.3.0" -"@ledgerhq/hw-transport@^6.24.1", "@ledgerhq/hw-transport@^6.30.4": +"@ledgerhq/hw-transport@^6.30.4": version "6.30.6" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.6.tgz#c6d84672ac4828f311831998f4101ea205215a6d" integrity sha512-fT0Z4IywiuJuZrZE/+W0blkV5UCotDPFTYKLkKCLzYzuE6javva7D/ajRaIeR+hZ4kTmKF4EqnsmDCXwElez+w== @@ -3508,7 +3487,7 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== -"@ledgerhq/logs@^6.10.0", "@ledgerhq/logs@^6.12.0": +"@ledgerhq/logs@^6.12.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.12.0.tgz#ad903528bf3687a44da435d7b2479d724d374f5d" integrity sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA== @@ -3878,15 +3857,8 @@ "@metamask/utils" "^8.2.0" "@metamask/eth-ledger-bridge-keyring@file:./package.tgz": - version "3.0.0" - resolved "file:./package.tgz#1d02854e64b81aeda8de597ecd9d02400318022c" - dependencies: - "@ethereumjs/rlp" "^4.0.0" - "@ethereumjs/tx" "^4.2.0" - "@ethereumjs/util" "^8.0.0" - "@ledgerhq/hw-app-eth" "6.26.1" - "@metamask/eth-sig-util" "^7.0.1" - hdkey "^2.1.0" + version "0.0.0" + resolved "file:./package.tgz#da39a3ee5e6b4b0d3255bfef95601890afd80709" "@metamask/eth-query@^3.0.1": version "3.0.1" @@ -11621,7 +11593,7 @@ axios-retry@^3.1.2: "@babel/runtime" "^7.15.4" is-retry-allowed "^2.2.0" -axios@1.4.0, axios@1.6.0, axios@^0.26.0, axios@^0.27.0, axios@^0.x, axios@^1.6.7: +axios@1.4.0, axios@1.6.0, axios@^0.27.0, axios@^0.x, axios@^1.6.7: version "1.6.0" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102" integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg== @@ -11930,7 +11902,7 @@ bignumber.js@^7.2.1: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== -bignumber.js@^9.0.1, bignumber.js@^9.0.2: +bignumber.js@^9.0.1: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== @@ -17736,7 +17708,7 @@ hastscript@^5.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" -hdkey@^2.0.1, hdkey@^2.1.0: +hdkey@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.1.0.tgz#755b30b73f54e93c31919c1b2f19205a8e57cb92" integrity sha512-i9Wzi0Dy49bNS4tXXeGeu0vIcn86xXdPQUpEYg+SO1YiO8HtomjmmRMaRyqL0r59QfcD4PfVbSF3qmsWFwAemA== From 52b8967e5da9419d298f6fcdafe725e269245e20 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Thu, 25 Apr 2024 09:28:34 +0100 Subject: [PATCH 12/75] feat: Fix the build issue after the package.tgz file change. --- package.tgz | Bin 0 -> 33645 bytes yarn.lock | 50 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/package.tgz b/package.tgz index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..618651da86b67301fa5068f7ecde7bd8fa1babe1 100644 GIT binary patch literal 33645 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 Date: Thu, 25 Apr 2024 14:10:20 +0100 Subject: [PATCH 13/75] feat: Try to fix the `yarn setup` integrity check failed because checksum not match. also improve the unit tests for ledger.ts to cover all functions. --- app/core/Ledger/Ledger.test.ts | 107 ++++++++++++++++++++++++++++++++- package.tgz | Bin 33645 -> 33648 bytes yarn.lock | 4 +- 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index d14b8b33757..747cd8a61ca 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -6,6 +6,8 @@ import { closeRunningAppOnLedger, forgetLedger, ledgerSignTypedMessage, + getDeviceId, + getLedgerAccountsByPage, } from './Ledger'; import Engine from '../../core/Engine'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; @@ -26,8 +28,44 @@ const ledgerKeyring = { openEthApp: jest.fn(), closeApps: jest.fn(), }, - deviceId: 'deviceId', + getDeviceId: jest.fn().mockResolvedValue('deviceId'), getName: jest.fn().mockResolvedValue('name'), + getFirstPage: jest.fn().mockResolvedValue([ + { + balance: '0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', + index: 0, + }, + { + balance: '1', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', + index: 1, + }, + ]), + getPreviousPage: jest.fn().mockResolvedValue([ + { + balance: '2', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', + index: 2, + }, + { + balance: '3', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', + index: 3, + }, + ]), + getNextPage: jest.fn().mockResolvedValue([ + { + balance: '4', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', + index: 4, + }, + { + balance: '5', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', + index: 5, + }, + ]), }; describe('Ledger core', () => { @@ -138,6 +176,73 @@ describe('Ledger core', () => { }); }); + describe('getDeviceId', () => { + it('should return the device id', async () => { + const value = await getDeviceId(); + expect(value).toBe('deviceId'); + expect(ledgerKeyring.getDeviceId).toHaveBeenCalled(); + }); + }); + + describe('getLedgerAccountsByPage', () => { + it('should return first page accounts when page is 1', async () => { + const value = await getLedgerAccountsByPage(0); + expect(value).toEqual([ + { + balance: '0x0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', + index: 0, + }, + { + balance: '0x0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', + index: 1, + }, + ]); + + expect(ledgerKeyring.getFirstPage).toHaveBeenCalled(); + }); + + it('should return next page accounts when page is 1', async () => { + const value = await getLedgerAccountsByPage(1); + expect(value).toEqual([ + { + balance: '0x0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', + index: 4, + }, + { + balance: '0x0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', + index: 5, + }, + ]); + }); + + it('should return previous page accounts when page is -1', async () => { + const value = await getLedgerAccountsByPage(-1); + expect(value).toEqual([ + { + balance: '0x0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', + index: 2, + }, + { + balance: '0x0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', + index: 3, + }, + ]); + }); + + it('should throw an error when an unspecified error occurs', async () => { + ledgerKeyring.getFirstPage.mockRejectedValueOnce(new Error('error')); + await expect(getLedgerAccountsByPage(0)).rejects.toThrow( + 'Unspecified error when connect Ledger Hardware, Error: error', + ); + }); + }); + describe('ledgerSignTypedMessage', () => { it('should call signTypedMessage from keyring controller and return correct signature', async () => { const expectedArg = { diff --git a/package.tgz b/package.tgz index 618651da86b67301fa5068f7ecde7bd8fa1babe1..6630b6d7d4f68aae20a53378892be90bf3f27fb3 100644 GIT binary patch delta 635 zcmV->0)+kTh63=00WJ8U#Z+Rj9RX zcedH)Yo&MZ*c&$lv>mZY!!|fP#h2u9$8Os#b$n-gr9%WQGTq<*+|WibbHM=z% zU^tzR>7n%Jmc6-=Mp|nUx)Gm$7QL?6hv}d;?G0WcH)9~lj%=dSmv4=%;#Q^}MYBN- z`;0oB?YwG|N;`Jj-pZ*I!;wYU_mRDAcg(dUN!u!u>yCSI+-!+7Inbxp^`a4_)6K(;>k*_v41mceEPz$DXflt16FoS`aGoXZxYu z-iB|FcRR|?Oj1zA6Nhg<3;+#ZAqO(a7bOUS$6lNe=}}0B{F&{Cvk$QyMB}C+|FOMW z0S%k@QA!%qw)rr^#4I^#oi^O*xqBHmMlLySXuc`gAbWN_mJey-yavk){uq1V*4E=w zJQ%pA8BiJrK30CgW_7(rF&>1St?koxrXAV53d)P&^55*x8b@w8xlExbyrZvHz9NOg V{pbF3|M_A*{|~`9tLXr+0su*hL5ctX delta 632 zcmV-;0*C$Zh63$|0XMvkE-RU$q zZ^)Gl{cI^{8m*nA-nQM@G*u)!N@EaRhDqSYF=k+Q>}?Z`Xl-hti#gnmy_uzdlRHm> z2Be)B<7qHvV`?ifB}J(jm2NtuV!N%KmoJ2Oc^6;B+#{V)JDe1#myBwv&u2p)TJLZn9_9r9q? S&;93%`TRdA?L*1{umS*J4?&0k diff --git a/yarn.lock b/yarn.lock index 0e90af0cd2a..61e092f3fb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3878,8 +3878,8 @@ "@metamask/utils" "^8.2.0" "@metamask/eth-ledger-bridge-keyring@file:./package.tgz": - version "3.0.0" - resolved "file:./package.tgz#2e109591adb1b70e3c7a8d4a91f46752dec97bb7208ffad9ce8d4228ba77f127dae4ae7f00c9c2aeadbb09383c3da9325581e3596cefae08fb5845a86010e22c" + version "3.0.1" + resolved "file:./package.tgz#467a12e21893a57383bace8184da154e041d9b148adc8d5d33daa376d2595800d7cbf9bf5ce9010a14a9f659fb26e3263d49ca80b32b206da448c6b139b847ca" dependencies: "@ethereumjs/rlp" "^4.0.0" "@ethereumjs/tx" "^4.2.0" From aa4f2edb38f54d16009707b37725a5bc9d7ae000 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 29 Apr 2024 09:11:01 +0100 Subject: [PATCH 14/75] feat: make a backup before carry out the rebase. --- package.tgz | Bin 33648 -> 33853 bytes yarn.lock | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.tgz b/package.tgz index 6630b6d7d4f68aae20a53378892be90bf3f27fb3..a5913a8ca8ad71a8dc1e41afb315de591329e862 100644 GIT binary patch literal 33853 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

1rt>IN|#G+?tsIv9(}712RI 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 V9a;_FF#yfStiRXtLrO|$bT*0H6pIh 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 YB89{K=l*m5`C>l*55YLA=>V_-00LAsfdBvi diff --git a/yarn.lock b/yarn.lock index 61e092f3fb1..41e01872b04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3381,7 +3381,7 @@ "@ledgerhq/cryptoassets@^6.26.1": version "6.37.0" resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-6.37.0.tgz#302833777bcd210809ca7820afb82cff8da5c296" - integrity sha512-xwrDKTS9koQBNNzc7CqgV6zfGHvNFWJjlIL0Kc4O4DVWYR2vUdztUHcvwHD1KPjxNYhVnsgIopmtq47fHt3nMg== + integrity "sha1-MCgzd3vNIQgJynggr7gs/42lwpY= sha512-xwrDKTS9koQBNNzc7CqgV6zfGHvNFWJjlIL0Kc4O4DVWYR2vUdztUHcvwHD1KPjxNYhVnsgIopmtq47fHt3nMg==" dependencies: invariant "2" @@ -3429,7 +3429,7 @@ "@ledgerhq/hw-app-eth@6.26.1": version "6.26.1" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.26.1.tgz#c807087a563c4e1fb539116344ce114f5c84286b" - integrity sha512-maA5h+i92uoB+LXhkix1ZZWqI1qAxLBI5vEncuAbJubIQaJVv/BIlR3oAlZ+slNcq+9QUZ8vnzjRksn67kvoYA== + integrity "sha1-yAcIelY8Th+1ORFjRM4RT1yEKGs= sha512-maA5h+i92uoB+LXhkix1ZZWqI1qAxLBI5vEncuAbJubIQaJVv/BIlR3oAlZ+slNcq+9QUZ8vnzjRksn67kvoYA==" dependencies: "@ethersproject/abi" "^5.5.0" "@ethersproject/rlp" "^5.5.0" @@ -3879,7 +3879,7 @@ "@metamask/eth-ledger-bridge-keyring@file:./package.tgz": version "3.0.1" - resolved "file:./package.tgz#467a12e21893a57383bace8184da154e041d9b148adc8d5d33daa376d2595800d7cbf9bf5ce9010a14a9f659fb26e3263d49ca80b32b206da448c6b139b847ca" + resolved "file:./package.tgz#52c609bfa8718a106159fa0d077e9c236e784d68588426ca43bcb0f7292da962dfadf66f061cdd6c198803a580ef6680f4f2d7d622989c936917c686a402c09c" dependencies: "@ethereumjs/rlp" "^4.0.0" "@ethereumjs/tx" "^4.2.0" From dc848aa01b089df0d480295891167896c4ea42e1 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 29 Apr 2024 09:56:48 +0100 Subject: [PATCH 15/75] feat: forget to push `yarn.lock` --- yarn.lock | 86 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/yarn.lock b/yarn.lock index 00f515b2cdc..ed5fd23534d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1866,7 +1866,7 @@ "@ethereumjs/util" "^8.1.0" crc-32 "^1.2.0" -"@ethereumjs/rlp@^4.0.1": +"@ethereumjs/rlp@^4.0.0", "@ethereumjs/rlp@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== @@ -3429,12 +3429,27 @@ rxjs "^7.8.1" semver "^7.3.5" +"@ledgerhq/devices@^8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.3.0.tgz#a1e1a21608e162fb3a512f57863bf9842b29493f" + integrity sha512-h5Scr+yIae8yjPOViCHLdMjpqn4oC2Whrsq8LinRxe48LEGMdPqSV1yY7+3Ch827wtzNpMv+/ilKnd8rY+rTlg== + dependencies: + "@ledgerhq/errors" "^6.16.4" + "@ledgerhq/logs" "^6.12.0" + rxjs "^7.8.1" + semver "^7.3.5" + "@ledgerhq/errors@^5.26.0", "@ledgerhq/errors@^5.50.0": version "5.50.0" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== -"@ledgerhq/errors@^6.10.0", "@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.3": +"@ledgerhq/errors@^6.10.0", "@ledgerhq/errors@^6.16.4": + version "6.16.4" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.4.tgz#a38baffe8b096d9fff3ad839cadb55704c8d8e7b" + integrity sha512-M57yFaLYSN+fZCX0E0zUqOmrV6eipK+s5RhijHoUNlHUqrsvUz7iRQgpd5gRgHB5VkIjav7KdaZjKiWGcHovaQ== + +"@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.3": version "6.16.3" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.3.tgz#646f68cc7e6e8d5126bce1ca06140c5ad963bee8" integrity sha512-3w7/SJVXOPa9mpzyll7VKoKnGwDD3BzWgN1Nom8byR40DiQvOKjHX+kKQausCedTHVNBn9euzPCNsftZ9+mxfw== @@ -3517,7 +3532,17 @@ "@ledgerhq/errors" "^5.50.0" events "^3.3.0" -"@ledgerhq/hw-transport@^6.24.1", "@ledgerhq/hw-transport@^6.30.4": +"@ledgerhq/hw-transport@^6.24.1": + version "6.30.6" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.6.tgz#c6d84672ac4828f311831998f4101ea205215a6d" + integrity sha512-fT0Z4IywiuJuZrZE/+W0blkV5UCotDPFTYKLkKCLzYzuE6javva7D/ajRaIeR+hZ4kTmKF4EqnsmDCXwElez+w== + dependencies: + "@ledgerhq/devices" "^8.3.0" + "@ledgerhq/errors" "^6.16.4" + "@ledgerhq/logs" "^6.12.0" + events "^3.3.0" + +"@ledgerhq/hw-transport@^6.30.4": version "6.30.5" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.5.tgz#841c9e4bb3849536db110ca2894d693d55bf54fd" integrity sha512-JMl//7BgPBvWxrWyMu82jj6JEYtsQyOyhYtonWNgtxn6KUZWht3gU4gxmLpeIRr+DiS7e50mW7m3GA+EudZmmA== @@ -3923,6 +3948,17 @@ "@metamask/obs-store" "^8.1.0" "@metamask/utils" "^8.2.0" +"@metamask/eth-ledger-bridge-keyring@file:./package.tgz": + version "3.0.1" + resolved "file:./package.tgz#52c609bfa8718a106159fa0d077e9c236e784d68588426ca43bcb0f7292da962dfadf66f061cdd6c198803a580ef6680f4f2d7d622989c936917c686a402c09c" + dependencies: + "@ethereumjs/rlp" "^4.0.0" + "@ethereumjs/tx" "^4.2.0" + "@ethereumjs/util" "^8.0.0" + "@ledgerhq/hw-app-eth" "6.26.1" + "@metamask/eth-sig-util" "^7.0.1" + hdkey "^2.1.0" + "@metamask/eth-query@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-query/-/eth-query-3.0.1.tgz#3439eb6c7d5ccff1d6a66df1d1802bae0c890444" @@ -17753,6 +17789,16 @@ hdkey@^2.0.1: safe-buffer "^5.1.1" secp256k1 "^4.0.0" +hdkey@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.1.0.tgz#755b30b73f54e93c31919c1b2f19205a8e57cb92" + integrity sha512-i9Wzi0Dy49bNS4tXXeGeu0vIcn86xXdPQUpEYg+SO1YiO8HtomjmmRMaRyqL0r59QfcD4PfVbSF3qmsWFwAemA== + dependencies: + bs58check "^2.1.2" + ripemd160 "^2.0.2" + safe-buffer "^5.1.1" + secp256k1 "^4.0.0" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -25236,7 +25282,7 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: +ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== @@ -26254,7 +26300,7 @@ string-range@~1.2, string-range@~1.2.1: resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd" integrity sha1-qJPtNH5yKZvIO++78qaSqNI51d0= -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -26272,15 +26318,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -26404,14 +26441,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm: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-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.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== @@ -28296,7 +28326,8 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -28331,15 +28362,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From aa461e2d6f97bb81cff70f45c27addae8862e845 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 29 Apr 2024 16:20:56 +0100 Subject: [PATCH 16/75] feat: Fix the build issue after rebase. --- app/components/Nav/App/index.js | 6 +++++- app/core/Engine.ts | 33 +++++++++++++++++++++------------ yarn.lock | 4 ++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 18a0c223806..973469442da 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -463,6 +463,7 @@ const App = ({ userLoggedIn }) => { } } } + initSDKConnect().catch((err) => { Logger.error(err, 'Error initializing SDKConnect'); }); @@ -708,7 +709,10 @@ const App = ({ userLoggedIn }) => { const LedgerConnectFlow = () => ( - + ); diff --git a/app/core/Engine.ts b/app/core/Engine.ts index ff0e0f634bc..6ef5795c047 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -125,7 +125,7 @@ import { LedgerMobileBridge, LedgerTransportMiddleware, } from '@metamask/eth-ledger-bridge-keyring'; -import { Encryptor, DERIVATION_PARAMS } from './Encryptor'; +import { Encryptor, LEGACY_DERIVATION_PARAMS } from './Encryptor'; import { isMainnetByChainId, getDecimalChainId, @@ -195,7 +195,7 @@ import { const NON_EMPTY = 'NON_EMPTY'; const encryptor = new Encryptor({ - derivationParams: DERIVATION_PARAMS, + derivationParams: LEGACY_DERIVATION_PARAMS, }); let currentChainId: any; @@ -210,6 +210,7 @@ interface TestOrigin { type: `${PhishingController['name']}:testOrigin`; handler: PhishingController['test']; } + type PhishingControllerActions = MaybeUpdateState | TestOrigin; type SnapsGlobalActions = @@ -348,6 +349,7 @@ class Engine { * Object that runs and manages the execution of Snaps */ snapExecutionService: WebViewExecutionService; + ///: END:ONLY_INCLUDE_IF /** @@ -430,7 +432,6 @@ class Engine { listener, ), chainId: networkController.state.providerConfig.chainId, - //@ts-expect-error This will be fixed when assets-controller is on v16 getNetworkClientById: networkController.getNetworkClientById.bind(networkController), }); @@ -444,14 +445,20 @@ class Engine { AppConstants.NETWORK_STATE_CHANGE_EVENT, listener, ), + getNetworkClientById: + networkController.getNetworkClientById.bind(networkController), // @ts-expect-error TODO: Resolve/patch mismatch between base-controller versions. Before: never, never. Now: string, string, which expects 3rd and 4th args to be informed for restrictedControllerMessengers messenger: this.controllerMessenger.getRestricted< 'NftController', - 'ApprovalController:addRequest', + | 'ApprovalController:addRequest' + | 'NetworkController:getNetworkClientById', never >({ name: 'NftController', - allowedActions: [`${approvalController.name}:addRequest`], + allowedActions: [ + `${approvalController.name}:addRequest`, + `${networkController.name}:getNetworkClientById`, + ], }), chainId: networkController.state.providerConfig.chainId, @@ -521,7 +528,6 @@ class Engine { AppConstants.TOKEN_LIST_STATE_CHANGE_EVENT, listener, ), - //@ts-expect-error This will be fixed when assets-controller is on v16 getNetworkClientById: networkController.getNetworkClientById.bind(networkController), chainId: networkController.state.providerConfig.chainId, @@ -558,21 +564,24 @@ class Engine { 'NetworkController:stateChange' >({ name: 'TokenListController', - allowedEvents: ['NetworkController:stateChange'], + allowedEvents: [`${networkController.name}:stateChange`], }), }); const currencyRateController = new CurrencyRateController({ // @ts-expect-error TODO: Resolve/patch mismatch between base-controller versions. Before: never, never. Now: string, string, which expects 3rd and 4th args to be informed for restrictedControllerMessengers messenger: this.controllerMessenger.getRestricted< 'CurrencyRateController', - never, + 'NetworkController:getNetworkClientById', never >({ name: 'CurrencyRateController', + allowedActions: [`${networkController.name}:getNetworkClientById`], }), state: initialState.CurrencyRateController, }); - currencyRateController.start(); + currencyRateController.startPollingByNetworkClientId( + networkController.state.selectedNetworkClientId, + ); const gasFeeController = new GasFeeController({ // @ts-expect-error TODO: Resolve/patch mismatch between base-controller versions. Before: never, never. Now: string, string, which expects 3rd and 4th args to be informed for restrictedControllerMessengers @@ -584,10 +593,10 @@ class Engine { >({ name: 'GasFeeController', allowedActions: [ - 'NetworkController:getNetworkClientById', - 'NetworkController:getEIP1559Compatibility', + `${networkController.name}:getNetworkClientById`, + `${networkController.name}:getEIP1559Compatibility`, ], - allowedEvents: ['NetworkController:stateChange'], + allowedEvents: [`${networkController.name}:stateChange`], }), getProvider: () => // @ts-expect-error at this point in time the provider will be defined by the `networkController.initializeProvider` diff --git a/yarn.lock b/yarn.lock index ed5fd23534d..df831fb9772 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3949,8 +3949,8 @@ "@metamask/utils" "^8.2.0" "@metamask/eth-ledger-bridge-keyring@file:./package.tgz": - version "3.0.1" - resolved "file:./package.tgz#52c609bfa8718a106159fa0d077e9c236e784d68588426ca43bcb0f7292da962dfadf66f061cdd6c198803a580ef6680f4f2d7d622989c936917c686a402c09c" + version "3.0.0" + resolved "file:./package.tgz#590a1b7e720327c6293b485209a2de0505573dd36c538a958b8bc77b2a8c433652652617d54844f08e81863ecfbdb30c4bfcb156e22e49a56539f679afa15eec" dependencies: "@ethereumjs/rlp" "^4.0.0" "@ethereumjs/tx" "^4.2.0" From 078cc285fb2bc9c20f51cf49fbd73cc1f9fe4ef6 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 30 Apr 2024 14:37:40 +0100 Subject: [PATCH 17/75] feat: add `remove hardware wallet` from AccountActions.tsx --- app/components/Nav/App/index.js | 9 +- .../AccountActions.constants.ts | 1 + .../Views/AccountActions/AccountActions.tsx | 91 +++++++- .../Views/LedgerAccountInfo/index.tsx | 197 ------------------ .../index.tsx} | 0 locales/languages/en.json | 22 +- 6 files changed, 102 insertions(+), 218 deletions(-) delete mode 100644 app/components/Views/LedgerAccountInfo/index.tsx rename app/components/Views/{LedgerAccountInfo/LedgerSelectAccount.tsx => LedgerSelectAccount/index.tsx} (100%) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index 973469442da..8e048152c60 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -67,7 +67,6 @@ import ImportPrivateKey from '../../Views/ImportPrivateKey'; import ImportPrivateKeySuccess from '../../Views/ImportPrivateKeySuccess'; import ConnectQRHardware from '../../Views/ConnectQRHardware'; import SelectHardwareWallet from '../../Views/ConnectHardware/SelectHardware'; -import LedgerAccountInfo from '../../Views/LedgerAccountInfo'; import { AUTHENTICATION_APP_TRIGGERED_AUTH_NO_CREDENTIALS } from '../../../constants/error'; import { UpdateNeeded } from '../../../components/UI/UpdateNeeded'; import { EnableAutomaticSecurityChecksModal } from '../../../components/UI/EnableAutomaticSecurityChecksModal'; @@ -103,7 +102,7 @@ import { MetaMetrics } from '../../../core/Analytics'; import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics'; import generateDeviceAnalyticsMetaData from '../../../util/metrics/DeviceAnalyticsMetaData/generateDeviceAnalyticsMetaData'; import generateUserSettingsAnalyticsMetaData from '../../../util/metrics/UserSettingsAnalyticsMetaData/generateUserProfileAnalyticsMetaData'; -import LedgerSelectAccount from '../../Views/LedgerAccountInfo/LedgerSelectAccount'; +import Index from '../../Views/LedgerSelectAccount'; import OnboardingSuccess from '../../Views/OnboardingSuccess'; import DefaultSettings from '../../Views/OnboardingSuccess/DefaultSettings'; import BasicFunctionalityModal from '../../UI/BasicFunctionality/BasicFunctionalityModal/BasicFunctionalityModal'; @@ -709,10 +708,7 @@ const App = ({ userLoggedIn }) => { const LedgerConnectFlow = () => ( - + ); @@ -723,7 +719,6 @@ const App = ({ userLoggedIn }) => { component={SelectHardwareWallet} options={SelectHardwareWallet.navigationOptions} /> - ); diff --git a/app/components/Views/AccountActions/AccountActions.constants.ts b/app/components/Views/AccountActions/AccountActions.constants.ts index 1041c9a6af1..12b68050ab6 100644 --- a/app/components/Views/AccountActions/AccountActions.constants.ts +++ b/app/components/Views/AccountActions/AccountActions.constants.ts @@ -3,3 +3,4 @@ export const EDIT_ACCOUNT = 'edit-account-action'; export const VIEW_ETHERSCAN = 'view-etherscan-action'; export const SHARE_ADDRESS = 'share-address-action'; export const SHOW_PRIVATE_KEY = 'show-private-key-action'; +export const REMOVE_HARDWARE_ACCOUNT = 'remove-hardward-account-action'; diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 6a083f36cac..f3804ea93e8 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -1,6 +1,6 @@ // Third party dependencies. -import React, { useMemo, useRef } from 'react'; -import { Platform, View } from 'react-native'; +import React, { useCallback, useMemo, useRef } from 'react'; +import { Alert, Platform, View } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { useDispatch, useSelector } from 'react-redux'; import Share from 'react-native-share'; @@ -37,11 +37,17 @@ import Routes from '../../../constants/navigation/Routes'; import generateTestId from '../../../../wdio/utils/generateTestId'; import { EDIT_ACCOUNT, + REMOVE_HARDWARE_ACCOUNT, SHARE_ADDRESS, SHOW_PRIVATE_KEY, VIEW_ETHERSCAN, } from './AccountActions.constants'; import { useMetrics } from '../../../components/hooks/useMetrics'; +import { getKeyringByAddress, isHardwareAccount } from '../../../util/address'; +import { removeAccountsFromPermissions } from '../../../core/Permissions'; +import ExtendedKeyringTypes from '../../../constants/keyringTypes'; +import { forgetLedger } from '../../../core/Ledger/Ledger'; +import Engine from '../../../core/Engine'; const AccountActions = () => { const { styles } = useStyles(styleSheet, {}); @@ -50,6 +56,11 @@ const AccountActions = () => { const dispatch = useDispatch(); const { trackEvent } = useMetrics(); + const Controller = useMemo(() => { + const { KeyringController, PreferencesController } = Engine.context as any; + return { KeyringController, PreferencesController }; + }, []); + const providerConfig = useSelector(selectProviderConfig); const selectedAddress = useSelector(selectSelectedAddress); @@ -126,6 +137,74 @@ const AccountActions = () => { }); }; + const removeHardwareAccount = useCallback(() => { + Alert.alert( + strings('accounts.remove_account_title'), + strings('accounts.remove_account_alert_description'), + [ + { + text: strings('accounts.remove_account_alert_cancel_btn'), + onPress: () => false, + style: 'cancel', + }, + { + text: strings('accounts.remove_account_alert_remove_btn'), + onPress: async () => { + const kr = getKeyringByAddress(selectedAddress); + let requestForgetDevice = false; + + await Controller.KeyringController.removeAccount(selectedAddress); + await removeAccountsFromPermissions([selectedAddress]); + const newAccounts = + await Controller.KeyringController.getAccounts(); + Controller.PreferencesController.updateIdentities(newAccounts); + + // setSelectedAddress to the initial account + Engine.setSelectedAddress(newAccounts[0]); + + const { keyrings } = Controller.KeyringController.state; + + const updatedKeyring = keyrings.find( + (keyring: { type: any }) => keyring.type === kr.type, + ); + + if (updatedKeyring) { + if (updatedKeyring.accounts.length === 0) { + requestForgetDevice = true; + } + } else { + requestForgetDevice = true; + } + if (requestForgetDevice) { + switch (kr.type) { + case ExtendedKeyringTypes.ledger: + await forgetLedger(); + trackEvent( + MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, + { + device_type: 'Ledger', + }, + ); + break; + case ExtendedKeyringTypes.qr: + await Controller.KeyringController.forgetQRDevice(); + // there is not a MetaMetricsEvent for this action?? + break; + default: + break; + } + } + }, + }, + ], + ); + }, [ + Controller.KeyringController, + Controller.PreferencesController, + selectedAddress, + trackEvent, + ]); + const goToEditAccountName = () => { navigate('EditAccountName'); }; @@ -168,6 +247,14 @@ const AccountActions = () => { onPress={goToExportPrivateKey} {...generateTestId(Platform, SHOW_PRIVATE_KEY)} /> + {isHardwareAccount(selectedAddress) && ( + + )} ); diff --git a/app/components/Views/LedgerAccountInfo/index.tsx b/app/components/Views/LedgerAccountInfo/index.tsx deleted file mode 100644 index 54b4062c23a..00000000000 --- a/app/components/Views/LedgerAccountInfo/index.tsx +++ /dev/null @@ -1,197 +0,0 @@ -/* eslint-disable @typescript-eslint/no-require-imports */ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable import/no-commonjs */ -import { StackActions, useNavigation } from '@react-navigation/native'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { - Image, - SafeAreaView, - StyleSheet, - TouchableOpacity, - View, -} from 'react-native'; -import { useDispatch, useSelector } from 'react-redux'; -import { strings } from '../../../../locales/i18n'; -import { setReloadAccounts } from '../../../actions/accounts'; -import { NO_RPC_BLOCK_EXPLORER, RPC } from '../../../constants/network'; -import Engine from '../../../core/Engine'; -import { forgetLedger, getLedgerKeyring } from '../../../core/Ledger/Ledger'; -import Device from '../../../util/device'; -import { getEtherscanAddressUrl } from '../../../util/etherscan'; -import { findBlockExplorerForRpc } from '../../../util/networks'; -import { - mockTheme, - useAppThemeFromContext, - useAssetFromTheme, -} from '../../../util/theme'; -import Text from '../../Base/Text'; -import { getNavigationOptionsTitle } from '../../UI/Navbar'; -import AccountDetails from '../../../components/UI/HardwareWallet/AccountDetails'; - -import ledgerDeviceDarkImage from '../../../images/ledger-device-dark.png'; -import ledgerDeviceLightImage from '../../../images/ledger-device-light.png'; -import { MetaMetricsEvents } from '../../../core/Analytics'; -import { useMetrics } from '../../../components/hooks/useMetrics'; - -const createStyles = (colors: any) => - StyleSheet.create({ - container: { - flex: 1, - backgroundColor: colors.background.default, - }, - imageWrapper: { - alignSelf: 'flex-start', - marginLeft: Device.getDeviceWidth() * 0.07, - }, - textWrapper: { - alignItems: 'center', - marginHorizontal: Device.getDeviceWidth() * 0.07, - }, - accountCountText: { - fontSize: 24, - }, - accountsContainer: { - flexDirection: 'row', - marginTop: 20, - marginLeft: Device.getDeviceWidth() * 0.02, - marginRight: Device.getDeviceWidth() * 0.07, - }, - textContainer: { - flex: 0.7, - }, - etherscanContainer: { - flex: 0.3, - justifyContent: 'center', - }, - etherscanImage: { - width: 30, - height: 30, - }, - forgetLedgerContainer: { - flex: 1, - flexDirection: 'column', - justifyContent: 'flex-end', - alignItems: 'center', - padding: 20, - }, - }); - -const LedgerAccountInfo = () => { - const dispatch = useDispatch(); - const navigation = useNavigation(); - const { trackEvent } = useMetrics(); - const [account, setAccount] = useState(''); - const [accountBalance, setAccountBalance] = useState('0'); - const { colors } = useAppThemeFromContext() ?? mockTheme; - const styles = useMemo(() => createStyles(colors), [colors]); - const ledgerThemedImage = useAssetFromTheme( - ledgerDeviceLightImage, - ledgerDeviceDarkImage, - ); - const { AccountTrackerController } = Engine.context as any; - const provider = useSelector( - (state: any) => - state.engine.backgroundState.NetworkController.providerConfig, - ); - const frequentRpcList = useSelector( - (state: any) => - state.engine.backgroundState.PreferencesController.frequentRpcList, - ); - - useEffect(() => { - navigation.setOptions( - getNavigationOptionsTitle('', navigation, true, colors), - ); - }, [navigation, colors]); - - useEffect(() => { - const getAccount = async () => { - const ledgerKeyring = await getLedgerKeyring(); - const accounts = await ledgerKeyring.getAccounts(); - - setAccount(accounts[0]); - }; - - getAccount(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onForgetDevice = async () => { - await forgetLedger(); - dispatch(setReloadAccounts(true)); - trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { - device_type: 'Ledger', - }); - navigation.dispatch(StackActions.pop(2)); - }; - - const getEthAmountForAccount = async (ledgerAccount: string) => { - if (ledgerAccount) { - const ethValue = await AccountTrackerController.syncBalanceWithAddresses([ - ledgerAccount, - ]); - - setAccountBalance(ethValue[ledgerAccount]?.balance); - } - }; - - useEffect(() => { - if (account) { - getEthAmountForAccount(account); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [account]); - - const toBlockExplorer = useCallback( - (address: string) => { - const { type, rpcUrl } = provider; - let accountLink: string; - - if (type === RPC) { - const blockExplorer = - findBlockExplorerForRpc(rpcUrl, frequentRpcList) || - NO_RPC_BLOCK_EXPLORER; - accountLink = `${blockExplorer}/address/${address}`; - } else { - accountLink = getEtherscanAddressUrl(type, address); - } - - navigation.navigate('Webview', { - screen: 'SimpleWebview', - params: { - url: accountLink, - }, - }); - }, - [frequentRpcList, navigation, provider], - ); - - return ( - - - - - - - {strings('ledger.ledger_account_count')} - - - - - - - - {strings('ledger.forget_device')} - - - - ); -}; - -export default React.memo(LedgerAccountInfo); diff --git a/app/components/Views/LedgerAccountInfo/LedgerSelectAccount.tsx b/app/components/Views/LedgerSelectAccount/index.tsx similarity index 100% rename from app/components/Views/LedgerAccountInfo/LedgerSelectAccount.tsx rename to app/components/Views/LedgerSelectAccount/index.tsx diff --git a/locales/languages/en.json b/locales/languages/en.json index ae2bb321cc0..0429b7b52ac 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -374,7 +374,7 @@ "no_available_tokens": "Don't see your token?", "add_tokens": "Import tokens", "are_you_sure_exit": "Are you sure you want to exit?", - "search_information_not_saved":"Your search information will not be saved.", + "search_information_not_saved": "Your search information will not be saved.", "import_token": "Would you like to import this token?", "tokens_detected_in_account": "{{tokenCount}} new {{tokensLabel}} found in this account", "token_toast": { @@ -403,9 +403,8 @@ "nfts_autodetection_desc": "Allow MetaMask to automatically detect NFTs from OpenSea and display in your MetaMask wallet.", "network_details_check": "Network details check", "network_with_chain_id": "The network with chain ID", - "chain_list_returned_different_ticker_symbol": - "This token symbol doesn't match the network name or chain ID entered. Many popular tokens use similar symbols, which scammers can use to trick you into sending them a more valuable token in return. Verify everything before you continue.", - "suggested_token_symbol":"Suggested ticker symbol:", + "chain_list_returned_different_ticker_symbol": "This token symbol doesn't match the network name or chain ID entered. Many popular tokens use similar symbols, which scammers can use to trick you into sending them a more valuable token in return. Verify everything before you continue.", + "suggested_token_symbol": "Suggested ticker symbol:", "potential_scam": "This is a potential scam", "network_not_matching": "This network doesn't match its associated chain ID or name. Many popular tokens use the name", "target_scam_network": "making it a target for scams. Scammers may trick you into sending them more valuable currency in return. Verify everything before you continue.", @@ -540,7 +539,7 @@ "symbol_cant_be_empty": "Token symbol can't be empty.", "symbol_length": "Symbol must be 11 characters or fewer", "decimals_cant_be_empty": "Token decimals can't be empty.", - "decimals_is_required":"Decimal is required. Find it on:", + "decimals_is_required": "Decimal is required. Find it on:", "no_tokens_found": "We couldn't find any tokens with that name.", "select_token": "Select Token", "address_must_be_smart_contract": "Personal address detected. Enter the token contract address." @@ -586,6 +585,10 @@ "remove_account_message": "Do you really want to remove this account?", "no": "No", "yes_remove_it": "Yes, remove it", + "remove_hardware_account": "Remove hardware account", + "remove_account_alert_description": "Are you sure you want to remove this hardware wallet account? You’ll have to resync your hardware wallet if you want to use this account again with MetaMask Mobile.", + "remove_account_alert_remove_btn": "Remove", + "remove_account_alert_cancel_btn": "Nevermind", "accounts_title": "Accounts", "connect_account_title": "Connect account", "connect_accounts_title": "Connect accounts", @@ -750,7 +753,7 @@ "notifications_desc": "Manage your notifications", "allow_notifications": "Allow notifications", "allow_notifications_desc": "Stay in the loop on what’s happening in your wallet with notifications. To use notifications, we use a profile to sync some settings across your devices. Learn how we protect your privacy while using this feature.", - "notifications_opts" :{ + "notifications_opts": { "customize_session_title": "Customize your notifications", "customize_session_desc": "Turn on the types of notifications you want to receive:", "account_session_title": "Account activity", @@ -1502,18 +1505,13 @@ "smart_transactions": { "status_submitting_header": "Submitting your transaction", "status_submitting_description": "Estimated completion {{timeLeft}}", - "status_success_header": "Your transaction is complete", - "status_submitting_past_estimated_deadline_header": "Sorry for the wait", "status_submitting_past_estimated_deadline_description": "If your transaction is not finalized within {{timeLeft}}, it will be canceled and you will not be charged for gas.", - "status_cancelled_header": "Your transaction was canceled", "status_cancelled_description": "Your transaction couldn't be completed, so it was canceled to save you from paying unnecessary gas fees.", - "status_failed_header": "Your transaction failed", "status_failed_description": "Sudden market changes can cause failures. If the problem continues, reach out to MetaMask customer support.", - "view_transaction": "View transaction", "view_activity": "View activity", "return_to_dapp": "Return to {{dappName}}", @@ -1939,7 +1937,7 @@ "manage_preferences_2": "Settings > Notifications.", "cancel": "Cancel", "cta": "Turn on" - } + } }, "protect_your_wallet_modal": { "title": "Protect your wallet", From ebfae119d2a0210f495499cfee84704aa1fffe7a Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 7 May 2024 03:20:29 +0100 Subject: [PATCH 18/75] feat: add `loading` mask when remove hardware account devices. --- .../UI/BlockingActionModal/index.js | 1 + .../AccountActions/AccountActions.styles.ts | 8 ++++++- .../Views/AccountActions/AccountActions.tsx | 21 ++++++++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/components/UI/BlockingActionModal/index.js b/app/components/UI/BlockingActionModal/index.js index a4b03ec8ed6..c3db92fb447 100644 --- a/app/components/UI/BlockingActionModal/index.js +++ b/app/components/UI/BlockingActionModal/index.js @@ -43,6 +43,7 @@ export default function BlockingActionModal({ backdropOpacity={1} isVisible={modalVisible} style={styles.modal} + hideModalContentWhileAnimating > diff --git a/app/components/Views/AccountActions/AccountActions.styles.ts b/app/components/Views/AccountActions/AccountActions.styles.ts index 153fbd3857f..743f36063df 100644 --- a/app/components/Views/AccountActions/AccountActions.styles.ts +++ b/app/components/Views/AccountActions/AccountActions.styles.ts @@ -1,18 +1,24 @@ // Third party dependencies. import { StyleSheet } from 'react-native'; +import { fontStyles } from '../../../styles/common'; /** * Style sheet function for AccountActions component. * * @returns StyleSheet object. */ -const styleSheet = () => +const styleSheet = (colors: any) => StyleSheet.create({ actionsContainer: { alignItems: 'flex-start', justifyContent: 'center', paddingVertical: 16, }, + text: { + color: colors.text.default, + fontSize: 14, + ...fontStyles.normal, + }, }); export default styleSheet; diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index f3804ea93e8..5605142545c 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -1,6 +1,6 @@ // Third party dependencies. -import React, { useCallback, useMemo, useRef } from 'react'; -import { Alert, Platform, View } from 'react-native'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { Alert, Platform, Text, View } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { useDispatch, useSelector } from 'react-redux'; import Share from 'react-native-share'; @@ -48,14 +48,19 @@ import { removeAccountsFromPermissions } from '../../../core/Permissions'; import ExtendedKeyringTypes from '../../../constants/keyringTypes'; import { forgetLedger } from '../../../core/Ledger/Ledger'; import Engine from '../../../core/Engine'; +import BlockingActionModal from '../../UI/BlockingActionModal'; +import { useTheme } from '../../../util/theme'; const AccountActions = () => { - const { styles } = useStyles(styleSheet, {}); + const { colors } = useTheme(); + const styles = styleSheet(colors); const sheetRef = useRef(null); const { navigate } = useNavigation(); const dispatch = useDispatch(); const { trackEvent } = useMetrics(); + const [blockingModalVisible, setBlockingModalVisible] = useState(false); + const Controller = useMemo(() => { const { KeyringController, PreferencesController } = Engine.context as any; return { KeyringController, PreferencesController }; @@ -150,9 +155,11 @@ const AccountActions = () => { { text: strings('accounts.remove_account_alert_remove_btn'), onPress: async () => { + setBlockingModalVisible(true); const kr = getKeyringByAddress(selectedAddress); let requestForgetDevice = false; + // Remove account from KeyringController await Controller.KeyringController.removeAccount(selectedAddress); await removeAccountsFromPermissions([selectedAddress]); const newAccounts = @@ -168,6 +175,7 @@ const AccountActions = () => { (keyring: { type: any }) => keyring.type === kr.type, ); + // If there are no more accounts in the keyring, forget the device if (updatedKeyring) { if (updatedKeyring.accounts.length === 0) { requestForgetDevice = true; @@ -194,6 +202,8 @@ const AccountActions = () => { break; } } + + setBlockingModalVisible(false); }, }, ], @@ -256,6 +266,11 @@ const AccountActions = () => { /> )} + + + {strings('connect_qr_hardware.please_wait')} + + ); }; From 82e0a5deebc3631cae1ef791b4678bd4f1f08b61 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Fri, 10 May 2024 04:24:50 +0100 Subject: [PATCH 19/75] feat: Rebase from main to upgrade to keyringController 13. Fix all blocken code. Enhance in BlockingActionModal with smooth animation to provide better user friendly interaction. add code to remove single accounts in actions. Still need to fix unit tests, however because MM mobile team will upgrade keyringController to 15, will start to fix unit tests after rerbase. --- .../__snapshots__/index.test.tsx.snap | 2 +- .../UI/BlockingActionModal/index.js | 4 + .../Views/AccountActions/AccountActions.tsx | 106 +++++++++--------- .../Views/LedgerSelectAccount/index.tsx | 46 +++++++- app/core/Engine.ts | 92 +++++++++------ app/core/Ledger/Ledger.ts | 5 +- .../@metamask+keyring-controller+13.0.0.patch | 33 +++++- .../@metamask+keyring-controller+9.0.0.patch | 0 yarn.lock | 74 +++++------- 9 files changed, 223 insertions(+), 139 deletions(-) delete mode 100644 patches/@metamask+keyring-controller+9.0.0.patch diff --git a/app/components/UI/BlockingActionModal/__snapshots__/index.test.tsx.snap b/app/components/UI/BlockingActionModal/__snapshots__/index.test.tsx.snap index 23df9c84ea8..cc1ee6b51fc 100644 --- a/app/components/UI/BlockingActionModal/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/BlockingActionModal/__snapshots__/index.test.tsx.snap @@ -16,7 +16,7 @@ exports[`BlockingActionModal should render correctly 1`] = ` deviceHeight={null} deviceWidth={null} hasBackdrop={true} - hideModalContentWhileAnimating={false} + hideModalContentWhileAnimating={true} isVisible={true} onBackButtonPress={[Function]} onBackdropPress={[Function]} diff --git a/app/components/UI/BlockingActionModal/index.js b/app/components/UI/BlockingActionModal/index.js index c3db92fb447..b126438edf0 100644 --- a/app/components/UI/BlockingActionModal/index.js +++ b/app/components/UI/BlockingActionModal/index.js @@ -33,6 +33,7 @@ export default function BlockingActionModal({ children, modalVisible, isLoadingAction, + onAnimationCompleted, }) { const { colors } = useTheme(); const styles = createStyles(colors); @@ -43,6 +44,7 @@ export default function BlockingActionModal({ backdropOpacity={1} isVisible={modalVisible} style={styles.modal} + onModalShow={onAnimationCompleted} hideModalContentWhileAnimating > @@ -70,4 +72,6 @@ BlockingActionModal.propTypes = { * Content to display above the action buttons */ children: PropTypes.node, + + onAnimationCompleted: PropTypes.func, }; diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 5605142545c..a98d9a8cefb 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -142,7 +142,7 @@ const AccountActions = () => { }); }; - const removeHardwareAccount = useCallback(() => { + const showRemoveHWAlert = useCallback(() => { Alert.alert( strings('accounts.remove_account_title'), strings('accounts.remove_account_alert_description'), @@ -156,61 +156,61 @@ const AccountActions = () => { text: strings('accounts.remove_account_alert_remove_btn'), onPress: async () => { setBlockingModalVisible(true); - const kr = getKeyringByAddress(selectedAddress); - let requestForgetDevice = false; + }, + }, + ], + ); + }, []); - // Remove account from KeyringController - await Controller.KeyringController.removeAccount(selectedAddress); - await removeAccountsFromPermissions([selectedAddress]); - const newAccounts = - await Controller.KeyringController.getAccounts(); - Controller.PreferencesController.updateIdentities(newAccounts); + const triggerRemoveHWAccount = useCallback(async () => { + if (blockingModalVisible) { + const kr = getKeyringByAddress(selectedAddress); + let requestForgetDevice = false; - // setSelectedAddress to the initial account - Engine.setSelectedAddress(newAccounts[0]); + // Remove account from KeyringController + await Controller.KeyringController.removeAccount(selectedAddress); + await removeAccountsFromPermissions([selectedAddress]); + const newAccounts = await Controller.KeyringController.getAccounts(); - const { keyrings } = Controller.KeyringController.state; + // setSelectedAddress to the initial account + Engine.setSelectedAddress(newAccounts[0]); - const updatedKeyring = keyrings.find( - (keyring: { type: any }) => keyring.type === kr.type, - ); + const { keyrings } = Controller.KeyringController.state; - // If there are no more accounts in the keyring, forget the device - if (updatedKeyring) { - if (updatedKeyring.accounts.length === 0) { - requestForgetDevice = true; - } - } else { - requestForgetDevice = true; - } - if (requestForgetDevice) { - switch (kr.type) { - case ExtendedKeyringTypes.ledger: - await forgetLedger(); - trackEvent( - MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, - { - device_type: 'Ledger', - }, - ); - break; - case ExtendedKeyringTypes.qr: - await Controller.KeyringController.forgetQRDevice(); - // there is not a MetaMetricsEvent for this action?? - break; - default: - break; - } - } + const updatedKeyring = keyrings.find( + (keyring: { type: any }) => keyring.type === kr.type, + ); - setBlockingModalVisible(false); - }, - }, - ], - ); + // If there are no more accounts in the keyring, forget the device + if (updatedKeyring) { + if (updatedKeyring.accounts.length === 0) { + requestForgetDevice = true; + } + } else { + requestForgetDevice = true; + } + if (requestForgetDevice) { + switch (kr.type) { + case ExtendedKeyringTypes.ledger: + await forgetLedger(); + trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { + device_type: 'Ledger', + }); + break; + case ExtendedKeyringTypes.qr: + await Controller.KeyringController.forgetQRDevice(); + // there is not a MetaMetricsEvent for this action?? + break; + default: + break; + } + } + + setBlockingModalVisible(false); + } }, [ Controller.KeyringController, - Controller.PreferencesController, + blockingModalVisible, selectedAddress, trackEvent, ]); @@ -260,13 +260,17 @@ const AccountActions = () => { {isHardwareAccount(selectedAddress) && ( )} - + {strings('connect_qr_hardware.please_wait')} diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index f9802c9f372..70f7f61a501 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -93,6 +93,13 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { { address: string; index: number; balance: string }[] >([]); + const [unlockAccounts, setUnlockAccounts] = useState({ + trigger: false, + accountIndexes: [] as number[], + }); + + const [forgetDevice, setForgetDevice] = useState(false); + const [existingAccounts, setExistingAccounts] = useState([]); useEffect(() => { @@ -140,14 +147,37 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { ); const onForget = useCallback(async () => { + setBlockingModalVisible(true); await forgetLedger(); dispatch(setReloadAccounts(true)); trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { device_type: 'Ledger', }); + setBlockingModalVisible(false); navigation.dispatch(StackActions.pop(2)); }, [dispatch, navigation, trackEvent]); + const onAnimationCompleted = useCallback(async () => { + if (blockingModalVisible) { + if (forgetDevice) { + await onForget(); + setBlockingModalVisible(false); + setForgetDevice(false); + } else if (unlockAccounts.trigger) { + await onUnlock(unlockAccounts.accountIndexes); + setBlockingModalVisible(false); + setUnlockAccounts({ trigger: false, accountIndexes: [] }); + } + } + }, [ + blockingModalVisible, + forgetDevice, + onForget, + onUnlock, + unlockAccounts.accountIndexes, + unlockAccounts.trigger, + ]); + return accounts.length <= 0 ? ( ) : ( @@ -170,12 +200,22 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { nextPage={nextPage} prevPage={prevPage} toggleAccount={onToggle} - onUnlock={onUnlock} - onForget={onForget} + onUnlock={(accountIndex: number[]) => { + setUnlockAccounts({ trigger: true, accountIndexes: accountIndex }); + setBlockingModalVisible(true); + }} + onForget={() => { + setForgetDevice(true); + setBlockingModalVisible(true); + }} title={strings('connect_qr_hardware.select_accounts')} /> - + {strings('connect_qr_hardware.please_wait')} diff --git a/app/core/Engine.ts b/app/core/Engine.ts index 9dce214b345..368332631a2 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -113,7 +113,7 @@ import { buildSnapEndowmentSpecifications, buildSnapRestrictedMethodSpecifications, } from '@metamask/snaps-rpc-methods'; -import type { EnumToUnion, DialogType } from '@metamask/snaps-sdk'; +import type { DialogType, EnumToUnion } from '@metamask/snaps-sdk'; // eslint-disable-next-line import/no-nodejs-modules import { Duplex } from 'stream'; ///: END:ONLY_INCLUDE_IF @@ -128,7 +128,7 @@ import { LedgerMobileBridge, LedgerTransportMiddleware, } from '@metamask/eth-ledger-bridge-keyring'; -import { Encryptor, LEGACY_DERIVATION_PARAMS } from './Encryptor'; +import { Encryptor, LEGACY_DERIVATION_OPTIONS } from './Encryptor'; import { isMainnetByChainId, getDecimalChainId, @@ -198,7 +198,7 @@ import { const NON_EMPTY = 'NON_EMPTY'; const encryptor = new Encryptor({ - derivationParams: LEGACY_DERIVATION_PARAMS, + keyDerivationOptions: LEGACY_DERIVATION_OPTIONS, }); let currentChainId: any; @@ -213,6 +213,7 @@ interface TestOrigin { type: `${PhishingController['name']}:testOrigin`; handler: PhishingController['test']; } + type PhishingControllerActions = MaybeUpdateState | TestOrigin; type SnapsGlobalActions = @@ -240,7 +241,8 @@ type GlobalActions = | SnapsGlobalActions ///: END:ONLY_INCLUDE_IF | KeyringControllerActions - | AccountsControllerActions; + | AccountsControllerActions + | PreferencesControllerActions; type GlobalEvents = | ApprovalControllerEvents | CurrencyRateStateChange @@ -254,7 +256,8 @@ type GlobalEvents = | SignatureControllerEvents | KeyringControllerEvents | PPOMControllerEvents - | AccountsControllerEvents; + | AccountsControllerEvents + | PreferencesControllerEvents; type PermissionsByRpcMethod = ReturnType; type Permissions = PermissionsByRpcMethod[keyof PermissionsByRpcMethod]; @@ -290,6 +293,51 @@ export interface EngineState { AccountsController: AccountsControllerState; } +/** + * All mobile controllers, keyed by name + */ +interface Controllers { + AccountsController: AccountsController; + AccountTrackerController: AccountTrackerController; + AddressBookController: AddressBookController; + ApprovalController: ApprovalController; + AssetsContractController: AssetsContractController; + CurrencyRateController: CurrencyRateController; + GasFeeController: GasFeeController; + KeyringController: KeyringController; + LoggingController: LoggingController; + NetworkController: NetworkController; + NftController: NftController; + NftDetectionController: NftDetectionController; + // TODO: Fix permission types + PermissionController: PermissionController; + PhishingController: PhishingController; + PreferencesController: PreferencesController; + PPOMController: PPOMController; + TokenBalancesController: TokenBalancesController; + TokenListController: TokenListController; + TokenDetectionController: TokenDetectionController; + TokenRatesController: TokenRatesController; + TokensController: TokensController; + TransactionController: TransactionController; + SignatureController: SignatureController; + ///: BEGIN:ONLY_INCLUDE_IF(snaps) + SnapController: SnapController; + SubjectMetadataController: SubjectMetadataController; + ///: END:ONLY_INCLUDE_IF + SwapsController: SwapsController; +} + +/** + * Controllers that area always instantiated + */ +type RequiredControllers = Omit; + +/** + * Controllers that are sometimes not instantiated + */ +type OptionalControllers = Pick; + /** * Core controller responsible for composing other metamask controllers together * and exposing convenience methods for common wallet operations. @@ -302,35 +350,7 @@ class Engine { /** * A collection of all controller instances */ - context: - | { - AccountTrackerController: AccountTrackerController; - AddressBookController: AddressBookController; - ApprovalController: ApprovalController; - AssetsContractController: AssetsContractController; - CurrencyRateController: CurrencyRateController; - GasFeeController: GasFeeController; - KeyringController: KeyringController; - LoggingController: LoggingController; - NetworkController: NetworkController; - NftController: NftController; - NftDetectionController: NftDetectionController; - // TODO: Fix permission types - PermissionController: PermissionController; - PhishingController: PhishingController; - PreferencesController: PreferencesController; - PPOMController?: PPOMController; - TokenBalancesController: TokenBalancesController; - TokenListController: TokenListController; - TokenDetectionController: TokenDetectionController; - TokenRatesController: TokenRatesController; - TokensController: TokensController; - TransactionController: TransactionController; - SignatureController: SignatureController; - SwapsController: SwapsController; - AccountsController: AccountsController; - } - | any; + context: RequiredControllers & Partial; /** * The global controller messenger. */ @@ -351,6 +371,7 @@ class Engine { * Object that runs and manages the execution of Snaps */ snapExecutionService: WebViewExecutionService; + ///: END:ONLY_INCLUDE_IF /** @@ -672,7 +693,8 @@ class Engine { const qrKeyringBuilder = () => new QRHardwareKeyring(); qrKeyringBuilder.type = QRHardwareKeyring.type; - const ledgerKeyringBuilder = () => new LedgerKeyring(); + const bridge = new LedgerMobileBridge(new LedgerTransportMiddleware()); + const ledgerKeyringBuilder = () => new LedgerKeyring({ bridge }); ledgerKeyringBuilder.type = LedgerKeyring.type; const keyringController = new KeyringController({ diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 74eada9a390..e02b1e55166 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -29,7 +29,7 @@ export const getLedgerKeyring = async (): Promise => { // There should only be one ledger keyring. const keyring = keyringController.getKeyringsByType( ExtendedKeyringTypes.ledger, - ); + ) as LedgerKeyring[]; return keyring.length ? keyring[0] : await addLedgerKeyring(); }; @@ -77,13 +77,12 @@ export const closeRunningAppOnLedger = async (): Promise => { * Forgets the ledger keyring previous device specific state. */ export const forgetLedger = async (): Promise => { - const { KeyringController, PreferencesController } = Engine.context; + const { KeyringController } = Engine.context; const keyring = await getLedgerKeyring(); keyring.forgetDevice(); await KeyringController.persistAllKeyrings(); - PreferencesController.updateIdentities(await KeyringController.getAccounts()); }; /** diff --git a/patches/@metamask+keyring-controller+13.0.0.patch b/patches/@metamask+keyring-controller+13.0.0.patch index a073a687e39..898d9223519 100644 --- a/patches/@metamask+keyring-controller+13.0.0.patch +++ b/patches/@metamask+keyring-controller+13.0.0.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/@metamask/keyring-controller/dist/KeyringController.js b/node_modules/@metamask/keyring-controller/dist/KeyringController.js -index fc649ea..9f3aa85 100644 +index fc649ea..1767a63 100644 --- a/node_modules/@metamask/keyring-controller/dist/KeyringController.js +++ b/node_modules/@metamask/keyring-controller/dist/KeyringController.js @@ -576,6 +576,18 @@ class KeyringController extends base_controller_1.BaseController { @@ -21,7 +21,36 @@ index fc649ea..9f3aa85 100644 let vault; let newEncryptionKey; if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) { -@@ -1091,10 +1103,17 @@ _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_keyrings +@@ -1043,6 +1055,28 @@ class KeyringController extends base_controller_1.BaseController { + } + }); + } ++ ++ /** ++ + * This Patch is generated to support the multiple accounts support for ledger devices, ++ + * The reason to create this is to access the #keyring private method is not possible from the outside. ++ + * To add multiple accounts support for ledger Hardware wallet ++ + * ++ + * @param index - Index of the account to unlock ++ + * @param keyring - Keyring instance to unlock ++ + * @return {*} - Promise resolving to the current state ++ + */ ++ unlockLedgerWalletAccount(index, keyring) { ++ return __awaiter(this, void 0, void 0, function* () { ++ keyring.setAccountToUnlock(index); ++ // QRKeyring is not yet compatible with Keyring from ++ // @metamask/utils, but we can use the `addNewAccount` method ++ // as it internally calls `addAccounts` from on the keyring instance, ++ // which is supported by QRKeyring API. ++ yield this.addNewAccountForKeyring(keyring); ++ yield this.persistAllKeyrings(); ++ }); ++ } ++ + unlockQRHardwareWalletAccount(index) { + return __awaiter(this, void 0, void 0, function* () { + const keyring = yield this.getOrAddQRKeyring(); +@@ -1091,10 +1125,17 @@ _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_keyrings return __classPrivateFieldGet(this, _KeyringController_keyringBuilders, "f").find((keyringBuilder) => keyringBuilder.type === type); }, _KeyringController_addQRKeyring = function _KeyringController_addQRKeyring() { return __awaiter(this, void 0, void 0, function* () { diff --git a/patches/@metamask+keyring-controller+9.0.0.patch b/patches/@metamask+keyring-controller+9.0.0.patch deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/yarn.lock b/yarn.lock index cf6fff561c6..1ec6af47b97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3397,27 +3397,12 @@ rxjs "^7.8.1" semver "^7.3.5" -"@ledgerhq/devices@^8.3.0": - version "8.3.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.3.0.tgz#a1e1a21608e162fb3a512f57863bf9842b29493f" - integrity sha512-h5Scr+yIae8yjPOViCHLdMjpqn4oC2Whrsq8LinRxe48LEGMdPqSV1yY7+3Ch827wtzNpMv+/ilKnd8rY+rTlg== - dependencies: - "@ledgerhq/errors" "^6.16.4" - "@ledgerhq/logs" "^6.12.0" - rxjs "^7.8.1" - semver "^7.3.5" - "@ledgerhq/errors@^5.26.0", "@ledgerhq/errors@^5.50.0": version "5.50.0" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== -"@ledgerhq/errors@^6.10.0", "@ledgerhq/errors@^6.16.4": - version "6.16.4" - resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.4.tgz#a38baffe8b096d9fff3ad839cadb55704c8d8e7b" - integrity sha512-M57yFaLYSN+fZCX0E0zUqOmrV6eipK+s5RhijHoUNlHUqrsvUz7iRQgpd5gRgHB5VkIjav7KdaZjKiWGcHovaQ== - -"@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.3": +"@ledgerhq/errors@^6.10.0", "@ledgerhq/errors@^6.16.2", "@ledgerhq/errors@^6.16.3": version "6.16.3" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.3.tgz#646f68cc7e6e8d5126bce1ca06140c5ad963bee8" integrity sha512-3w7/SJVXOPa9mpzyll7VKoKnGwDD3BzWgN1Nom8byR40DiQvOKjHX+kKQausCedTHVNBn9euzPCNsftZ9+mxfw== @@ -3500,17 +3485,7 @@ "@ledgerhq/errors" "^5.50.0" events "^3.3.0" -"@ledgerhq/hw-transport@^6.24.1": - version "6.30.6" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.6.tgz#c6d84672ac4828f311831998f4101ea205215a6d" - integrity sha512-fT0Z4IywiuJuZrZE/+W0blkV5UCotDPFTYKLkKCLzYzuE6javva7D/ajRaIeR+hZ4kTmKF4EqnsmDCXwElez+w== - dependencies: - "@ledgerhq/devices" "^8.3.0" - "@ledgerhq/errors" "^6.16.4" - "@ledgerhq/logs" "^6.12.0" - events "^3.3.0" - -"@ledgerhq/hw-transport@^6.30.4": +"@ledgerhq/hw-transport@^6.24.1", "@ledgerhq/hw-transport@^6.30.4": version "6.30.5" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.5.tgz#841c9e4bb3849536db110ca2894d693d55bf54fd" integrity sha512-JMl//7BgPBvWxrWyMu82jj6JEYtsQyOyhYtonWNgtxn6KUZWht3gU4gxmLpeIRr+DiS7e50mW7m3GA+EudZmmA== @@ -3866,19 +3841,6 @@ "@metamask/safe-event-emitter" "^3.0.0" "@metamask/utils" "^8.3.0" -"@metamask/eth-keyring-controller@^15.0.0": - version "15.1.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-keyring-controller/-/eth-keyring-controller-15.1.0.tgz#dc7c5ec3a075a2eb3f90b1f452cd67232714451d" - integrity sha512-FL6bVet2Rp3n6z+tKM9Lp0dBhTNj7wPKFLBvTTqJq7wBEbXir/AdN7JtnSXWC4BzbfsonXtnGuX353z0x3+8lw== - dependencies: - "@ethereumjs/tx" "^4.2.0" - "@metamask/browser-passworder" "^4.3.0" - "@metamask/eth-hd-keyring" "^7.0.1" - "@metamask/eth-sig-util" "^7.0.0" - "@metamask/eth-simple-keyring" "^6.0.1" - "@metamask/obs-store" "^8.1.0" - "@metamask/utils" "^8.2.0" - "@metamask/eth-ledger-bridge-keyring@file:./package.tgz": version "3.0.0" resolved "file:./package.tgz#590a1b7e720327c6293b485209a2de0505573dd36c538a958b8bc77b2a8c433652652617d54844f08e81863ecfbdb30c4bfcb156e22e49a56539f679afa15eec" @@ -26376,7 +26338,7 @@ string-range@~1.2, string-range@~1.2.1: resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd" integrity sha1-qJPtNH5yKZvIO++78qaSqNI51d0= -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -26394,6 +26356,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -26517,7 +26488,14 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm: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-ansi@6.0.1, strip-ansi@^6.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== @@ -28595,8 +28573,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: - name wrap-ansi-cjs +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -28631,6 +28608,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From a0c2b1641b8430dd9ab7185be05fd4069d3b3fec Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 4 Jun 2024 02:25:37 +0100 Subject: [PATCH 20/75] fix: Backup new metric change. --- app/components/Views/AccountActions/AccountActions.tsx | 4 ++++ app/components/Views/LedgerSelectAccount/index.tsx | 5 +++++ app/core/Analytics/MetaMetrics.events.ts | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index a98d9a8cefb..4ed30662d84 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -171,6 +171,10 @@ const AccountActions = () => { await Controller.KeyringController.removeAccount(selectedAddress); await removeAccountsFromPermissions([selectedAddress]); const newAccounts = await Controller.KeyringController.getAccounts(); + trackEvent(MetaMetricsEvents.WALLET_REMOVED, { + accountType: kr.type, + address: selectedAddress, + }); // setSelectedAddress to the initial account Engine.setSelectedAddress(newAccounts[0]); diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 70f7f61a501..97817da49ff 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -141,6 +141,11 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { Logger.log('Error: Connecting QR hardware wallet', err); } setBlockingModalVisible(false); + + trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { + device_type: 'Ledger', + //TODO Do we need to add address here? + }); navigation.pop(2); }, [KeyringController, navigation], diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts index 6ae6e1aa44c..2b548dc778c 100644 --- a/app/core/Analytics/MetaMetrics.events.ts +++ b/app/core/Analytics/MetaMetrics.events.ts @@ -883,6 +883,7 @@ enum DESCRIPTION { WALLET_QR_SCANNER = 'QR scanner', WALLET_COPIED_ADDRESS = 'Copied Address', WALLET_ADD_COLLECTIBLES = 'Add Collectibles', + WALLET_REMOVED = 'Account removed', // Transactions TRANSACTIONS_CONFIRM_STARTED = 'Confirm Started', TRANSACTIONS_EDIT_TRANSACTION = 'Edit Transaction', @@ -1133,6 +1134,11 @@ const legacyMetaMetricsEvents = { ACTIONS.WALLET_VIEW, DESCRIPTION.WALLET_ADD_COLLECTIBLES, ), + WALLET_REMOVED: generateOpt( + EVENT_NAME.WALLET_VIEW, + ACTIONS.WALLET_VIEW, + DESCRIPTION.WALLET_REMOVED, + ), // Transactions TRANSACTIONS_CONFIRM_STARTED: generateOpt( EVENT_NAME.TRANSACTIONS, From f9941c0dddead997c7cc8694baa164c489127240 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Fri, 7 Jun 2024 09:34:03 +0100 Subject: [PATCH 21/75] fix: Rebase from main to upgrade to keyring-controller 16. This version will be first version which ledger work after keyring controller 16 upgrade. --- .../Views/AccountActions/AccountActions.tsx | 1 - .../ConnectHardware/SelectHardware/index.tsx | 6 - .../Views/LedgerAccountInfo/index.tsx | 9 +- .../Views/LedgerSelectAccount/index.tsx | 7 +- app/core/Engine.ts | 2 +- app/core/Ledger/Ledger.test.ts | 7 +- app/core/Ledger/Ledger.ts | 76 ++++++------- .../@metamask+keyring-controller+13.0.0.patch | 71 ------------ .../@metamask+keyring-controller+16.0.0.patch | 104 ++++++++++++++++-- yarn.lock | 20 ++-- 10 files changed, 146 insertions(+), 157 deletions(-) delete mode 100644 patches/@metamask+keyring-controller+13.0.0.patch diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 4ed30662d84..f2552e562b4 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -9,7 +9,6 @@ import Share from 'react-native-share'; import BottomSheet, { BottomSheetRef, } from '../../../component-library/components/BottomSheets/BottomSheet'; -import { useStyles } from '../../../component-library/hooks'; import AccountAction from '../AccountAction/AccountAction'; import { IconName } from '../../../component-library/components/Icons/Icon'; import { diff --git a/app/components/Views/ConnectHardware/SelectHardware/index.tsx b/app/components/Views/ConnectHardware/SelectHardware/index.tsx index 755018b6763..a259d92f200 100644 --- a/app/components/Views/ConnectHardware/SelectHardware/index.tsx +++ b/app/components/Views/ConnectHardware/SelectHardware/index.tsx @@ -16,7 +16,6 @@ import Text, { } from '../../../../component-library/components/Texts/Text'; import Routes from '../../../../constants/navigation/Routes'; import { MetaMetricsEvents } from '../../../../core/Analytics'; -import { withLedgerKeyring } from '../../../../core/Ledger/Ledger'; import { fontStyles } from '../../../../styles/common'; import { mockTheme, @@ -25,7 +24,6 @@ import { } from '../../../../util/theme'; import { getNavigationOptionsTitle } from '../../../UI/Navbar'; import { useMetrics } from '../../../../components/hooks/useMetrics'; -import type LedgerKeyring from '@consensys/ledgerhq-metamask-keyring'; const createStyle = (colors: any) => StyleSheet.create({ @@ -106,10 +104,6 @@ const SelectHardwareWallet = () => { }; const navigateToConnectLedger = async () => { - const accounts = await withLedgerKeyring(async (keyring: LedgerKeyring) => - keyring.getAccounts(), - ); - trackEvent(MetaMetricsEvents.CONNECT_LEDGER, { device_type: 'Ledger', }); diff --git a/app/components/Views/LedgerAccountInfo/index.tsx b/app/components/Views/LedgerAccountInfo/index.tsx index 54b4062c23a..d7b4bbac0ac 100644 --- a/app/components/Views/LedgerAccountInfo/index.tsx +++ b/app/components/Views/LedgerAccountInfo/index.tsx @@ -15,7 +15,7 @@ import { strings } from '../../../../locales/i18n'; import { setReloadAccounts } from '../../../actions/accounts'; import { NO_RPC_BLOCK_EXPLORER, RPC } from '../../../constants/network'; import Engine from '../../../core/Engine'; -import { forgetLedger, getLedgerKeyring } from '../../../core/Ledger/Ledger'; +import { forgetLedger, withLedgerKeyring } from '../../../core/Ledger/Ledger'; import Device from '../../../util/device'; import { getEtherscanAddressUrl } from '../../../util/etherscan'; import { findBlockExplorerForRpc } from '../../../util/networks'; @@ -106,13 +106,14 @@ const LedgerAccountInfo = () => { useEffect(() => { const getAccount = async () => { - const ledgerKeyring = await getLedgerKeyring(); - const accounts = await ledgerKeyring.getAccounts(); + const accounts = await withLedgerKeyring( + async (keyring: any) => await keyring.getAccounts(), + ); setAccount(accounts[0]); }; - getAccount(); + getAccount().then(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 97817da49ff..b1d9ae4416a 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -21,7 +21,6 @@ import ledgerDeviceDarkImage from 'images/ledger-device-dark.png'; import { forgetLedger, getLedgerAccountsByPage, - getLedgerKeyring, } from '../../../core/Ledger/Ledger'; import LedgerConnect from '../LedgerConnect'; import { setReloadAccounts } from '../../../actions/accounts'; @@ -132,10 +131,10 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { const onUnlock = useCallback( async (accountIndexes: number[]) => { setBlockingModalVisible(true); - const keyring = await getLedgerKeyring(); + try { for (const index of accountIndexes) { - await KeyringController.unlockLedgerWalletAccount(index, keyring); + await KeyringController.unlockLedgerWalletAccount(index); } } catch (err) { Logger.log('Error: Connecting QR hardware wallet', err); @@ -148,7 +147,7 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { }); navigation.pop(2); }, - [KeyringController, navigation], + [KeyringController, navigation, trackEvent], ); const onForget = useCallback(async () => { diff --git a/app/core/Engine.ts b/app/core/Engine.ts index 4f5dcfc6a42..f8f6f315e56 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -118,7 +118,7 @@ import { buildSnapEndowmentSpecifications, buildSnapRestrictedMethodSpecifications, } from '@metamask/snaps-rpc-methods'; -import type { DialogType, EnumToUnion } from '@metamask/snaps-sdk'; +import type { EnumToUnion, DialogType } from '@metamask/snaps-sdk'; // eslint-disable-next-line import/no-nodejs-modules import { Duplex } from 'stream'; ///: END:ONLY_INCLUDE_IF diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index d1c28a2f9be..2af2cb90dce 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -5,15 +5,10 @@ import { closeRunningAppOnLedger, forgetLedger, ledgerSignTypedMessage, - unlockLedgerDefaultAccount, getDeviceId, - withLedgerKeyring, } from './Ledger'; import Engine from '../../core/Engine'; -import { - KeyringTypes, - SignTypedDataVersion, -} from '@metamask/keyring-controller'; +import { SignTypedDataVersion } from '@metamask/keyring-controller'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; jest.mock('../../core/Engine', () => ({ diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 649b55c5ec8..668d5e5a2e9 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -1,8 +1,11 @@ -import LedgerKeyring from '@consensys/ledgerhq-metamask-keyring'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; import ExtendedKeyringTypes from '../../constants/keyringTypes'; import Engine from '../Engine'; +import { + LedgerKeyring, + LedgerMobileBridge, +} from '@metamask/eth-ledger-bridge-keyring'; /** * Perform an operation with the Ledger keyring. @@ -43,44 +46,24 @@ export const connectLedgerHardware = async ( ): Promise => { const appAndVersion = await withLedgerKeyring( async (keyring: LedgerKeyring) => { - keyring.setTransport(transport as unknown as any, deviceId); - return await keyring.getAppAndVersion(); + keyring.setHdPath("m/44'/60'/0'/0"); + keyring.setDeviceId(deviceId); + const bridge = keyring.bridge as LedgerMobileBridge; + await bridge.updateTransportMethod(transport); + return await bridge.getAppNameAndVersion(); }, ); return appAndVersion.appName; }; -/** - * Retrieve the first account from the Ledger device. - * @param isAccountImportReq - Whether we need to import a ledger account by calling addNewAccountForKeyring - * @returns The default (first) account on the device - */ -export const unlockLedgerDefaultAccount = async ( - isAccountImportReq: boolean, -): Promise<{ - address: string; - balance: string; -}> => { - const address = await withLedgerKeyring(async (keyring: LedgerKeyring) => { - if (isAccountImportReq) { - await keyring.addAccounts(1); - } - return await keyring.getDefaultAccount(); - }); - - return { - address, - balance: `0x0`, - }; -}; - /** * Automatically opens the Ethereum app on the Ledger device. */ export const openEthereumAppOnLedger = async (): Promise => { await withLedgerKeyring(async (keyring: LedgerKeyring) => { - await keyring.openEthApp(); + const bridge = keyring.bridge as LedgerMobileBridge; + await bridge.openEthApp(); }); }; @@ -89,7 +72,8 @@ export const openEthereumAppOnLedger = async (): Promise => { */ export const closeRunningAppOnLedger = async (): Promise => { await withLedgerKeyring(async (keyring: LedgerKeyring) => { - await keyring.quitApp(); + const bridge = keyring.bridge as LedgerMobileBridge; + await bridge.closeApps(); }); }; @@ -98,7 +82,7 @@ export const closeRunningAppOnLedger = async (): Promise => { */ export const forgetLedger = async (): Promise => { await withLedgerKeyring(async (keyring: LedgerKeyring) => { - await keyring.forgetDevice(); + keyring.forgetDevice(); }); }; @@ -108,25 +92,29 @@ export const forgetLedger = async (): Promise => { * @returns The DeviceId */ export const getDeviceId = async (): Promise => - await withLedgerKeyring(async (keyring: LedgerKeyring) => keyring.getDeviceId()); -}; + await withLedgerKeyring(async (keyring: LedgerKeyring) => + keyring.getDeviceId(), + ); +/** + * Unlock Ledger Accounts by page + * @param page - The page number to unlock + */ export const getLedgerAccountsByPage = async ( page: number, ): Promise<{ balance: string; address: string; index: number }[]> => { try { - const keyring = await getLedgerKeyring(); - let accounts; - switch (page) { - case -1: - accounts = await keyring.getPreviousPage(); - break; - case 1: - accounts = await keyring.getNextPage(); - break; - default: - accounts = await keyring.getFirstPage(); - } + const accounts = await withLedgerKeyring(async (keyring: LedgerKeyring) => { + switch (page) { + case -1: + return await keyring.getPreviousPage(); + case 1: + return await keyring.getNextPage(); + default: + return await keyring.getFirstPage(); + } + }); + return accounts.map((account) => ({ ...account, balance: '0x0', diff --git a/patches/@metamask+keyring-controller+13.0.0.patch b/patches/@metamask+keyring-controller+13.0.0.patch deleted file mode 100644 index a073a687e39..00000000000 --- a/patches/@metamask+keyring-controller+13.0.0.patch +++ /dev/null @@ -1,71 +0,0 @@ -diff --git a/node_modules/@metamask/keyring-controller/dist/KeyringController.js b/node_modules/@metamask/keyring-controller/dist/KeyringController.js -index fc649ea..9f3aa85 100644 ---- a/node_modules/@metamask/keyring-controller/dist/KeyringController.js -+++ b/node_modules/@metamask/keyring-controller/dist/KeyringController.js -@@ -576,6 +576,18 @@ class KeyringController extends base_controller_1.BaseController { - return { type, data }; - }))); - serializedKeyrings.push(...__classPrivateFieldGet(this, _KeyringController_unsupportedKeyrings, "f")); -+ /** -+ * ============================== PATCH INFORMATION ============================== -+ * The HD keyring is the default keyring for all wallets if this keyring is missing -+ * for some reason we should avoid saving the keyrings -+ * -+ * The upstream fix is here: https://github.com/MetaMask/core/pull/4168 -+ * -+ * This patch can be found on the core branch `extension-keyring-controller-v13-patch` -+ */ -+ if (!serializedKeyrings.some((keyring) => keyring.type === KeyringTypes.hd)) { -+ throw new Error(constants_1.KeyringControllerError.NoHdKeyring); -+ } - let vault; - let newEncryptionKey; - if (__classPrivateFieldGet(this, _KeyringController_cacheEncryptionKey, "f")) { -@@ -1091,10 +1103,17 @@ _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_keyrings - return __classPrivateFieldGet(this, _KeyringController_keyringBuilders, "f").find((keyringBuilder) => keyringBuilder.type === type); - }, _KeyringController_addQRKeyring = function _KeyringController_addQRKeyring() { - return __awaiter(this, void 0, void 0, function* () { -- // QRKeyring is not yet compatible with Keyring type from @metamask/utils -- const qrKeyring = (yield __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_newKeyring).call(this, KeyringTypes.qr, { -- accounts: [], -- })); -+ // QRKeyring is not yet compatible with Keyring type from @metamask/utils -+ /** -+ * Patch for @metamask/keyring-controller v13.0.0 -+ * Below code change will fix the issue 23804, The intial code added a empty accounts as argument when creating a new QR keyring. -+ * cause the new Keystone MetamaskKeyring default properties all are undefined during deserialise() process. -+ * Please refer to PR 23903 for detail. -+ * -+ * This patch can be found on the core branch `extension-keyring-controller-v13-patch` -+ */ -+ // @ts-expect-error See patch note -+ const qrKeyring = (yield __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_newKeyring).call(this, KeyringTypes.qr)); - const accounts = yield qrKeyring.getAccounts(); - yield __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_checkForDuplicate).call(this, KeyringTypes.qr, accounts); - __classPrivateFieldGet(this, _KeyringController_keyrings, "f").push(qrKeyring); -diff --git a/node_modules/@metamask/keyring-controller/dist/constants.d.ts b/node_modules/@metamask/keyring-controller/dist/constants.d.ts -index 0c02177..805c0d5 100644 ---- a/node_modules/@metamask/keyring-controller/dist/constants.d.ts -+++ b/node_modules/@metamask/keyring-controller/dist/constants.d.ts -@@ -25,6 +25,7 @@ export declare enum KeyringControllerError { - MissingVaultData = "KeyringController - Cannot persist vault without vault information", - ExpiredCredentials = "KeyringController - Encryption key and salt provided are expired", - NoKeyringBuilder = "KeyringController - No keyringBuilder found for keyring", -- DataType = "KeyringController - Incorrect data type provided" -+ DataType = "KeyringController - Incorrect data type provided", -+ NoHdKeyring = "KeyringController - No HD Keyring found" - } - //# sourceMappingURL=constants.d.ts.map -\ No newline at end of file -diff --git a/node_modules/@metamask/keyring-controller/dist/constants.js b/node_modules/@metamask/keyring-controller/dist/constants.js -index 58b3a15..10768a8 100644 ---- a/node_modules/@metamask/keyring-controller/dist/constants.js -+++ b/node_modules/@metamask/keyring-controller/dist/constants.js -@@ -30,5 +30,6 @@ var KeyringControllerError; - KeyringControllerError["ExpiredCredentials"] = "KeyringController - Encryption key and salt provided are expired"; - KeyringControllerError["NoKeyringBuilder"] = "KeyringController - No keyringBuilder found for keyring"; - KeyringControllerError["DataType"] = "KeyringController - Incorrect data type provided"; -+ KeyringControllerError["NoHdKeyring"] = "KeyringController - No HD Keyring found"; - })(KeyringControllerError = exports.KeyringControllerError || (exports.KeyringControllerError = {})); - //# sourceMappingURL=constants.js.map -\ No newline at end of file diff --git a/patches/@metamask+keyring-controller+16.0.0.patch b/patches/@metamask+keyring-controller+16.0.0.patch index 30d86b784e6..eb9f4d5458e 100644 --- a/patches/@metamask+keyring-controller+16.0.0.patch +++ b/patches/@metamask+keyring-controller+16.0.0.patch @@ -47,13 +47,46 @@ index 6d75e83..f45940f 100644 export { AccountImportStrategy, diff --git a/node_modules/@metamask/keyring-controller/dist/chunk-2GJQ6KDW.js b/node_modules/@metamask/keyring-controller/dist/chunk-EGSSQX6L.js -similarity index 99% +similarity index 98% rename from node_modules/@metamask/keyring-controller/dist/chunk-2GJQ6KDW.js rename to node_modules/@metamask/keyring-controller/dist/chunk-EGSSQX6L.js -index 81fdecf..3844651 100644 +index 81fdecf..e3e959c 100644 --- a/node_modules/@metamask/keyring-controller/dist/chunk-2GJQ6KDW.js +++ b/node_modules/@metamask/keyring-controller/dist/chunk-EGSSQX6L.js -@@ -1113,9 +1113,7 @@ getKeyringBuilderForType_fn = function(type) { +@@ -1022,6 +1022,32 @@ var KeyringController = class extends _basecontroller.BaseController { + await keyring.addAccounts(1); + }); + } ++ ++ /** ++ * Get Ledger keyring. ++ * ++ * @returns The Ledger Keyring if defined, otherwise undefined ++ */ ++ getLedgerKeyring() { ++ return this.getKeyringsByType("Ledger Hardware" /* qr */)[0]; ++ } ++ ++ /** ++ * This Patch is generated to support the multiple accounts support for ledger devices, ++ * The reason to create this is to access the #keyring private method is not possible from the outside. ++ * To add multiple accounts support for ledger Hardware wallet ++ * ++ * @param index - Index of the account to unlock ++ * @return {*} - Promise resolving to the current state ++ */ ++ async unlockLedgerWalletAccount(index) { ++ return _chunkZGV2QNCGjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { ++ const keyring = this.getLedgerKeyring(); // the keyring will not be none since scan stage already create ledgerKeyring ++ keyring.setAccountToUnlock(index); ++ await keyring.addAccounts(1); ++ }); ++ } ++ + async getAccountKeyringType(account) { + const keyring = await this.getKeyringForAccount( + account +@@ -1113,9 +1139,7 @@ getKeyringBuilderForType_fn = function(type) { _addQRKeyring = new WeakSet(); addQRKeyring_fn = async function() { _chunkZGV2QNCGjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); @@ -64,22 +97,53 @@ index 81fdecf..3844651 100644 }; _subscribeToQRKeyringEvents = new WeakSet(); subscribeToQRKeyringEvents_fn = function(qrKeyring) { -@@ -1465,4 +1463,4 @@ var KeyringController_default = KeyringController; +@@ -1465,4 +1489,4 @@ var KeyringController_default = KeyringController; exports.KeyringTypes = KeyringTypes; exports.isCustodyKeyring = isCustodyKeyring; exports.AccountImportStrategy = AccountImportStrategy; exports.SignTypedDataVersion = SignTypedDataVersion; exports.keyringBuilderFactory = keyringBuilderFactory; exports.getDefaultKeyringState = getDefaultKeyringState; exports.KeyringController = KeyringController; exports.KeyringController_default = KeyringController_default; -//# sourceMappingURL=chunk-2GJQ6KDW.js.map \ No newline at end of file +//# sourceMappingURL=chunk-EGSSQX6L.js.map -\ No newline at end of file diff --git a/node_modules/@metamask/keyring-controller/dist/chunk-USAGXPFN.mjs b/node_modules/@metamask/keyring-controller/dist/chunk-MQVFNKTH.mjs -similarity index 99% +similarity index 98% rename from node_modules/@metamask/keyring-controller/dist/chunk-USAGXPFN.mjs rename to node_modules/@metamask/keyring-controller/dist/chunk-MQVFNKTH.mjs -index 0cf6255..6b769d8 100644 +index 0cf6255..5147bd0 100644 --- a/node_modules/@metamask/keyring-controller/dist/chunk-USAGXPFN.mjs +++ b/node_modules/@metamask/keyring-controller/dist/chunk-MQVFNKTH.mjs -@@ -1113,9 +1113,7 @@ getKeyringBuilderForType_fn = function(type) { +@@ -1022,6 +1022,31 @@ var KeyringController = class extends BaseController { + await keyring.addAccounts(1); + }); + } ++ ++ /** ++ * Get Ledger Hardware keyring. ++ * ++ * @returns The Ledger Keyring if defined, otherwise undefined ++ */ ++ getLedgerKeyring() { ++ return this.getKeyringsByType("Ledger Hardware" /* qr */)[0]; ++ } ++ /** ++ * This Patch is generated to support the multiple accounts support for ledger devices, ++ * The reason to create this is to access the #keyring private method is not possible from the outside. ++ * To add multiple accounts support for ledger Hardware wallet ++ * ++ * @param index - Index of the account to unlock ++ * @param keyring - Ledger Keyring instance to unlock ++ * @return {*} - Promise resolving to the current state ++ */ ++ async unlockLedgerWalletAccount(index, keyring) { ++ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { ++ keyring.setAccountToUnlock(index); ++ await keyring.addAccounts(1); ++ }); ++ } ++ + async getAccountKeyringType(account) { + const keyring = await this.getKeyringForAccount( + account +@@ -1113,9 +1138,7 @@ getKeyringBuilderForType_fn = function(type) { _addQRKeyring = new WeakSet(); addQRKeyring_fn = async function() { __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); @@ -90,14 +154,13 @@ index 0cf6255..6b769d8 100644 }; _subscribeToQRKeyringEvents = new WeakSet(); subscribeToQRKeyringEvents_fn = function(qrKeyring) { -@@ -1465,4 +1463,4 @@ export { +@@ -1465,4 +1488,4 @@ export { KeyringController, KeyringController_default }; -//# sourceMappingURL=chunk-USAGXPFN.mjs.map \ No newline at end of file +//# sourceMappingURL=chunk-MQVFNKTH.mjs.map -\ No newline at end of file diff --git a/node_modules/@metamask/keyring-controller/dist/index.js b/node_modules/@metamask/keyring-controller/dist/index.js index 35b2a0f..655541f 100644 --- a/node_modules/@metamask/keyring-controller/dist/index.js @@ -132,3 +195,24 @@ index ba28e4a..443b073 100644 import "./chunk-4OE2G6WW.mjs"; export { AccountImportStrategy, +diff --git a/node_modules/@metamask/keyring-controller/dist/types/KeyringController.d.ts b/node_modules/@metamask/keyring-controller/dist/types/KeyringController.d.ts +index b976e18..16d882f 100644 +--- a/node_modules/@metamask/keyring-controller/dist/types/KeyringController.d.ts ++++ b/node_modules/@metamask/keyring-controller/dist/types/KeyringController.d.ts +@@ -7,6 +7,7 @@ import type { EthBaseTransaction, EthBaseUserOperation, EthKeyring, EthUserOpera + import type { PersonalMessageParams, TypedMessageParams } from '@metamask/message-manager'; + import type { Eip1024EncryptedData, Hex, Json, KeyringClass } from '@metamask/utils'; + import type { Patch } from 'immer'; ++import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; + declare const name = "KeyringController"; + /** + * Available keyring types +@@ -602,6 +603,8 @@ export declare class KeyringController extends BaseController; + unlockQRHardwareWalletAccount(index: number): Promise; ++ getLedgerKeyring(): LedgerKeyring | undefined; ++ unlockLedgerWalletAccount(index: number): Promise; + getAccountKeyringType(account: string): Promise; + forgetQRDevice(): Promise<{ + removedAccounts: string[]; diff --git a/yarn.lock b/yarn.lock index 284bb918fd3..7c0c5059df0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1891,7 +1891,7 @@ dependencies: "@ethereumjs/util" "^9.0.3" -"@ethereumjs/rlp@^4.0.1": +"@ethereumjs/rlp@^4.0.0", "@ethereumjs/rlp@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== @@ -3972,6 +3972,15 @@ "@metamask/safe-event-emitter" "^3.0.0" "@metamask/utils" "^8.3.0" +"@metamask/eth-json-rpc-provider@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-3.0.2.tgz#42e544d227285fe56336e2301961a6283dcfadad" + integrity sha512-ma5bYjKa71bSw5+iibEnIiY25s8wkDnTljrqOnGw5MkTEU4PQDiKnK9YjxfUZSasx2BPEsp1OW2NS+pnrRMO4Q== + dependencies: + "@metamask/json-rpc-engine" "^8.0.2" + "@metamask/safe-event-emitter" "^3.0.0" + "@metamask/utils" "^8.3.0" + "@metamask/eth-ledger-bridge-keyring@file:./package.tgz": version "3.0.0" resolved "file:./package.tgz#590a1b7e720327c6293b485209a2de0505573dd36c538a958b8bc77b2a8c433652652617d54844f08e81863ecfbdb30c4bfcb156e22e49a56539f679afa15eec" @@ -3983,15 +3992,6 @@ "@metamask/eth-sig-util" "^7.0.1" hdkey "^2.1.0" -"@metamask/eth-json-rpc-provider@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-3.0.2.tgz#42e544d227285fe56336e2301961a6283dcfadad" - integrity sha512-ma5bYjKa71bSw5+iibEnIiY25s8wkDnTljrqOnGw5MkTEU4PQDiKnK9YjxfUZSasx2BPEsp1OW2NS+pnrRMO4Q== - dependencies: - "@metamask/json-rpc-engine" "^8.0.2" - "@metamask/safe-event-emitter" "^3.0.0" - "@metamask/utils" "^8.3.0" - "@metamask/eth-query@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-query/-/eth-query-3.0.1.tgz#3439eb6c7d5ccff1d6a66df1d1802bae0c890444" From 192fdb4ec1aafcb8e5beed40eb66ffa163ef64f6 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 11 Jun 2024 09:57:22 +0100 Subject: [PATCH 22/75] feat: Add unit tests to cover all functions in ledger core. --- app/core/Ledger/Ledger.test.ts | 125 ++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 48 deletions(-) diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 2af2cb90dce..064f2075d09 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -1,15 +1,16 @@ -import LedgerKeyring from '@consensys/ledgerhq-metamask-keyring'; import { - connectLedgerHardware, - openEthereumAppOnLedger, closeRunningAppOnLedger, + connectLedgerHardware, forgetLedger, - ledgerSignTypedMessage, getDeviceId, + getLedgerAccountsByPage, + ledgerSignTypedMessage, + openEthereumAppOnLedger, } from './Ledger'; import Engine from '../../core/Engine'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; +import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; jest.mock('../../core/Engine', () => ({ context: { @@ -26,20 +27,61 @@ describe('Ledger core', () => { beforeEach(() => { jest.resetAllMocks(); - - // @ts-expect-error This is a partial mock, not completely identical - // TODO: Replace this with a type-safe mock + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore ledgerKeyring = { addAccounts: jest.fn(), - setTransport: jest.fn(), - getAppAndVersion: jest.fn().mockResolvedValue({ appName: 'appName' }), - getDefaultAccount: jest.fn().mockResolvedValue('defaultAccount'), - openEthApp: jest.fn(), - quitApp: jest.fn(), - forgetDevice: jest.fn(), + bridge: { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + getAppNameAndVersion: jest + .fn() + .mockResolvedValue({ appName: 'appName' }), + updateTransportMethod: jest.fn(), + openEthApp: jest.fn(), + closeApps: jest.fn(), + }, deserialize: jest.fn(), - deviceId: 'deviceId', - getName: jest.fn().mockResolvedValue('name'), + forgetDevice: jest.fn(), + getDeviceId: jest.fn().mockReturnValue('deviceId'), + getFirstPage: jest.fn().mockResolvedValue([ + { + balance: '0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', + index: 0, + }, + { + balance: '1', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', + index: 1, + }, + ]), + getNextPage: jest.fn().mockResolvedValue([ + { + balance: '4', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', + index: 4, + }, + { + balance: '5', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', + index: 5, + }, + ]), + getPreviousPage: jest.fn().mockResolvedValue([ + { + balance: '2', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', + index: 2, + }, + { + balance: '3', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', + index: 3, + }, + ]), + setDeviceId: jest.fn(), + setHdPath: jest.fn(), }; const mockKeyringController = MockEngine.context.KeyringController; @@ -54,12 +96,12 @@ describe('Ledger core', () => { const mockTransport = 'foo' as unknown as BleTransport; it('should call keyring.setTransport', async () => { await connectLedgerHardware(mockTransport, 'bar'); - expect(ledgerKeyring.setTransport).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.updateTransportMethod).toHaveBeenCalled(); }); it('should call keyring.getAppAndVersion', async () => { await connectLedgerHardware(mockTransport, 'bar'); - expect(ledgerKeyring.getAppAndVersion).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.getAppNameAndVersion).toHaveBeenCalled(); }); it('should return app name', async () => { @@ -68,45 +110,17 @@ describe('Ledger core', () => { }); }); - describe('unlockLedgerDefaultAccount', () => { - it('should not call KeyringController.addNewAccountForKeyring if isAccountImportReq is false', async () => { - const account = await unlockLedgerDefaultAccount(false); - expect( - MockEngine.context.KeyringController.addNewAccountForKeyring, - ).not.toHaveBeenCalled(); - expect(ledgerKeyring.getDefaultAccount).toHaveBeenCalled(); - - expect(account).toEqual({ - address: 'defaultAccount', - balance: '0x0', - }); - }); - - it('should call KeyringController.addNewAccountForKeyring if isAccountImportReq is true', async () => { - const account = await unlockLedgerDefaultAccount(true); - expect( - MockEngine.context.KeyringController.addNewAccountForKeyring, - ).toHaveBeenCalledWith(ledgerKeyringClone); - expect(ledgerKeyring.getDefaultAccount).toHaveBeenCalled(); - - expect(account).toEqual({ - address: 'defaultAccount', - balance: '0x0', - }); - }); - }); - describe('openEthereumAppOnLedger', () => { it('should call keyring.openEthApp', async () => { await openEthereumAppOnLedger(); - expect(ledgerKeyring.openEthApp).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.openEthApp).toHaveBeenCalled(); }); }); describe('closeRunningAppOnLedger', () => { it('should call keyring.quitApp', async () => { await closeRunningAppOnLedger(); - expect(ledgerKeyring.quitApp).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.closeApps).toHaveBeenCalled(); }); }); @@ -120,7 +134,22 @@ describe('Ledger core', () => { describe('getDeviceId', () => { it('should return deviceId', async () => { const value = await getDeviceId(); - expect(value).toBe('deviceIdClone'); + expect(value).toBe('deviceId'); + }); + }); + + describe('getLedgerAccountsByPage', () => { + it('should call getNextPage on ledgerKeyring', async () => { + await getLedgerAccountsByPage(1); + expect(ledgerKeyring.getNextPage).toHaveBeenCalled(); + }); + it('should call getNextPage on ledgerKeyring', async () => { + await getLedgerAccountsByPage(-1); + expect(ledgerKeyring.getPreviousPage).toHaveBeenCalled(); + }); + it('should call getFirstPage on ledgerKeyring', async () => { + await getLedgerAccountsByPage(0); + expect(ledgerKeyring.getFirstPage).toHaveBeenCalled(); }); }); From 5dd2f5182a718784108a2309dbb88a259a439d5c Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 17 Jun 2024 14:49:50 +0100 Subject: [PATCH 23/75] feat: Fix the broken unit tests after rebase. --- .../AccountActions/AccountActions.test.tsx | 24 +++++++++++++++---- .../Views/AccountActions/AccountActions.tsx | 4 ++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index 07f6e5f04ef..7522b62fb68 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -15,10 +15,7 @@ import { VIEW_ETHERSCAN, } from './AccountActions.constants'; import initialBackgroundState from '../../../util/test/initial-background-state.json'; -import { - MOCK_ACCOUNTS_CONTROLLER_STATE, - MOCK_ADDRESS_2, -} from '../../../util/test/accountsControllerTestUtils'; +import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils'; import { toChecksumHexAddress } from '@metamask/controller-utils'; const mockEngine = Engine; @@ -35,6 +32,21 @@ const initialState = { jest.mock('../../../core/Engine', () => ({ init: () => mockEngine.init({}), + context: { + PreferencesController: { + selectedAddress: `0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756`, + }, + KeyringController: { + state: { + keyrings: [ + { + type: 'HD Key Tree', + accounts: ['0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756'], + }, + ], + }, + }, + }, })); const mockNavigate = jest.fn(); @@ -107,7 +119,9 @@ describe('AccountActions', () => { fireEvent.press(getByTestId(SHARE_ADDRESS)); expect(Share.open).toHaveBeenCalledWith({ - message: toChecksumHexAddress(MOCK_ADDRESS_2), + message: toChecksumHexAddress( + '0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756', + ), }); }); diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 385e7e43876..31d2a11165c 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -164,7 +164,7 @@ const AccountActions = () => { }, []); const triggerRemoveHWAccount = useCallback(async () => { - if (blockingModalVisible) { + if (blockingModalVisible && selectedAddress) { const kr = getKeyringByAddress(selectedAddress); let requestForgetDevice = false; @@ -262,7 +262,7 @@ const AccountActions = () => { onPress={goToExportPrivateKey} {...generateTestId(Platform, SHOW_PRIVATE_KEY)} /> - {isHardwareAccount(selectedAddress) && ( + {selectedAddress && isHardwareAccount(selectedAddress) && ( Date: Tue, 25 Jun 2024 12:46:22 +0100 Subject: [PATCH 24/75] feat: change ho use @metamask/eth-ledger-keyring-bridge@4.0.0 version. --- app/core/Ledger/Ledger.test.ts | 2 +- package.json | 6 ++--- package.tgz | Bin 33853 -> 0 bytes yarn.lock | 46 ++++++++++++++------------------- 4 files changed, 23 insertions(+), 31 deletions(-) delete mode 100644 package.tgz diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 064f2075d09..d33505cfb00 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -143,7 +143,7 @@ describe('Ledger core', () => { await getLedgerAccountsByPage(1); expect(ledgerKeyring.getNextPage).toHaveBeenCalled(); }); - it('should call getNextPage on ledgerKeyring', async () => { + it('should call getPreviousPage on ledgerKeyring', async () => { await getLedgerAccountsByPage(-1); expect(ledgerKeyring.getPreviousPage).toHaveBeenCalled(); }); diff --git a/package.json b/package.json index 6d303021346..8f2577ca018 100644 --- a/package.json +++ b/package.json @@ -143,16 +143,16 @@ "@metamask/contract-metadata": "^2.1.0", "@metamask/controller-utils": "^8.0.4", "@metamask/design-tokens": "^4.0.0", + "@metamask/eth-ledger-bridge-keyring": "^4.0.0", "@metamask/eth-sig-util": "^7.0.2", - "@metamask/eth-ledger-bridge-keyring": "file:./package.tgz", "@metamask/etherscan-link": "^2.0.0", "@metamask/gas-fee-controller": "^15.1.2", "@metamask/key-tree": "^9.0.0", "@metamask/keyring-api": "^4.0.0", "@metamask/keyring-controller": "^16.0.0", - "@metamask/network-controller": "^18.1.0", "@metamask/logging-controller": "^3.0.0", "@metamask/message-signing-snap": "^0.3.3", + "@metamask/network-controller": "^18.1.0", "@metamask/permission-controller": "^9.0.0", "@metamask/phishing-controller": "^9.0.0", "@metamask/post-message-stream": "^8.0.0", @@ -169,11 +169,11 @@ "@metamask/sdk-communication-layer": "^0.20.2", "@metamask/signature-controller": "^16.0.0", "@metamask/slip44": "3.1.0", + "@metamask/smart-transactions-controller": "10.1.1", "@metamask/snaps-controllers": "^8.3.1", "@metamask/snaps-rpc-methods": "^9.0.0", "@metamask/snaps-sdk": "^4.2.0", "@metamask/snaps-utils": "^7.4.0", - "@metamask/smart-transactions-controller": "10.1.1", "@metamask/swappable-obj-proxy": "^2.1.0", "@metamask/swaps-controller": "^9.0.0", "@metamask/transaction-controller": "^13.0.0", diff --git a/package.tgz b/package.tgz deleted file mode 100644 index a5913a8ca8ad71a8dc1e41afb315de591329e862..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33853 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

1rt>IN|#G+?tsIv9(}712RI zWVr)6Xs0ZQ=Acjgbptt=EHyK%gR!(thMXt7gT`fcJcrq!^iU2uAQ`(s2QejVgVt6% zFB0uUIcLxYeNi~#FO-(V%s>pBc}tNPOy$uC#h~M0MpR&pO4#rjblwhB+z-sYAt+g! zr^RbvyNN4cg~kTPfnFZxtKuq%Ruy3twDofR_zL=REQeQ6py!CKAlXe>#Z>!S3a%i9 zcgYy@tq~UNMPlv+QYV<}PLPjxC>YOuJIHxd1SQy<|5C_t=i!dgAUMhh)38~6)A>X< zUe)V@L8LsK8L~l&{ffv9+C7Sf{5M2!(AUZ>1aVN?)%9XHSS$Bi;yCDxR(+c={el96 zB>c1uDy(|6$zpK23%m58vgd z(P)szN}+}om~Ss8X-F4I?d507V7-Cb0;+Wtz50nO-m_%Ux3mrGls33_~dAzXMs z^W0-lgxjcuATH4H+B}c?E8`-HElB%u*eM3TrO zW}^{|qLqw&t>durUXawIlxI?Zk)8*3^)vY9s(cV7XY^zus^6#DcZCMqFCi$iIR|BJ~i{Jo*}%7o7r%S2r>q41z|oA4s@gHj9Q~6gM(ZxnfKF-6h$C7_ zFQ%Pw95=hKHa5((-%}F*a)L+|&)|PoR$s{O74?v^=D^+LkhCi|7@@wXPxFXouSy@8 zgF6j5!R+MdGbX7#9n)q(FxJ^7TjN`m+%XD@K^YvM7FH!;>vz*y$a74YI_7P<+|pdh zZ*>QyFT63i((?vi6QBq?_RUFrbDBj#9DSyGxkAT9)VXG~n%BN{zUP{cD>Z02FrEkb zRge`?5I?RKG*7BXRJ8d>YW9%0rYqA0%Zkh9priZ^Um#Fopd97$U|1^4X~Ai6lrjJ{ z6ue@2k*_X1a#}pcd8?VzXH;Sfwq}WEk6$rA56_e!*T}+Z2{9Oo<(}7qA^a~lgi@1t z?^RCamteuP2-K&=PL$;ZNESCM24R??gT2j6CqWERB{iBr<`#`rxe@C~k zGI}1R1#xVK z%%XC`6DTm7U1=c3%E`+&Qf^?*mB~WN}QJ*_wPM+2{Y&^^G6r|L^hR%>RwofXyfgVfj0fLF?t11zLp6WhLY(k$Gcl zsYiw$IrGTbx{(5U9W8cJvMdw$LU@c@z$2u2Ple`%8;OfVq|t5Dyq85I(2xPRmaF+E zYz8PT_Gz&QH@x`eDfwwwS1EVNCVsAqgwoIgKlFqN^CYHP9BGcs7d*~WEM;#GJ%R>f zIoxIPMcf4q-<`eRg14M{$Xs_;;1tN?VJ{FN7ZNhxkb>#EvvsgMBN7XkMU_o%P76kY zHa!j4J)}Lqc9l7%_*`ICIlje^0mEd+Wh(N=ALlHe0cNCjfluE_I54_%s4GUs%r=Kb zkU7Sr3azx|!!*>XI!r@oHB3W|TbAXNrlDd`d5A=Gv9y58urG)ILI(Mb4>IouU-hM2;gY?A3&-R*SlB8_`rOXI$* z%{5yZ)#AV?uUb_#iZ-gab<7bU}}p9KXqEdZrjx$Prn zmBaB$PwK9H7iS-|9!#sSOZ!1@QmVmq>YXedqW!N_0c=iC%9U_8ZpC0H&D`yyoc+BU zol|y%5R4Biw`jJaObCQx&>gL{X?WxXjd(Fgc!b>_UXfKD3?Li0FEij^?R zVS`&q=w-1IM+qgY`$m$k!2Qa{d}WB_%6WGuV?ZPp z=I8Z1$ij;tqY~<#|E~|-m+j8K$TnG9U0r1Y!?woOlwdN8gp4x3zFXv1Ff6zItV@_J zCkVWhDFJX?%eb5KYZ?V$>P3wk2EVu|!LZw2En8$kz0Uds>I}?rdnY#9WgL1R?73hMbI}xL~c*IHZ|4^yef!wcrhc zb4mh$(5FRf5JX8gBttY1kzx=OupR3F!W8z^IyBffLBlpX9HZV3r5A%Jrlr<=J1kOi zLV=Z|A#Z{ZJC`HorIQbW5kA?%TM;NqPtnMM%HO$1GAx9B9#%4`HOK7#G!B=^c}|)y z2{?f+FV{JJenqETQe2W1omPNJ4<^GSVq-)aML?ukq(_{8Wji#)#$o>-gK$2^$-RV` z1O+7ySMp*KQwyRt=K!m*AXi$H@5kXGH0j6|N0+tY($Wi-)U2W3ZE=Yz2Gh%m%epqH zpxG>m4wk@BGwqs7NVmG8-1j5O|u5nvqtQJ1{b9K8UX+tE^yR zO%rlfg%OLNv}%85w`XLV+_y@sD>O%2aqB|1iGI(bmudI3S5}Qdc*gTA@*SbhS3=Dm zHxu+^+mQIB;S~dCH~d14Y+J&t&SjZZy?mu3gBmD%Hk|(16qj=tS-wC#T_y0m*b+k$ zQg9`uNpFH{7X+SoweQTlgqWR(#Fr;L*`=yuKD3w3^k+sqJ%*lQ-OM5?AG1$uZsl;- zA!s*uWf&%r#5IsMxn{yhb)9GMnORBQeHn|x2}$1UIe61-;JVVo>DSU%I>~3;@3Fm2 zw=lz@Bk;(F6OVl8dE`Urkq`C~m3&_N5QvxT?AtP)7vZQ6qU=L}e=>cRJcYkEr>zZb zoz^~_2*~2A1^dK;eL^G7EZWLC2Hd zm9|wpn7S>{B_Z|%d3%)!K&m?Wv;t*GbNYF{Nh&~hH>p)Ax>TG z^gH>IV!9p;+H zj~<{&5Eer-H>8LzqhWoO0g`L!HrA@Qw4CxLW4K6Yo)P@PkdBB%*I+8Jce;@lnsX3? zO>rXKr z0z-~Nm|Y{EIx)pnZMno4^0#F;#jt9YiKT4 zQH&^0DC=P*;|-u?#Q@6*;)zJBJ5OJ?ICT5gz?dIlMXjDG_dpyP8&BB%4PSBXjW zMkeRDdJX<$>gZ9@i^m~VQ#x(wlnh7pdfhza3yyz(ZQd)JxT+h;z(~eWa(r&PY_a{7 zL*C=gUgNukwqGqGD`NTbQ)VSuuhNIa&CEcfR-A@loK{8EkV6Vchd`+wSpXI2j?6P z+bq#|9z=rOjs8~B?2i@9vYQ_BZG!yc3-A64eHTvCaT20YAEZBDM90;KDoxVZDC!Rb zRFHt{?_UlT)N(SE~&bPb%k>@ zI7RE&g4UM>7e|-R>7`*_DsFgtryyPH7H{emo`QMZtGw!aJO%Sx@AHh6Sba-ouQ||r z_(I|%C=>Gn2A`u|&eSVT$0#8KdI^{ggTjVdq%7n~62S`>a?U(7>f}fRfKX4M5-JaT zO;dG^5SE~7%yuz|dIR83eCpFnhr@9a^&l1@bZ!oT3kw?@$ElmxlV}VxH-Vo=0hTYd z(%~>omTaKR>#A;l{dOBFqYy8B>~}cdonJ7ETlk#R+4=e?eNJJjwJXk&GE;$0c4KtF7jkL!a&>iSt`74XcbHdnD5ME&b(eMR zUaDTC?0%%?vRmA~a$re7LrIX*P+;qknv4tOv>xyopK3`tcbjCi8JgNWo7y~|)jY3m zF7~8LTeEiU+;Hd{P;x>cuG2A1dTB_HF|vhR@(BgE%phYIs4@D+89N3Z2#@Y|S*DQL#R2m#zAO;#MR>%`CD*(r*GMcs3vb4hz+^!3y`%2{9* zdjlmCijk{E!3dmIjr}g6Oq88avUow`cm*Ua4avdoE01l1NeVm^LG>WE_RT1Ub#pQ) zXueq2%U-77(i-O}+X2h;As0~`i`im9D-L#FAu)jbx(qJZL;DLD3_E=5Jsrz+cPutt z1kIE<*xDu~XoOz&=&0b+3HwEq!;GKMUI32=beQ7MsrLzL)P#WlFYcUt-^s<+!X`Nc z&p!qGVlV-TgW${5fU%(h1fI6(sg8l7s}XI8wPd2lhF8NoIQ>dWNh*6`MLGMWX23NM zqEF3EZSiK*-E=Qq^n$D(?7rel!0K*_$ozK}myHH#a-IPl2KWwC4WW#Aolwo8>%!2F*QN=RU&V4v`SrKzAbKYRUcs60S z*{@~W;b}(cXSylgnM7b@{!9}%fK0hDb2${bU$(M`omJ=E882CRW9z89lV(Ju5=1HF z!ImJlV8)I}V)6Xd!g6YdyiM6MBky}lcL=42vl>9l?ox4|TXI8Vl6I4rkw4iXR_NIx z1&LE9yM?>eaxn3#3)V7M$%n^cF~X5xq?V(g@?Uu=TSAIo(w4q&-a6$o+Dp%AHbJKo z>G8mKW+nR2`$28lYY!}LQDB69?lD;}`vPcv9k2f5i~4YNT!r8;4WrYDhQHqkKM7?D zdU{WQo&4L499wHZXtu*bqf2-xz{UQR`aQp@fW}~O6$JQ~hJQrNQE%0=-fq-6wS-JK z6g@9n+|Hh-i}-;?k;GQ>IkYG>(=MsdL?f;Nzq+w5Xy%qFu*E9^hbhgIf(&tyfzZsN z{yfOEsQsw(n6;YrpTzh0(G`Aw>0OrSE9>CT<+_=C8Vc&9+qt1g0R9V z3eQV?6ifg3Qtt0+Ng2o8#&9;AFJjZV0s(CxWsh8XCQP~N7M@7p$7+`vlFIT1R7R}DGU6j1c4F$psFJ;`+^Tf6XprNmRM0JegNG9)WU*FEEWO;tIQ z8G$v*Dw2$oQSxMF>=EU^Z!mXMG>8cMG5YBJzOiKSKh&OEoa}>rM*!SFqY2>2Oygq5 zjB;^ufw<+bs-9Rs zhZNq_6lumz_0xe4f*c&S$GxJq)akx@xVGL#AWNQ&wFSor?f*L?RsHvkDW`R=a>@?5 z26jy>HR7qRmL(l;9o_$44OuPC`E}$n2bSx&IN*oQ{QY(2YpSJgaNnYrB4bTeN@wb# zL`6*6oO7xFP-II{qYSSe3!#^3p|&@Ya@~bVhb6;$VaA)XJ%y${(5rY4IQ!LKZ)-GqSUiR-zUPa;6+XR$!&M|m>sHReTD3zv?Z+`k)9GWc`ur%ugy@C14%3^?$ z9%GPe`r*2ymx&)FfPBt%Mkkf7qb>$Kqm${*!<-4N=bPVw8?Bft=1mr-#@t@Frthlx z*EF?^@Ciy|T94Cy?f5i^VrJ473>v62C;RQ{*qSBPoifp1&APdhNtUWaYNczKy4@QD zNf@Il%u?rQ%kvt?0@JQjaUA%jqkwjCWA4bMV}QAI=2ZVR;#dpua#K;sD6x+P=AB9v zr_8OqyA=0pEA5q3V>6r3i~VeG3^Am_BRH8FTp%xex3nhAM`7D)3I>VjbRzZKn#KXx z!utP$&>kcQk}gBFW8HXARI zC|u5&li7{I&fFpF1~u8Uu@yKDIP<@Q`@7%18OQf|-jbOTuOd`{CA4pyZ!6-txx963 z4;H($}I-afzvKnWGyK$z^A7rL=K45A!7Ep!OAAq{Y2Owt6se^_7c7id$nVFx z%HlHTQ_AR?lGz^pl!>;h)=q+)vKzq2oa&Gr3FS-hkY>?2tsVd4i)eDy_{Wz>o;K{+ zRn5%46%D{-u2ju|WHJt6gXRW#p!-7~*`HXEzOJbgU zwH0+8B9@K~;$&o*kd3waU2S5O=MvRwy5QGqnh{)5}(Xia}6f%}8OGCcy*(j_Y z+uJ(6s|-;W5_%MsDc$SUn8ImxjxXBPWp6#Bqd4f%+S0JGI#v~x?( z00a5m{_zLF>a3z~QZj+O(AO_m`qEPh@4=7%@r7Y^1yNC-vf#MeeEWrZDx32qayRgf6O62fD-#={W=8c z9_K9E>FN@T5@S?gLE6QA=M&RRAed4r^M?H5VaD7lrI@g3swCENt^gj%5rcRe?VQ0c z8;v_8Psdr0?ggV!l=R;m>~5FRj?{<2=9|TH=(D!(uQs=Qzq{7-_I!WOd+qzLy=T7v zjQ!K{eXN`D;toY<(DwcH7BukT%RMB6KUQDPTi!27CHduk%gccV z{0kd9^yqc`-7W(6Uif~yM z^*d`VZ{PR#8J%!o9pCS;->?Ax#IE1?{u}7^1^Z19FzHBIw1H9q(6sF^pOO9dKWb=;r;v`AT+TOV7`)5rC8_CMc_CGfj((+b};1!3~dXHtfGw^7K0lW zKmb_(s)l7UFu-cKVZj0cSSJkXTfeodNMwPA(xk~GiB$~Wp!j@dG0vY=j3NVox4bhp ziC6)!1lq4j)LXwf2O-x23}S=vB`l@Zqsj^M?E(u$(-5KcPwyF!>zii@);o$O)?~ifkxYv}Nc%d&Gu@aJ&y%Nr}g=Zniix`Uf zqU_vjYim!*3?YsV?S8UaR-iTHVu_`?c@)n=r7CZq|~+al$}~G;NV#v{AC_pUpPz zbz7RuQW#s4jGeExUiZO! zJLUsLmcxJ4T&sVS1X2H}dTMJ{nt+tdZ4E6o=sgf*ul-ih^v+toe}+66a|Er?B6wpr zT5H-wt=vlwVf_N|Z{Lv=$@f4H=*a1lodW#(CFD^Ba_kOr7z57I+H3*7_c*@Rs<&1i z{-enyP$7FzDOO^MIvZI6LXqIL-)Xcpy-vuA|6zlC>$mulRQR|UQ*90XCUTv*O>PCS zi5imfx2@N5eM@G7{z8od5Uh(4UEeH41-RW3&bpd{vuTc-eG_=ZZlR{QfI4Kb1D1s= zfWFuMYd{~u+GAsMiBZAxpWQsKWT{#!a<|uIqb0$Wh*py>h?teQ4ojk@-}+rEF%^9c z1aQ-!>D&y>NyeJ7&Ue=$i_frIUspKI)h-_UjgyvYTmg4fMiJHr#33@C*+0}q(y+;iH zJ>M_XfQGtOYYhzmH*hS5PUJYIOj8j64Mgj*?X7owf1M5WUdM}ed_UU3zdr70Tr`_v ztW8Ny0m3J80y;ZR^-Ar6>Nkb5oQet^*Wxx+iddLKM|>VKW+-W`*5MS^)G0D|TZ7`l z+FETwDt&BO(A*oM5D7_%k-NY+O0tNP7-uGRChwvh#wCJ4-g8xK`e0wfK7nAhslI4) z)%Zj*=Nku(Yt`Zp;GW(xT-3T4y}kjkgnn->tQ``6ZWieJt%0t&NqZQ;!Xez_(yreG zWqam<0c=`oAP!|w+jrDag$k0lF-TF@FE{QP0*@7EM}VlqBD2_P81ymyRoHjVe2W0A`#t6fm&Fed1^Y z@GG7ZOCwN_tpb zBldvTxCNMhfK3&xheE}Y=1sq4ZQ{TSkDA`3Gas8=1r6W(yB>Treatn8arRR{9L;%-5>l2{rarm6cxUk`_9K(mp(gwxiTT zOHDBC2)J{twVk+6SBDi%Ej$5Y*7}ctXqh?Qg6?NY1b$}fw7vBm-(TNBF5mI`&wRiC z3^&bZ-lu22{|W!do}n>%*hC|7(~F+@euO0-pLy$GNX9?zJ@caHz8^iulIM&|a9vxx zp$l7U2+Y8&7)nt>age%Oc`MBpwh(df8`N&-dWMV2hf+0AV#*XvGx(Vq+-{qn-y%C3 z>k{mUIsQ>}yr|{-kx;x4b8+nYLj$VxQ$dfUcWkZRnm2VR^%gBJaHI4~ z8)aYHwiH+bOP=Xn8G3f^yKm^bhFEXqyNBtwP7hkX|G<)l16?P(iSbIes>1Z%*QWZV zwyYK7x$fBlzxCN6ujjXMu^mvCcJk{h?c*GjdDPY@gG_Fk!0o#*YcaVrbq8-tazE00 zQt;K0O$_^UQ^Q6zF)X)@rfsDAOL2dw_cRUn%cgzRZ>mdx`$LYqym9FZOwiqE-X*jCFUV0tH!{edtJY!^R#{<(N_f1N8R;wowikA zUEjHW!EGV8ZjC>8n9UnOOZtN#O5A5%U8D#(w8ke)zh;}~l*RcL+o~Hx*O^VfR}n26 zC2N*olcZT5f9u5SqaTZa#kCW-gA>GfuG#-BTPk8E@!^*ySWKmlLZxg|V9H9*=e9TA z@%=HXlIJ_##WUZ(K(+8vU93=hJE=UR3liLO9I#s_Vne}}a38jccYj%l1X#hiqZzo_ zK~V8yQ*KZ|{w^L+J7{BigH%Vvz`>$r8*43diD^KFg~!3 zdv28-M$u7iIL}ZOdC-)+sFWaUQ)idrrInyH<-)lYw*|wBU8n=Q97m+Ji`UVqd140P zXGzi$H-S9@!GY&YTHXJsZC={G7R&?MDIa?4)y?tg2z>r8XegR8hWfxGLqlOR&Tc@O zoc^GWWR?-@hdT0&>PYKmTe<~R)1FIPdfMXBi*3A*T9QwGV;lKf=^uaq?Ztt%%9crw z|CpT~1UhrwasmFO>HELv9Nk4>rkab(1N~yrVdmnt_&M$utZT+{6~lM#1^3Xix-zaZ zl%`?S`8>aGgLZwp<@=5BxtF*6U*cwK>z~j4%YKu2B~7rRSO|06AL6ermF6{`7L_}O zTBVj!fsI`Y*`-s-J9gXA&2EfWe;{%mHnqJH*93h1m$sG8VY*Rvr2wsNYJ+~^w|0PJ zx9@k=MQxFZMfl^P-eBIF-PdRab6ssz-oqYPQyK5>q1k_ZOZl{ z7ir8kcvJdWpIqE!F7WpIT9d2x*H2C58U^>5&tba3Tvp!vfGVNIZ+s(b`u+pm+NTHt z%+qF))tc-Vu*v$eVA%3EcKPa=wBWP4@j!ny_;mp1fzCP#v3sp+=q%PWf!DTMXQ>pa zC12hb=*!!6=qz2Sv%H>DXVGqokPqA1MlM`l*%ZSPd}ULy>zJ+ARjSHcm!|S|s-of= zd(6jfoeGVMP?O~Hoq&@5iqold9g~tkM?(yW@8{}-_X6C~Y-yx0@3(!wuVW4mBBZzd zr{CI~^!&X8HUwky8`ydy1pKJ9|CvS*Aj5fSSFrvR_{Xz0(-+{F;1aGbL29GY;24v6o&lRY2I@CukHZ@2lHDn*>6>Fv~dG3aDJHzs=g!e{Y1{|&KfkD#F(apWB zUf+(h6+6=-nCtsVfn}t$-c=ob4r6yTs$YBV;VOB8m_AgPuId{Y)BG*bB``SBc7Nk` z=YOQp@0GS~iSxsW{hHa=j}DxIR2tJ!o!?Fa%*3j;YMfe_3c>`^PuB*qNY?< z7+#0`y;`MLrs_#8C3WOCcMx+s{n)mr0_k;#QdwCe6$oW6S9Cr^<-XE3)K$J%Zn~+% zy@en=YvUX5ddKhdnFkBsP`}L#tsT=`B1NHIGDEXDP->d;=9i{KJb$*FYs8|zI$AZ4rwHWedrG;I+ch7 zN{*QOhS=kkI1(oNkGgvlH(nz8{QTEKCmR8F$37aUPORaI7UH@X_|pf4v_!nHaO5(yBM(tu!fUQoz&kXoz_ZzhFMo zD;Ae>=~pe=Viu2gB`j~wyLco^bdk9g|>SX^rG?m~JS9@@!K7sBUjwK1T@ z7b3EUvaA}|>7Gnk!hsBF5e$R;tU-&xN*?uBEKpzmOk{q7Qc@~UD>O3$V2LZ5KNgrN zLcBkd+2}xi);x}qVkL}n$ilgj(92>ajuObs38*%bbR|hc`Y~T&fm3pAHh6+)(GN>z zYEx`K4m-u*H$8R?uWzITzXfzU5GSxRjEX`-3WEpc8dA^OFX)69V#IqW)1a^@CQXd~ z>luIT^XxkUbX~+eV4;E;92LM4!d^i(>>(8BQIc8M9!M_%2_;y?D=m7^v8=BJPXS0g zKGX9c3n4Ww6GeKDqg{ekfW?% z%&%nhW*;K%(C{&$fkA43a*y>neC(LeC-0V=QJ$CHKlw9x!YFyE7`l|C07;6Gr=CJE z!Qlzbw%`+nI)A!JPSP}{L4w~H2Rz;6>C?CVOs+~D=qU1NI{PZ|lY*iFQQ;3aNk)SZ z^KrA%cki*Bjj$WK8u6iQk^|oK@d53nS@@V$^{gR3d7?Mw=?SVEGm7v=<=Ch?eHbko zcklk0kh^BAGM1H058WB%WEg~$CB`*m3FHbhg+9PTm`3EQlW`OmtbH1XG!q$Aax>>I zKxCmq5k#LBtw9ha-B2Xa#;j)vz0e_qDTExYV~c$gI0QH4S&y-ph2yr{vm_iWt1M>M z5}A@2%nl~&r6I`&!H8m>T3{Y5OHWy1E{GU-?h%pAm!(uAH7>SmX&f$-^PDtal3GG9 zmY3_2Ous_Dq9G(|UeQSg*kC|GPsSxjQbm}Lc*YzsynkiUqjJ7Lb8-wptB!G+FJVrD z1SVr8FD5ayKGf972|8T(AkoBNUi|)V|V|Jcda#6R8k-S5-fee8Fi$K~Unn^8Cd!~~8} zhUpDQGzn?ao5+NboPU;$-ML zmO+J6+({fcKFB7tNnQn65e4z%YW3oYNGz#MpIla$l8FEH^r^kdh$Fp~p2R4JHe3}U zCPhR|`S;VOOlH|ImByt*7GIw}Rm+&OxTBiE?@?aAGbNLhlbxMQ9XoQAJbh|N0e3DV zE{~qfk<*9)X*9?I9i~S1BG5p>G+E+7&UB zYhwOHUz&)ho)TM9%*f28krr|b>2$hgMlP{bxafDK%7U1nl%vx|EUUCZ-}4>tduOF z^K{iDXCg(`)`8?Q3Bys!Fskvy0!;HHU!Bsdq+3fiEX6oyaF)nZ{&7>~RK*XNN7X`! z^S5b2kQm_Jl2c(O(*uR~20_xNxh2lHMgVb(Wfm`E-MI91Z4ON(gJ;GrX){K0b7?*C z$aoY61$~ZPV7`G(v9$;ZQj)(0cl`V_`Kg4qIN${_9APH%u(M=hZltOaiVjEqar049c45JC>H~?&FP7l#*iKfdeZVp zuv7&VT1fSLR;nh3NaIA_vZ<^zJE^oqNiQCURMFJIF{Sj(^?Kbr+Y4rRU!La@AkBtW zQBfRzOic3_(?%QWj=t_uc-Oe35OT}digx1~f|Nj7G@en?|l&~Furj@`H@=x|i*1!vSBjixH@c2$SU ziHZwa|G`ebhgUtdLi~NC+0dtTGzS=?b2!L~dl3DO1>~xJ8=~Dx;0;1uVQy#gWw==k z%Q@&;cO!&on$3Kzq*ekuyU3BNpV@DC9ml|2>q>6=bsPhD-7C2%*KrKsw_eGq)m?GH zRb1*@8SLDAadNcFw7Y=evea%&FW{7m5;CBdfZ;GGG=N2l)xZiL1u;2?WS3)F$3(lL zEdo4&O1RB(5kZxWJ=6heOmi`adYFR>Z;E>9a5zq)9;Ew)eoe%=*wG+yOuC6ZRmQk- z6YhBwVEIxj9S+lENrRU%Ym@fn*CJn`IJYb8{8L=k&qZp=6UZjXr|x820A+!hf?T9l z4)|h|Vip=JBM@_#IZB^Xm`Cl#P~ZM5kjQR=L*OS8JkZ_-V!cOy^NTnNCH zcv;sjVM-YSwKn>eKS!c+nU&f$%`y7Bj)do>)1;)7t9s_Ho>x@Q9o5C4F6FnY(nbnO z{s7XA#_0t)rb#ai=`n#USaQuIv`Iqiw;t@>t0CNzf8 zje-%)Jab1#D3b#x0RMu<@d`*M8iK!|$99S&1wj@;{UFx&l_&%OjQyFBZ<*YU&8wRfnkg zWpFpSr!AY{BK_@f|7FeCr$AHz-FZFmGL4D}VS5UA5g?BjDL5oBcQ<{pBa*yMinfJf z5F}((Z72X$sCLM&~b9(s-E2`Z>Gdi*+ z*{mj6sYyPoNp4Wr57SY>G?yOJT;R%3y3;~InnnF62}~J9Dnla}zqx3V~6nB{s*w{YP1XNsy z729!SE3MaQCm*qdtyuw+$J$>{rrF=bU%&V%C__yTyQwua%+Y{x2a^Y`fR!C-0bQ@f#r7~ z>ci%(5{gY}LNE5Sy>S6&l_S_64UUxC&6eJVDJk1l+xj`pqSJ|dJ|KlP%)CfYZ!KsO zf_+t2!5T;Ac7&s48%vaQMy2aX zZMjNPiQN!LHd6Z`FL&(%fA@l(N<99EXM&zd@@Agvol2F!TY9CCu07D#se&QQxsS=Z zv2L|Kj#vNjMSZwBb|E!P!{{`k;qTv$lLX;LN$&wLl7E*GVv9k^3utf&uVT2)bftdJ zuUwFL)@im>fZu5NM}!Nj>UovoL?&DK3`*Y0jn`HhuffPgrJ3ewg$i2nXSdT=xzHtM zuWOLJih5y6^Q0g{ToNF@vZy~OwAA*7IOkGSHm~x!sL~`VBwLRUY)C_2UY*W1=bQ6s zJAAsX2+AG3*JkRcDSn?OG1cln7GxE!H+W9xgIM~t+47;nWOgv&O2O~VliayVP285l=TCRolMwsCJt z2^^0Ffn+GLyCbL3Ph*+`1bIdV2L)z|78k@|#!C8#RKQK&6x~vx7kDs|Q*jhS*AbHv z^WKep+f-{8U9gOBU<_YD!=CW*;s{sHH(yv)zD&9Pfb;Q@eC(k=EPcXJb!ir?gb6SG z`RU`MC%lXu3v+lbz%Am^WeU0P%N09(mdTre-98nsjhO2e{u#hYUMU#a^_5>3^9%bcd4WF7PSLk<3B zHJDsq=X3y?sdSpp_DzkmQqd$rK5oufU;mXFDSIs`yl*rIB@2(2X`wa|l5!1%tx3}Y zahhiGNr>g~q&tBbZGYCodV zA?)H!UKxbJ@Wud4i?$OPF`odjG`GeH3Uok;rg?0WR|_XW4BLYCDj^xiu`D*8EC}?? z$Gq2Y4ZfaJupwyY@Z>eEp%w4-^nFjC_d$>gheJI|fIOk<+atU+*s|xpJ{so(eXAEo zA@xP0bsTHoIgulCYZres8st^y|F?Io-EG@U`u*%*!LaYCt(2lD`IcDgxDe&K-qxF@ z-fp(1X%kw4WyTW8l9cT<_5SufgBt-7yhyU0v^CF(Ly_QM1{mB1GY@_=6z)46y*;E@Py%&

z{~!#=CZZ3!d{k(hP#R|#2h@G|44;QFZrR)=fKBA`hG+Wu-c{!2!CpSO5Q^xvY6y7}=--u4q=rXy2 zs|{_%sI+-7#^-*3$Lsm{Rj%|7)p!9~R16>i$~s~88?}O*b3d2K@5@isZbl9&%&lP- zkg_lx<%_238E;htZN7^uCMmRqPDjPgA;YbozyM-wZ5bO-1{ zzdw@vZ`V6c$9cz->;bNo6zO&=(!En;(Z)e*cKEotV1HlE+o72%6qTsENEhBH;=0lc z$?9RpIGTDRys@||N4htkvF>ZCb%>9la!;`&B8N(ird_p zWv9t%c{q-|RIvKA|RrZlL|Or*>eC5j>l z+e5*aS^V+>oDe!SSd?mJP_gU%H3l_C+`7-b5!MhiKCVHJInnU3A$h4)@%f@pNQ4m% z4$}(~R+W8t75ZbAU^ERAh`LHN?{C*SPoJ(CPjws;ASwnYb%xVxi~}j$lw+rn&>Qho zQ$hBc@wCH>WQ<2ss5BXcP0f_s4-y^ZmZnNxu#)^Div24u#Z=}3P(%iMxF$wi2Kbd0 zdEV1$nnfUE4))5`qeY36B8BUzal*o*LLSfK|G|QGSume1QWA1vCzu*?!8E|2zz6}J zx?ctOZq(ez49XyDL&CeYP{5D@nIxz@3P7;tY=S*n zgtzc*TEXZkrXWis+=(XPn~!YcnC))^r*uLhk`t3WPtGZF!VH6v{7nFdk&vP;;;@Y$ zl9r|x4B0TYwG{X~dUZlvytaV?wp|P^$Nmh5^HjJPWTe=}IqYEwf)Wa3?Bgp;g5n@_ zLHIK33x`)?!M%*{tBIXUfXgcxXr#I?9dQ~c1l{VbR0wI4`+k%HYJfU$kHR4J{2&np zq1nNP+;lsNO*94;kDEQZmSiD3Y~@k%wI+Y2d*p?Y@Hb_->$bKj1$YIqmgH@|sP0a= zr2X59QnmD(w5tB)CLUT1EX+U}=%8ev)#={_zVXI*x-rw3OXY@s!O8h^&vlr)oVm?He00=G3Q^2uakYh zJm&c&mCDjR%w3nhCL_tv%B27u74SW3C8%&na=8~5D(aNBuwrRk${bW>6b9%1WFC9k zHd0Wq%Fl!}Y!)en8?Q zMJ9;>+YQY902?VDT?FAYoO}SIwig6=niy065*t7DzOjsNkB62qn9gw;hM=&o@!32% zYgP15Z0VrZLJiV{w1kjqfl;ce2$g<0#XJ^OB+y9*Hok#oMLtG|AD8s6Cl$!muD=#+Q zz9puRAE@!-8y`={r{r)B;!{H)?!lU_Pu4R2{L>JS{NS{Q{VUjdhphl?(TH@5^p}=# z-eQ&#*g7CxC~WbVGqWXaqrse*Zl#$luUzkP9s4At+GMs4m6&G|h{y99lHLd~6u_&| zBQP(ptw)9eSkQVf6s0(pb70$nL@b&Um)bNA3@$g>`k-n4{L^@HZtNLP$WMah%2TyU zm~*{=3y6(K2v6J&z=K{)m##0sKte58V}DGFh1<3Kl(WtDLe#=JFwSkje~_w*)DeJH zo|Cuar?F2T8+%k7JHct;lNP(_uicia(or*2I;vNtN*%hJYM*?{Y6s}ie65q^$ddIg zH!!GJW6!A8guDvys-H+z-;(N@nzDxsBdJ))%KKdH&cZRTNUo-{a10;M!m<7KEly6z zUS|tnAi}W$kqst;V>0*1quY#?;_UM+_kB(*?Q4z2Ri6;1xB52|oaRiluS=;+a7qp2 z{WTWrPL=Nm2HChljnj!`n3?RR1>qb}71aFwN=e*oMvyZnjn+ zr7d;s!z`oQ2i=!Y&lwStmYzjW$Kt2hwI18JkJIA?ehuxXZ<!eQhIJw{cRZIgCXQQ?m}K%d(V5N{4m z_JK5Q{=V7<9oCiRpaYD^@mSyEJnlICop+q_%Dm#f)2G*Ws;A$QGRy0HQ$ZRxipMRm z&teO#pv2`HVX}e>|JQIz03(H)DPOPHY@NsFXEI%&`tcV2Nu&O!QF)myE-0{K+;?suN6sc7(yW1$lY1P6-)S*(KZZ|hJLq}V{bgApOWR;=on?|HwG zpWmqLIx5TiyIBr55b<$$vf=JzBT#+04v8tx%er+EEV!}pmf(-_4;a|ONjS#S7O`bQ z3>z31A>BT=ff)z7LlNwZ$>{x^_IPJfW3%+)SqPN6QUu0j;DOwLB;D4Oy{S|*T& zQQ1LSV)DU9GntQC$%)f8!M~2@0R?P?H{!~R-Bt20B)c#XZw6EE6ml@ImJQi>9g zlEXBjB)Psj1{)*a=W71dhP-|s>%8d>%W9qutd%I2NsdoiYFwtw0i-`1#I zgTl>qwD;T^>c(S3m@%;XEGU6EBMVVDi(C-|v_B;n*`Tx@-TBCjLLG=3C?c zv+4A9^Zq|Oo4rT>pNF_Unrvt>_ckr_+@E4|&n(dc(=w^5{uqX-xo3*siMjWSY1^G7 z9_e)`P0U{{69!I5`m1HmsIn*Kp4l&42q9!8-yrn+VIQHLL44TnAECpeK0@xd)(JwM zbvZ!ja?n3Ur@I5|XXGAQvjIZau62mq8`pY?&?VfX!+!r2`Z0AaXNXYG?Y}~Yo37P! z5%S58_pX%;5b_7s5kg1ScL;rFxd_3#lbeAx9{?-I2pwBn1B6cL?Fl?&sobfO3MgL> z2s5*Rl<*k2(;>e-9QOPDm+0sS9)Fu{`CIGLDh^-c{r!*Gu%OAT%RD?hhdcD)J$6|BEtpgj}Cwdo!R9q|yiv z5qb?DoC}sbN{Uq|xn~5?p(G_|^Ww0`X8$ERE$}H5JS5Cyd{X6x`{}?62MC2wBJK%r z-CcxhNHm8`+~;l$eHXd0P$~C6n-%u&1B8Chgy;@vu^b9bJ3?+k3VWN$_Y*=t(b^?H zCj-GNV~WutREmGHvVMi!^l*WqzzE@d^<{quTS`R+g^GNMP6`F^GeSQL@!t=G&>rv? zu&T3>3i;rF;q*k!P==G#yvg5hS{$=d4Z$36_<6Mn(e%`eS!U;bVLL)XN6{MEC1BvRUTT3<5panHq z8L+PEGlFu2jLsIwh?KpOQJNbmbc#$ny@f{s)>kUo@nPLX$>5tlLcz(lu@X;aX2jD z6KEZ6c-bcV1LR!7n=Sy+FSsilxlO z7ua1Kf8$N?k)nYW^F7ewXfR;LFKS{JS+a63Li%2INPfeR!yDpx(Bw62Xz=?fwiq*P z!hbT9u&x~BEi|PZB#hywghmt3T%NFzV=hfB!4+#kLuw9dZ`HA?^U!69?$ANEAWIpz zh^QzTLq3N&thEA5&a?~!tqkeCne(iSEL!lJRbbX?JZ5XkU+*x>lHHxZEG5XuF7GX? zBe0CKtd`CSzOtO&!rp}%#Vk3GOF4}|KJ;Nl^b{Qe-xregyO~>TGcC(`#cH%{b*I=$ zO}itP*m^araL3Yq+}t5npZB8vu=k;`g+nwHl`TCyqx9g6JAh?rq})0mSl<2S&H$-6 z0i@#mkEYE-^4QcgjeBs9U9_!8&aqlGT-rDGP8!rwp0QQ>Ts+A%6-t}w#T}u=sA#mM zZnkXwUgu-mK<%6O!Pe;Qa&EB8_0V^#Bg?qJ7F0!bfYm9+;&3*(9xFHX6ct$K-?~V} z>775-T59Orsw<*}uyM2tU8se@nbEBvOIQ9_3E#IP>cGm>csskdajmg zA9q|WRMJY&I=r5U1FP0^^}tq6z^FJ*Z)va8va`9$xYJtYLIPm(LPr%k7PIyvi<` zi;O%gd1&efsD}QTx`AqWb8hW2qr}m2k+RDA?^))usEk$lqJUmrXU#7TaHZXf*7_-T zlck1B%>e;rNo&roU&w}=>&zh>Tv>3^E;`0-}e!# z@vm|J{FmMC&SU$3kV}jI2uDZaflqD38niz7JppptU?^tvjd5UX z@TiQ9&@yJ0acLQsJq4<-N1`*u8EP#9o*0)jh6MP49XDg&V3fh&a~<&2?@;W@DF~Yd zGyFXfgaTnEeC{>-y_~^&5;e0TXr3%+hJ7cGeP%0@xI8_`(=4SjKB}K+BY=3PdmkY` z%GJF8U!Ik^`QKKt|KHtw#Q%MeOB?^gtGqVgry}Nu>CGaMKP2u4oL_ea;Cs3|us*op zu?#=12G->ep^4rVwQ?3Kyv*{`Sj~%q|Ki_$EnMIJl_FVc9az`?cXzkc_TO`Mw;$vG z9^_(PwK*eFxfgfSBkLsJ-m)X;N(@-|CdI)x$+JYK9jf6QjL4c3<+8^s9%gr|KO}B3 z#wGvNGLsz(O!mfMF#TYFD+uGt*f-3<%kN*Fnte45CS|~r9J0NHd@5nsJPbx5o6MVD zGV*Zs`(I`qSkwNuy1j0`{ck&4kL~{%`Bb`oXr1&!>z@JOj>@8);9$Jr++fEtoSX48`0F_QFRv?m zKyGXp<2G_7VU-Bo`b;o(^zxn&8NFRu-oH684h0yqjJIc*zLuQEUR7pJS5s!(O2aAr z;`~aCBi&luvEyJA#xV$d9>y~-73ZD9bV?^v3F6okLKlpEqnj5Fs_A=r;`+Wp;nW0| z{;w^HsbU6gW-*m`=+*vsK%mpi8AEDP5F2w>$27qyMiqI<(pg-y#qu~t!k2ro!5Jp~8 z>;(a?0Kh-#03bma7Ky;MfHyY-1rjLx>KM(38HzzXberKNWJnGfP6T@fR!wqy2#_n) zcbJ29NCdOH3~!-;JHfC4+*sfb<=|Z!KrvLv!0)omb$1HpM5H1iRUtGmhz7#ME2!ZN zJLV2~dPVSF$U~Bec#{V%;;Ww1x+h*0c%gnbo^DTl5@Y2`q1JOVnEF;%8$H1CLE*m%%F7nbSjzGU(Q zmIs&V`9d-gOs$`h`@OjPf!>`0YFDm(xyS`Xkj`*uT@TS$!%7&91uU?uVqX|^@hVKT zbWuw&50*3T-aA^tg0|u}1sDWk6MmCNKM+q>MO`31p$eFRo@2;M&-5D=>Y(g%B3g;5 zn44&*f2!VAEZJys$*PPrpN}tlZVk?Tfr-5)`!Rkd{cs%pk7Z0aO(QQqf#-Zfi_SmU zkNysfJDH2R&azV$z_XlDNBFPPo6MjiOQTqGBuj^Ja9}u`U24bRgvyLGock*Kj7U>! zxlnVZ@S631Z@akv zzqPsfxc+~LYZ>&lwS_^#gkN}JVyBG*BIZhcv$2z0~qc?yx!;14T>J_i$8+g@3?v%>#ks$RblS1Xh>8=K$& z$Norak}HW)(+(S*dL~8tic?|y)L_o#9+_A1WE_?sreqMWhj0Ake&q#@BOJp#4%RNZ ztGq@N;m^RJwQN%IWt?*0t&#F=pxEwdF6uqc?gFoMS*0*JQ{^?UPICw zjc}5Lv2pDs0zv`00f1=ppo31SI}XnQCQRWu6^rJ?r8bQNgUd}8hBVEee;QBDjXmQD z`3X@FqI6m%%(-5`1;j=qgrA*Z0z&7BrS$a$44HINF!sj`2ZBx&{FJlJ{jrq7IWW#? z2u4;_q>dm2<(#}FKfz&&{J%%VAxGBdKWVWW;=fg*N=MC9>8M_nDs|{?s(tb)s~x6M z`C2E_>Lu%4ZeUQc#-35F33(OZRX>rczLnK=>1-5S)2yoZX*Nf=Bd@>mnQ`9nj~z10 z9Ox7uyh7AY|5L62>ZbhF-hL15&<9twMvxwa9=Akmq}Aw4*-WSXzSg^Lp_K*0ZsY5V z!m-5}p?z91>ijnoiCG#2_k@=QC(r!_)+vjgyINVTmLc6XI*9f2T=__4Lc&s;oHtfe zF!}S_jE1NtrH89=BAL(N0*9kDV0Pi-uK!L8zq?WZGI;8$e%Yo*2trBbhQ1P?zzt2e z){^V@PBlnKTXtBaeoQZYCytIR$*lAo$V&Cje^ufhsjQqoQ{Tu1*Z)gNQM3Nv-rVgy z#{YeyOY8rqRQXEIe-fnyB7qS)MAmGG&*Yiq})IBX@TR!Z|g%am3agp974i4sW zByTTpl-eEAleY3_6H$VkuRqGq4CCI3Kj;~QlL+OL38hz;F8F$hRLLOk_oY^gR8kdu z)BHH!!9el;iibn%*%19+?ftdz=|@X%!QH#7Rw=Sy<6REskX5pez~0sx*dbl%#*p@) zn+IFammhw!%x2yv^a%UHi=u;y8hE1|!*ifkW3}&8&6yYl2cfd6R(*f!*6WH_RbjDS z<2zNlR##o2?rEhil}OW;rUFPSWur8=#j!*y!7lG)?JbF0+E=K+%c_vo-dc?p(F(?> z9+7lWw0n6K%n~oQ2)@5k*Drz9s%VN(DpiUXMM`zBXKb0Zz`C@eI=m?Q>w9b6{Z+sI zBcNTYz<<`_zx1}ddHko|BmUpRTsg-gG7qhE{oiM1DCQZt#}4pfxP3Hn_VBi!cU>Ym zRGn@2BOZnE_&+EGuvA^)_{Afl=sjHx+y5%?pY`~^dH;{@uCw`w|MDQ0>i@Cg_N&$k z%Jk;;)2OMX`tqw zVSYWssWbVP;_MHeK5{_&|x<@4t0MHs`QK|BvaW8lSSK8}6* z7VOOMj?5b}yx=v)X_9*f_5=DBh8U?zNt&#Rr~*mldN%i`V<46$Bct7p;*cIxmr$T~ zo4hBz7B3a@WN!o~cfohDKa0I{Fb}8T68gc51A`J((vJaO1EBoF5#gB}6daC(O8GD@ z87BD*e(cdb5kZPPoB4#f(FGn|s&B?P!oe5^BOfO+P4xwo6^_W~N+OyUyk@!Dkc&6E zz(%``Z&KrZVx=nGbN@!mZ@Yc&-)Om}?#0cxhEITe6yr4YacrzVecE|SihrHm&&KP< z`V;qC^j}AZ-@iPfzsZ{qvnjl?A)k0ef41BHBnV^NCWmW+Qqtc>yB&mWax8f}4BGQx z28W*GF{DdzEGj#SF^pWmligv;(_s(oWpdV*SuGHpLF7B1fT!5WjnURNy`b+8}`1Ze?L!s(x!iVrcltKPVO1zj@{c~ zov)Gay%T@Z7Swh{Ck@@;_!560k9&64c4YakhP5JswV3wrzilc~OuZ{_7J6yNn@+>) zcGi+1pKXav?XD(Cw`2FVbO}k1(jLVhqBQi91T(U`_KvQMXll9>mvFj0d#hMZ-aHN4 zaBk!TPs0h5sVmXUN|bJw(oTmY>~`fcElJrvi^J;#$JrNkwA=5Qsy0kcF+LXfAlu%p z{0LMhVVWq{GWood=|h`ov|1W8m8@vRw?fd~GrMc=9=irYctQZ@=4^yyjs+>q93M0ZP}arP1YGhk?dxlsO8JMMwW29kdCt1rW^RI zbb32w$+A+~wY&CqNumUXETX>8?H#+PPn8vES5|U0k-|Q7Q=-~*b-tOkDz!Q{<;Se2 zWuj&|D|z+o-7GKuMsP(}CuErCWV%93I>a-MyP5cD(w4b$>|NfX|F>m7v$wL&ld*W>1>S^;1moP-n>O#!OdL%7K<-v$9q&01D)L+Rncdxi zyBGUCsb?lmd=z7{{WzVrz3H|0A!(m`WVoRRMrR)s$F3LILwfRkJE6yqzrcR9z5U`6 zk4D~Qfs~?wFQi}4SuOWbf=5wrd*`xS_>Od574NI*^51OGn#5jo@gc{e@Q%J+`x_b- V9 Date: Tue, 25 Jun 2024 13:14:32 +0100 Subject: [PATCH 25/75] feat: fix some lint issue after rebase from main --- app/components/Nav/App/index.js | 4 +- .../Views/AccountActions/AccountActions.tsx | 5 +- app/components/Views/LedgerConnect/index.tsx | 14 -- app/core/Ledger/Ledger.test.ts | 149 +++++++++--------- 4 files changed, 77 insertions(+), 95 deletions(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index a5e77f9e1a6..af0425e543a 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -463,6 +463,7 @@ const App = ({ userLoggedIn }) => { } } } + initSDKConnect() .then(() => { queueOfHandleDeeplinkFunctions.current.forEach((func) => func()); @@ -728,7 +729,7 @@ const App = ({ userLoggedIn }) => { const LedgerConnectFlow = () => ( - + ); @@ -739,7 +740,6 @@ const App = ({ userLoggedIn }) => { component={SelectHardwareWallet} options={SelectHardwareWallet.navigationOptions} /> - ); diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 31d2a11165c..0a7180bb930 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -61,7 +61,8 @@ const AccountActions = () => { const [blockingModalVisible, setBlockingModalVisible] = useState(false); const Controller = useMemo(() => { - const { KeyringController, PreferencesController } = Engine.context as any; + const { KeyringController, PreferencesController } = + Engine.context as never; return { KeyringController, PreferencesController }; }, []); @@ -183,7 +184,7 @@ const AccountActions = () => { const { keyrings } = Controller.KeyringController.state; const updatedKeyring = keyrings.find( - (keyring: { type: any }) => keyring.type === kr.type, + (keyring: { type: never }) => keyring.type === kr.type, ); // If there are no more accounts in the keyring, forget the device diff --git a/app/components/Views/LedgerConnect/index.tsx b/app/components/Views/LedgerConnect/index.tsx index a9d4806a70a..aed291d16fd 100644 --- a/app/components/Views/LedgerConnect/index.tsx +++ b/app/components/Views/LedgerConnect/index.tsx @@ -11,9 +11,6 @@ import { useNavigation } from '@react-navigation/native'; import { Device as NanoDevice } from '@ledgerhq/react-native-hw-transport-ble/lib/types'; import { useDispatch } from 'react-redux'; import { strings } from '../../../../locales/i18n'; -import Engine from '../../../core/Engine'; -import StyledButton from '../../../components/UI/StyledButton'; -import Text from '../../../components/Base/Text'; import { mockTheme, useAppThemeFromContext, @@ -115,9 +112,7 @@ interface LedgerConnectProps { } const LedgerConnect = ({ onConnectLedger }: LedgerConnectProps) => { - // const { AccountTrackerController } = Engine.context as any; const theme = useAppThemeFromContext() ?? mockTheme; - // const { trackEvent } = useMetrics(); const navigation = useNavigation(); const styles = useMemo(() => createStyles(theme), [theme]); const [selectedDevice, setSelectedDevice] = useState(null); @@ -143,16 +138,7 @@ const LedgerConnect = ({ onConnectLedger }: LedgerConnectProps) => { const connectLedger = () => { setLoading(true); - // trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { - // device_type: 'Ledger', - // }); ledgerLogicToRun(async () => { - // const account = await unlockLedgerDefaultAccount(true); - // await AccountTrackerController.syncBalanceWithAddresses([account]); - // trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { - // device_type: 'Ledger', - // }); - // navigation.dispatch(StackActions.pop(2)); onConnectLedger(); }); }; diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index d33505cfb00..798ebe8d4c0 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -1,16 +1,20 @@ +import LedgerKeyring from '@consensys/ledgerhq-metamask-keyring'; import { - closeRunningAppOnLedger, connectLedgerHardware, + openEthereumAppOnLedger, + closeRunningAppOnLedger, forgetLedger, - getDeviceId, - getLedgerAccountsByPage, ledgerSignTypedMessage, - openEthereumAppOnLedger, + unlockLedgerDefaultAccount, + getDeviceId, + withLedgerKeyring, } from './Ledger'; import Engine from '../../core/Engine'; -import { SignTypedDataVersion } from '@metamask/keyring-controller'; +import { + KeyringTypes, + SignTypedDataVersion, +} from '@metamask/keyring-controller'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; -import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; jest.mock('../../core/Engine', () => ({ context: { @@ -27,61 +31,20 @@ describe('Ledger core', () => { beforeEach(() => { jest.resetAllMocks(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + + // @ts-expect-error This is a partial mock, not completely identical + // TODO: Replace this with a type-safe mock ledgerKeyring = { addAccounts: jest.fn(), - bridge: { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - getAppNameAndVersion: jest - .fn() - .mockResolvedValue({ appName: 'appName' }), - updateTransportMethod: jest.fn(), - openEthApp: jest.fn(), - closeApps: jest.fn(), - }, - deserialize: jest.fn(), + setTransport: jest.fn(), + getAppAndVersion: jest.fn().mockResolvedValue({ appName: 'appName' }), + getDefaultAccount: jest.fn().mockResolvedValue('defaultAccount'), + openEthApp: jest.fn(), + quitApp: jest.fn(), forgetDevice: jest.fn(), - getDeviceId: jest.fn().mockReturnValue('deviceId'), - getFirstPage: jest.fn().mockResolvedValue([ - { - balance: '0', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', - index: 0, - }, - { - balance: '1', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', - index: 1, - }, - ]), - getNextPage: jest.fn().mockResolvedValue([ - { - balance: '4', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', - index: 4, - }, - { - balance: '5', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', - index: 5, - }, - ]), - getPreviousPage: jest.fn().mockResolvedValue([ - { - balance: '2', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', - index: 2, - }, - { - balance: '3', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', - index: 3, - }, - ]), - setDeviceId: jest.fn(), - setHdPath: jest.fn(), + deserialize: jest.fn(), + deviceId: 'deviceId', + getName: jest.fn().mockResolvedValue('name'), }; const mockKeyringController = MockEngine.context.KeyringController; @@ -96,12 +59,12 @@ describe('Ledger core', () => { const mockTransport = 'foo' as unknown as BleTransport; it('should call keyring.setTransport', async () => { await connectLedgerHardware(mockTransport, 'bar'); - expect(ledgerKeyring.bridge.updateTransportMethod).toHaveBeenCalled(); + expect(ledgerKeyring.setTransport).toHaveBeenCalled(); }); it('should call keyring.getAppAndVersion', async () => { await connectLedgerHardware(mockTransport, 'bar'); - expect(ledgerKeyring.bridge.getAppNameAndVersion).toHaveBeenCalled(); + expect(ledgerKeyring.getAppAndVersion).toHaveBeenCalled(); }); it('should return app name', async () => { @@ -110,17 +73,64 @@ describe('Ledger core', () => { }); }); + describe('withLedgerKeyring', () => { + it('runs the operation with a Ledger keyring', async () => { + const mockOperation = jest.fn(); + const mockLedgerKeyring = {}; + MockEngine.context.KeyringController.withKeyring.mockImplementation( + async ( + selector: Record, + operation: Parameters< + typeof MockEngine.context.KeyringController.withKeyring + >[1], + options?: Record, + ) => { + expect(selector).toStrictEqual({ type: KeyringTypes.ledger }); + expect(options).toStrictEqual({ createIfMissing: true }); + // @ts-expect-error This mock keyring is not type compatible + await operation(mockLedgerKeyring); + }, + ); + + await withLedgerKeyring(mockOperation); + + expect(mockOperation).toHaveBeenCalledWith(mockLedgerKeyring); + }); + }); + + describe('unlockLedgerDefaultAccount', () => { + it('should not call KeyringController.addNewAccountForKeyring if isAccountImportReq is false', async () => { + const account = await unlockLedgerDefaultAccount(false); + + expect(ledgerKeyring.getDefaultAccount).toHaveBeenCalled(); + expect(account).toEqual({ + address: 'defaultAccount', + balance: '0x0', + }); + }); + + it('should call KeyringController.addNewAccountForKeyring if isAccountImportReq is true', async () => { + const account = await unlockLedgerDefaultAccount(true); + + expect(ledgerKeyring.getDefaultAccount).toHaveBeenCalled(); + expect(account).toEqual({ + address: 'defaultAccount', + balance: '0x0', + }); + }); + }); + describe('openEthereumAppOnLedger', () => { it('should call keyring.openEthApp', async () => { await openEthereumAppOnLedger(); - expect(ledgerKeyring.bridge.openEthApp).toHaveBeenCalled(); + expect(ledgerKeyring.openEthApp).toHaveBeenCalled(); }); }); describe('closeRunningAppOnLedger', () => { it('should call keyring.quitApp', async () => { await closeRunningAppOnLedger(); - expect(ledgerKeyring.bridge.closeApps).toHaveBeenCalled(); + expect(ledgerKeyring.quitApp).toHaveBeenCalled(); }); }); @@ -138,21 +148,6 @@ describe('Ledger core', () => { }); }); - describe('getLedgerAccountsByPage', () => { - it('should call getNextPage on ledgerKeyring', async () => { - await getLedgerAccountsByPage(1); - expect(ledgerKeyring.getNextPage).toHaveBeenCalled(); - }); - it('should call getPreviousPage on ledgerKeyring', async () => { - await getLedgerAccountsByPage(-1); - expect(ledgerKeyring.getPreviousPage).toHaveBeenCalled(); - }); - it('should call getFirstPage on ledgerKeyring', async () => { - await getLedgerAccountsByPage(0); - expect(ledgerKeyring.getFirstPage).toHaveBeenCalled(); - }); - }); - describe('ledgerSignTypedMessage', () => { it('should call signTypedMessage from keyring controller and return correct signature', async () => { const expectedArg = { From 0158f8414966fded5ef54d1392d29e2a56156b85 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 25 Jun 2024 13:15:41 +0100 Subject: [PATCH 26/75] feat: fix some lint issue after rebase from main --- app/core/Ledger/Ledger.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 798ebe8d4c0..9cf5f23d051 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -29,11 +29,11 @@ const MockEngine = jest.mocked(Engine); describe('Ledger core', () => { let ledgerKeyring: LedgerKeyring; + // @ts-ignore beforeEach(() => { jest.resetAllMocks(); // @ts-expect-error This is a partial mock, not completely identical - // TODO: Replace this with a type-safe mock ledgerKeyring = { addAccounts: jest.fn(), setTransport: jest.fn(), From 50fd23d63cf87ad6d49f6c86c0bf17e1640ff3fc Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 02:11:19 +0100 Subject: [PATCH 27/75] feat: fix all unit tests after rebase --- .../Views/AccountActions/AccountActions.tsx | 8 +- app/components/Views/LedgerConnect/index.tsx | 2 + app/core/Ledger/Ledger.test.ts | 149 +++++++++--------- 3 files changed, 84 insertions(+), 75 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 0a7180bb930..bf45b4aac69 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -49,6 +49,7 @@ import { forgetLedger } from '../../../core/Ledger/Ledger'; import Engine from '../../../core/Engine'; import BlockingActionModal from '../../UI/BlockingActionModal'; import { useTheme } from '../../../util/theme'; +import { Hex } from '@metamask/utils'; const AccountActions = () => { const { colors } = useTheme(); @@ -61,8 +62,7 @@ const AccountActions = () => { const [blockingModalVisible, setBlockingModalVisible] = useState(false); const Controller = useMemo(() => { - const { KeyringController, PreferencesController } = - Engine.context as never; + const { KeyringController, PreferencesController } = Engine.context; return { KeyringController, PreferencesController }; }, []); @@ -170,7 +170,7 @@ const AccountActions = () => { let requestForgetDevice = false; // Remove account from KeyringController - await Controller.KeyringController.removeAccount(selectedAddress); + await Controller.KeyringController.removeAccount(selectedAddress as Hex); await removeAccountsFromPermissions([selectedAddress]); const newAccounts = await Controller.KeyringController.getAccounts(); trackEvent(MetaMetricsEvents.WALLET_REMOVED, { @@ -184,7 +184,7 @@ const AccountActions = () => { const { keyrings } = Controller.KeyringController.state; const updatedKeyring = keyrings.find( - (keyring: { type: never }) => keyring.type === kr.type, + (keyring) => keyring.type === kr.type, ); // If there are no more accounts in the keyring, forget the device diff --git a/app/components/Views/LedgerConnect/index.tsx b/app/components/Views/LedgerConnect/index.tsx index aed291d16fd..fa951fdfb51 100644 --- a/app/components/Views/LedgerConnect/index.tsx +++ b/app/components/Views/LedgerConnect/index.tsx @@ -11,6 +11,8 @@ import { useNavigation } from '@react-navigation/native'; import { Device as NanoDevice } from '@ledgerhq/react-native-hw-transport-ble/lib/types'; import { useDispatch } from 'react-redux'; import { strings } from '../../../../locales/i18n'; +import StyledButton from '../../../components/UI/StyledButton'; +import Text from '../../../components/Base/Text'; import { mockTheme, useAppThemeFromContext, diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 9cf5f23d051..56164af7667 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -1,20 +1,19 @@ -import LedgerKeyring from '@consensys/ledgerhq-metamask-keyring'; import { - connectLedgerHardware, - openEthereumAppOnLedger, closeRunningAppOnLedger, + connectLedgerHardware, forgetLedger, - ledgerSignTypedMessage, - unlockLedgerDefaultAccount, getDeviceId, - withLedgerKeyring, + getLedgerAccountsByPage, + ledgerSignTypedMessage, + openEthereumAppOnLedger, } from './Ledger'; import Engine from '../../core/Engine'; -import { - KeyringTypes, - SignTypedDataVersion, -} from '@metamask/keyring-controller'; +import { SignTypedDataVersion } from '@metamask/keyring-controller'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; +import { + LedgerKeyring, + LedgerMobileBridge, +} from '@metamask/eth-ledger-bridge-keyring'; jest.mock('../../core/Engine', () => ({ context: { @@ -28,23 +27,63 @@ const MockEngine = jest.mocked(Engine); describe('Ledger core', () => { let ledgerKeyring: LedgerKeyring; + let ledgerMobileBridge: LedgerMobileBridge; - // @ts-ignore beforeEach(() => { jest.resetAllMocks(); - // @ts-expect-error This is a partial mock, not completely identical + ledgerMobileBridge = { + getAppNameAndVersion: jest.fn().mockResolvedValue({ appName: 'appName' }), + updateTransportMethod: jest.fn(), + openEthApp: jest.fn(), + closeApps: jest.fn(), + }; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore ledgerKeyring = { addAccounts: jest.fn(), - setTransport: jest.fn(), - getAppAndVersion: jest.fn().mockResolvedValue({ appName: 'appName' }), - getDefaultAccount: jest.fn().mockResolvedValue('defaultAccount'), - openEthApp: jest.fn(), - quitApp: jest.fn(), - forgetDevice: jest.fn(), + bridge: ledgerMobileBridge, deserialize: jest.fn(), - deviceId: 'deviceId', - getName: jest.fn().mockResolvedValue('name'), + forgetDevice: jest.fn(), + getDeviceId: jest.fn().mockReturnValue('deviceId'), + getFirstPage: jest.fn().mockResolvedValue([ + { + balance: '0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', + index: 0, + }, + { + balance: '1', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', + index: 1, + }, + ]), + getNextPage: jest.fn().mockResolvedValue([ + { + balance: '4', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', + index: 4, + }, + { + balance: '5', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', + index: 5, + }, + ]), + getPreviousPage: jest.fn().mockResolvedValue([ + { + balance: '2', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', + index: 2, + }, + { + balance: '3', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', + index: 3, + }, + ]), + setDeviceId: jest.fn(), + setHdPath: jest.fn(), }; const mockKeyringController = MockEngine.context.KeyringController; @@ -59,12 +98,12 @@ describe('Ledger core', () => { const mockTransport = 'foo' as unknown as BleTransport; it('should call keyring.setTransport', async () => { await connectLedgerHardware(mockTransport, 'bar'); - expect(ledgerKeyring.setTransport).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.updateTransportMethod).toHaveBeenCalled(); }); it('should call keyring.getAppAndVersion', async () => { await connectLedgerHardware(mockTransport, 'bar'); - expect(ledgerKeyring.getAppAndVersion).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.getAppNameAndVersion).toHaveBeenCalled(); }); it('should return app name', async () => { @@ -73,64 +112,17 @@ describe('Ledger core', () => { }); }); - describe('withLedgerKeyring', () => { - it('runs the operation with a Ledger keyring', async () => { - const mockOperation = jest.fn(); - const mockLedgerKeyring = {}; - MockEngine.context.KeyringController.withKeyring.mockImplementation( - async ( - selector: Record, - operation: Parameters< - typeof MockEngine.context.KeyringController.withKeyring - >[1], - options?: Record, - ) => { - expect(selector).toStrictEqual({ type: KeyringTypes.ledger }); - expect(options).toStrictEqual({ createIfMissing: true }); - // @ts-expect-error This mock keyring is not type compatible - await operation(mockLedgerKeyring); - }, - ); - - await withLedgerKeyring(mockOperation); - - expect(mockOperation).toHaveBeenCalledWith(mockLedgerKeyring); - }); - }); - - describe('unlockLedgerDefaultAccount', () => { - it('should not call KeyringController.addNewAccountForKeyring if isAccountImportReq is false', async () => { - const account = await unlockLedgerDefaultAccount(false); - - expect(ledgerKeyring.getDefaultAccount).toHaveBeenCalled(); - expect(account).toEqual({ - address: 'defaultAccount', - balance: '0x0', - }); - }); - - it('should call KeyringController.addNewAccountForKeyring if isAccountImportReq is true', async () => { - const account = await unlockLedgerDefaultAccount(true); - - expect(ledgerKeyring.getDefaultAccount).toHaveBeenCalled(); - expect(account).toEqual({ - address: 'defaultAccount', - balance: '0x0', - }); - }); - }); - describe('openEthereumAppOnLedger', () => { it('should call keyring.openEthApp', async () => { await openEthereumAppOnLedger(); - expect(ledgerKeyring.openEthApp).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.openEthApp).toHaveBeenCalled(); }); }); describe('closeRunningAppOnLedger', () => { it('should call keyring.quitApp', async () => { await closeRunningAppOnLedger(); - expect(ledgerKeyring.quitApp).toHaveBeenCalled(); + expect(ledgerKeyring.bridge.closeApps).toHaveBeenCalled(); }); }); @@ -148,6 +140,21 @@ describe('Ledger core', () => { }); }); + describe('getLedgerAccountsByPage', () => { + it('should call getNextPage on ledgerKeyring', async () => { + await getLedgerAccountsByPage(1); + expect(ledgerKeyring.getNextPage).toHaveBeenCalled(); + }); + it('should call getNextPage on ledgerKeyring', async () => { + await getLedgerAccountsByPage(-1); + expect(ledgerKeyring.getPreviousPage).toHaveBeenCalled(); + }); + it('should call getFirstPage on ledgerKeyring', async () => { + await getLedgerAccountsByPage(0); + expect(ledgerKeyring.getFirstPage).toHaveBeenCalled(); + }); + }); + describe('ledgerSignTypedMessage', () => { it('should call signTypedMessage from keyring controller and return correct signature', async () => { const expectedArg = { From a3a656684df43615f9e5c9ab867a40a94409f80b Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 02:36:49 +0100 Subject: [PATCH 28/75] feat: fix all lint and tsc in broken pipeline --- .../AccountActions/AccountActions.styles.ts | 3 +- .../Views/LedgerAccountInfo/index.tsx | 11 +- .../Views/LedgerSelectAccount/index.tsx | 26 ++-- app/core/Ledger/Ledger.test.ts | 111 ++++++++---------- 4 files changed, 74 insertions(+), 77 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.styles.ts b/app/components/Views/AccountActions/AccountActions.styles.ts index 743f36063df..ae01e15c2f8 100644 --- a/app/components/Views/AccountActions/AccountActions.styles.ts +++ b/app/components/Views/AccountActions/AccountActions.styles.ts @@ -1,13 +1,14 @@ // Third party dependencies. import { StyleSheet } from 'react-native'; import { fontStyles } from '../../../styles/common'; +import { Colors } from '../../../util/theme/models'; /** * Style sheet function for AccountActions component. * * @returns StyleSheet object. */ -const styleSheet = (colors: any) => +const styleSheet = (colors: Colors) => StyleSheet.create({ actionsContainer: { alignItems: 'flex-start', diff --git a/app/components/Views/LedgerAccountInfo/index.tsx b/app/components/Views/LedgerAccountInfo/index.tsx index c0268e30dcf..fea181f36a4 100644 --- a/app/components/Views/LedgerAccountInfo/index.tsx +++ b/app/components/Views/LedgerAccountInfo/index.tsx @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-require-imports */ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable import/no-commonjs */ import { StackActions, useNavigation } from '@react-navigation/native'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { @@ -32,10 +29,10 @@ import ledgerDeviceDarkImage from '../../../images/ledger-device-dark.png'; import ledgerDeviceLightImage from '../../../images/ledger-device-light.png'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { useMetrics } from '../../../components/hooks/useMetrics'; +import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; +import { Colors } from '../../../util/theme/models'; -// TODO: Replace "any" with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const createStyles = (colors: any) => +const createStyles = (colors: Colors) => StyleSheet.create({ container: { flex: 1, @@ -115,7 +112,7 @@ const LedgerAccountInfo = () => { useEffect(() => { const getAccount = async () => { const accounts = await withLedgerKeyring( - async (keyring: any) => await keyring.getAccounts(), + async (keyring: LedgerKeyring) => await keyring.getAccounts(), ); setAccount(accounts[0]); diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index b1d9ae4416a..6ab3b456ee9 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -24,14 +24,20 @@ import { } from '../../../core/Ledger/Ledger'; import LedgerConnect from '../LedgerConnect'; import { setReloadAccounts } from '../../../actions/accounts'; -import { StackActions } from '@react-navigation/native'; +import { + NavigationProp, + ParamListBase, + StackActions, +} from '@react-navigation/native'; import { useDispatch } from 'react-redux'; +import { Colors } from '../../../util/theme/models'; +import { KeyringController } from '@metamask/keyring-controller'; interface ILedgerSelectAccountProps { - navigation: any; + navigation: NavigationProp; } -const createStyles = (colors: any) => +const createStyles = (colors: Colors) => StyleSheet.create({ container: { flex: 1, @@ -82,8 +88,10 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { ledgerDeviceDarkImage, ); - const KeyringController = useMemo(() => { - const { KeyringController: controller } = Engine.context as any; + const keyringController = useMemo(() => { + const { KeyringController: controller } = Engine.context as { + KeyringController: KeyringController; + }; return controller; }, []); @@ -102,10 +110,10 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { const [existingAccounts, setExistingAccounts] = useState([]); useEffect(() => { - KeyringController.getAccounts().then((value: string[]) => { + keyringController.getAccounts().then((value: string[]) => { setExistingAccounts(value); }); - }, [KeyringController]); + }, [keyringController]); const onConnectHardware = useCallback(async () => { trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { @@ -134,7 +142,7 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { try { for (const index of accountIndexes) { - await KeyringController.unlockLedgerWalletAccount(index); + await keyringController.unlockLedgerWalletAccount(index); } } catch (err) { Logger.log('Error: Connecting QR hardware wallet', err); @@ -147,7 +155,7 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { }); navigation.pop(2); }, - [KeyringController, navigation, trackEvent], + [keyringController, navigation, trackEvent], ); const onForget = useCallback(async () => { diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 56164af7667..0df437ddac2 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -10,10 +10,6 @@ import { import Engine from '../../core/Engine'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; -import { - LedgerKeyring, - LedgerMobileBridge, -} from '@metamask/eth-ledger-bridge-keyring'; jest.mock('../../core/Engine', () => ({ context: { @@ -26,65 +22,60 @@ jest.mock('../../core/Engine', () => ({ const MockEngine = jest.mocked(Engine); describe('Ledger core', () => { - let ledgerKeyring: LedgerKeyring; - let ledgerMobileBridge: LedgerMobileBridge; - - beforeEach(() => { - jest.resetAllMocks(); - - ledgerMobileBridge = { + const ledgerKeyring = { + addAccounts: jest.fn(), + bridge: { getAppNameAndVersion: jest.fn().mockResolvedValue({ appName: 'appName' }), updateTransportMethod: jest.fn(), openEthApp: jest.fn(), closeApps: jest.fn(), - }; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - ledgerKeyring = { - addAccounts: jest.fn(), - bridge: ledgerMobileBridge, - deserialize: jest.fn(), - forgetDevice: jest.fn(), - getDeviceId: jest.fn().mockReturnValue('deviceId'), - getFirstPage: jest.fn().mockResolvedValue([ - { - balance: '0', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', - index: 0, - }, - { - balance: '1', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', - index: 1, - }, - ]), - getNextPage: jest.fn().mockResolvedValue([ - { - balance: '4', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', - index: 4, - }, - { - balance: '5', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', - index: 5, - }, - ]), - getPreviousPage: jest.fn().mockResolvedValue([ - { - balance: '2', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', - index: 2, - }, - { - balance: '3', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', - index: 3, - }, - ]), - setDeviceId: jest.fn(), - setHdPath: jest.fn(), - }; + }, + deserialize: jest.fn(), + forgetDevice: jest.fn(), + getDeviceId: jest.fn().mockReturnValue('deviceId'), + getFirstPage: jest.fn().mockResolvedValue([ + { + balance: '0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', + index: 0, + }, + { + balance: '1', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', + index: 1, + }, + ]), + getNextPage: jest.fn().mockResolvedValue([ + { + balance: '4', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', + index: 4, + }, + { + balance: '5', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', + index: 5, + }, + ]), + getPreviousPage: jest.fn().mockResolvedValue([ + { + balance: '2', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', + index: 2, + }, + { + balance: '3', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', + index: 3, + }, + ]), + setDeviceId: jest.fn(), + setHdPath: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + const mockKeyringController = MockEngine.context.KeyringController; mockKeyringController.withKeyring.mockImplementation( @@ -145,7 +136,7 @@ describe('Ledger core', () => { await getLedgerAccountsByPage(1); expect(ledgerKeyring.getNextPage).toHaveBeenCalled(); }); - it('should call getNextPage on ledgerKeyring', async () => { + it('should call getPreviousPage on ledgerKeyring', async () => { await getLedgerAccountsByPage(-1); expect(ledgerKeyring.getPreviousPage).toHaveBeenCalled(); }); From c5b5c7d91df2da7511db0df09f86904ebd2ee7ce Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 03:02:23 +0100 Subject: [PATCH 29/75] feat: Fix ide complain about the navigationProp not contains pop method. Also fix the unit tests broken in Ledger.test.ts --- .../Views/LedgerSelectAccount/index.tsx | 11 +- app/core/Ledger/Ledger.test.ts | 122 +++++++++++------- 2 files changed, 76 insertions(+), 57 deletions(-) diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 6ab3b456ee9..7b29248aff5 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -24,17 +24,14 @@ import { } from '../../../core/Ledger/Ledger'; import LedgerConnect from '../LedgerConnect'; import { setReloadAccounts } from '../../../actions/accounts'; -import { - NavigationProp, - ParamListBase, - StackActions, -} from '@react-navigation/native'; +import { StackActions } from '@react-navigation/native'; import { useDispatch } from 'react-redux'; import { Colors } from '../../../util/theme/models'; import { KeyringController } from '@metamask/keyring-controller'; +import { StackNavigationProp } from '@react-navigation/stack'; interface ILedgerSelectAccountProps { - navigation: NavigationProp; + navigation: StackNavigationProp; } const createStyles = (colors: Colors) => @@ -69,7 +66,7 @@ const createStyles = (colors: Colors) => error: { ...fontStyles.normal, fontSize: 14, - color: colors.red, + color: colors.error, }, text: { color: colors.text.default, diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 0df437ddac2..3d567d935f0 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -21,63 +21,85 @@ jest.mock('../../core/Engine', () => ({ })); const MockEngine = jest.mocked(Engine); -describe('Ledger core', () => { - const ledgerKeyring = { - addAccounts: jest.fn(), - bridge: { - getAppNameAndVersion: jest.fn().mockResolvedValue({ appName: 'appName' }), - updateTransportMethod: jest.fn(), - openEthApp: jest.fn(), - closeApps: jest.fn(), - }, - deserialize: jest.fn(), - forgetDevice: jest.fn(), - getDeviceId: jest.fn().mockReturnValue('deviceId'), - getFirstPage: jest.fn().mockResolvedValue([ - { - balance: '0', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', - index: 0, - }, - { - balance: '1', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', - index: 1, - }, - ]), - getNextPage: jest.fn().mockResolvedValue([ - { - balance: '4', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', - index: 4, - }, - { - balance: '5', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', - index: 5, - }, - ]), - getPreviousPage: jest.fn().mockResolvedValue([ - { - balance: '2', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', - index: 2, - }, - { - balance: '3', - address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', - index: 3, - }, - ]), - setDeviceId: jest.fn(), - setHdPath: jest.fn(), +interface mockKeyringType { + addAccounts: jest.Mock; + bridge: { + getAppNameAndVersion: jest.Mock; + updateTransportMethod: jest.Mock; + openEthApp: jest.Mock; + closeApps: jest.Mock; }; + deserialize: jest.Mock; + forgetDevice: jest.Mock; + getDeviceId: jest.Mock; + getFirstPage: jest.Mock; + getNextPage: jest.Mock; + getPreviousPage: jest.Mock; + setDeviceId: jest.Mock; + setHdPath: jest.Mock; +} + +describe('Ledger core', () => { + let ledgerKeyring: mockKeyringType; beforeEach(() => { jest.resetAllMocks(); const mockKeyringController = MockEngine.context.KeyringController; + ledgerKeyring = { + addAccounts: jest.fn(), + bridge: { + getAppNameAndVersion: jest + .fn() + .mockResolvedValue({ appName: 'appName' }), + updateTransportMethod: jest.fn(), + openEthApp: jest.fn(), + closeApps: jest.fn(), + }, + deserialize: jest.fn(), + forgetDevice: jest.fn(), + getDeviceId: jest.fn().mockReturnValue('deviceId'), + getFirstPage: jest.fn().mockResolvedValue([ + { + balance: '0', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', + index: 0, + }, + { + balance: '1', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB3', + index: 1, + }, + ]), + getNextPage: jest.fn().mockResolvedValue([ + { + balance: '4', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB4', + index: 4, + }, + { + balance: '5', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB5', + index: 5, + }, + ]), + getPreviousPage: jest.fn().mockResolvedValue([ + { + balance: '2', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB6', + index: 2, + }, + { + balance: '3', + address: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB7', + index: 3, + }, + ]), + setDeviceId: jest.fn(), + setHdPath: jest.fn(), + }; + mockKeyringController.withKeyring.mockImplementation( // @ts-expect-error The Ledger keyring is not compatible with our keyring type yet (_selector, operation) => operation(ledgerKeyring), From d62e322a3e63c04e58d3eabd21b7745bda63ddf0 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 06:25:32 +0100 Subject: [PATCH 30/75] feat: Remove the redundant file `LedgerAccountInfo/index.tsx`, Refine the test description. --- .../LedgerConfirmationModal.test.tsx | 2 +- .../Views/LedgerAccountInfo/index.tsx | 203 ------------------ app/constants/navigation/Routes.ts | 1 - 3 files changed, 1 insertion(+), 205 deletions(-) delete mode 100644 app/components/Views/LedgerAccountInfo/index.tsx diff --git a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx index d21e716827e..18124741e6b 100644 --- a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx +++ b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx @@ -356,7 +356,7 @@ describe('LedgerConfirmationModal', () => { expect(onConfirmation).toHaveBeenCalled(); }); - it('logs LEDGER_HARDWARE_WALLET_ERROR thrown', async () => { + it('logs LEDGER_HARDWARE_WALLET_ERROR thrown when ledger error occurs', async () => { const onConfirmation = jest.fn(); const ledgerLogicToRun = jest.fn(); diff --git a/app/components/Views/LedgerAccountInfo/index.tsx b/app/components/Views/LedgerAccountInfo/index.tsx deleted file mode 100644 index fea181f36a4..00000000000 --- a/app/components/Views/LedgerAccountInfo/index.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import { StackActions, useNavigation } from '@react-navigation/native'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { - Image, - SafeAreaView, - StyleSheet, - TouchableOpacity, - View, -} from 'react-native'; -import { useDispatch, useSelector } from 'react-redux'; -import { strings } from '../../../../locales/i18n'; -import { setReloadAccounts } from '../../../actions/accounts'; -import { NO_RPC_BLOCK_EXPLORER, RPC } from '../../../constants/network'; -import Engine from '../../../core/Engine'; -import { forgetLedger, withLedgerKeyring } from '../../../core/Ledger/Ledger'; -import Device from '../../../util/device'; -import { getEtherscanAddressUrl } from '../../../util/etherscan'; -import { findBlockExplorerForRpc } from '../../../util/networks'; -import { - mockTheme, - useAppThemeFromContext, - useAssetFromTheme, -} from '../../../util/theme'; -import Text from '../../Base/Text'; -import { getNavigationOptionsTitle } from '../../UI/Navbar'; -import AccountDetails from '../../../components/UI/HardwareWallet/AccountDetails'; - -import ledgerDeviceDarkImage from '../../../images/ledger-device-dark.png'; -import ledgerDeviceLightImage from '../../../images/ledger-device-light.png'; -import { MetaMetricsEvents } from '../../../core/Analytics'; -import { useMetrics } from '../../../components/hooks/useMetrics'; -import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; -import { Colors } from '../../../util/theme/models'; - -const createStyles = (colors: Colors) => - StyleSheet.create({ - container: { - flex: 1, - backgroundColor: colors.background.default, - }, - imageWrapper: { - alignSelf: 'flex-start', - marginLeft: Device.getDeviceWidth() * 0.07, - }, - textWrapper: { - alignItems: 'center', - marginHorizontal: Device.getDeviceWidth() * 0.07, - }, - accountCountText: { - fontSize: 24, - }, - accountsContainer: { - flexDirection: 'row', - marginTop: 20, - marginLeft: Device.getDeviceWidth() * 0.02, - marginRight: Device.getDeviceWidth() * 0.07, - }, - textContainer: { - flex: 0.7, - }, - etherscanContainer: { - flex: 0.3, - justifyContent: 'center', - }, - etherscanImage: { - width: 30, - height: 30, - }, - forgetLedgerContainer: { - flex: 1, - flexDirection: 'column', - justifyContent: 'flex-end', - alignItems: 'center', - padding: 20, - }, - }); - -const LedgerAccountInfo = () => { - const dispatch = useDispatch(); - const navigation = useNavigation(); - const { trackEvent } = useMetrics(); - const [account, setAccount] = useState(''); - const [accountBalance, setAccountBalance] = useState('0'); - const { colors } = useAppThemeFromContext() ?? mockTheme; - const styles = useMemo(() => createStyles(colors), [colors]); - const ledgerThemedImage = useAssetFromTheme( - ledgerDeviceLightImage, - ledgerDeviceDarkImage, - ); - // TODO: Replace "any" with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { AccountTrackerController } = Engine.context as any; - const provider = useSelector( - // TODO: Replace "any" with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (state: any) => - state.engine.backgroundState.NetworkController.providerConfig, - ); - const frequentRpcList = useSelector( - // TODO: Replace "any" with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (state: any) => - state.engine.backgroundState.PreferencesController.frequentRpcList, - ); - - useEffect(() => { - navigation.setOptions( - getNavigationOptionsTitle('', navigation, true, colors), - ); - }, [navigation, colors]); - - useEffect(() => { - const getAccount = async () => { - const accounts = await withLedgerKeyring( - async (keyring: LedgerKeyring) => await keyring.getAccounts(), - ); - - setAccount(accounts[0]); - }; - - getAccount().then(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onForgetDevice = async () => { - await forgetLedger(); - dispatch(setReloadAccounts(true)); - trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { - device_type: 'Ledger', - }); - navigation.dispatch(StackActions.pop(2)); - }; - - const getEthAmountForAccount = async (ledgerAccount: string) => { - if (ledgerAccount) { - const ethValue = await AccountTrackerController.syncBalanceWithAddresses([ - ledgerAccount, - ]); - - setAccountBalance(ethValue[ledgerAccount]?.balance); - } - }; - - useEffect(() => { - if (account) { - getEthAmountForAccount(account); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [account]); - - const toBlockExplorer = useCallback( - (address: string) => { - const { type, rpcUrl } = provider; - let accountLink: string; - - if (type === RPC) { - const blockExplorer = - findBlockExplorerForRpc(rpcUrl, frequentRpcList) || - NO_RPC_BLOCK_EXPLORER; - accountLink = `${blockExplorer}/address/${address}`; - } else { - accountLink = getEtherscanAddressUrl(type, address); - } - - navigation.navigate('Webview', { - screen: 'SimpleWebview', - params: { - url: accountLink, - }, - }); - }, - [frequentRpcList, navigation, provider], - ); - - return ( - - - - - - - {strings('ledger.ledger_account_count')} - - - - - - - - {strings('ledger.forget_device')} - - - - ); -}; - -export default React.memo(LedgerAccountInfo); diff --git a/app/constants/navigation/Routes.ts b/app/constants/navigation/Routes.ts index c55a97440b9..84d367bc7d6 100644 --- a/app/constants/navigation/Routes.ts +++ b/app/constants/navigation/Routes.ts @@ -29,7 +29,6 @@ const Routes = { SELECT_DEVICE: 'SelectHardwareWallet', CONNECT_QR_DEVICE: 'ConnectQRHardwareFlow', CONNECT_LEDGER: 'ConnectLedgerFlow', - LEDGER_ACCOUNT: 'LedgerAccountInfo', LEDGER_CONNECT: 'LedgerConnect', }, LEDGER_MESSAGE_SIGN_MODAL: 'LedgerMessageSignModal', From f17359e19ebb6d9df01b628141dbe7da46f93ae1 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 06:39:06 +0100 Subject: [PATCH 31/75] feat: Revert the format of file. --- app/util/hardwareWallet/hardwareWallets/ledger.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/util/hardwareWallet/hardwareWallets/ledger.ts b/app/util/hardwareWallet/hardwareWallets/ledger.ts index 0b0c9f96a36..1e2c4a28930 100644 --- a/app/util/hardwareWallet/hardwareWallets/ledger.ts +++ b/app/util/hardwareWallet/hardwareWallets/ledger.ts @@ -1,7 +1,6 @@ import { createNavigationDetails } from '../../navigation/navUtils'; import Routes from '../../../constants/navigation/Routes'; import { getDeviceId } from '../../../core/Ledger/Ledger'; - export interface LedgerSignModelNavParams { // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -16,7 +15,6 @@ export interface LedgerSignModelNavParams { // eslint-disable-next-line @typescript-eslint/no-explicit-any type: any; } - export interface LedgerMessageSignModalParams extends LedgerSignModelNavParams { // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any From 5ca0447b392d9d96dfa2eea63b2d393028a7ad16 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 07:23:43 +0100 Subject: [PATCH 32/75] feat: revert to main version. --- ios/Podfile.lock | 2 +- package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7c833786bdf..f3c1ddfaecb 100755 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1060,7 +1060,7 @@ SPEC CHECKSUMS: lottie-react-native: 3e722c63015fdb9c27638b0a77969fc412067c18 MMKV: f902fb6719da13c2ab0965233d8963a59416f911 MMKVCore: d26e4d3edd5cb8588c2569222cbd8be4231374e9 - MultiplatformBleAdapter: ea8bac405ec200d0ca9de0f89afef6f06fb2abbc + MultiplatformBleAdapter: b1fddd0d499b96b607e00f0faa8e60648343dc1d nanopb: 438bc412db1928dac798aa6fd75726007be04262 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c Permission-BluetoothPeripheral: 247e379c9ecb4b1af2b87f73e4a15a00a5bc0c1f diff --git a/package.json b/package.json index 1458d52b690..ab47f8cef7e 100644 --- a/package.json +++ b/package.json @@ -158,9 +158,9 @@ "@metamask/key-tree": "^9.0.0", "@metamask/keyring-api": "^6.0.0", "@metamask/keyring-controller": "^16.0.0", + "@metamask/network-controller": "^18.1.0", "@metamask/logging-controller": "^3.0.0", "@metamask/message-signing-snap": "^0.3.3", - "@metamask/network-controller": "^18.1.0", "@metamask/permission-controller": "^9.0.0", "@metamask/phishing-controller": "^9.0.0", "@metamask/post-message-stream": "^8.0.0", @@ -177,11 +177,11 @@ "@metamask/sdk-communication-layer": "^0.20.2", "@metamask/signature-controller": "^16.0.0", "@metamask/slip44": "3.1.0", - "@metamask/smart-transactions-controller": "10.1.1", "@metamask/snaps-controllers": "^8.3.1", "@metamask/snaps-rpc-methods": "^9.0.0", "@metamask/snaps-sdk": "^4.2.0", "@metamask/snaps-utils": "^7.4.0", + "@metamask/smart-transactions-controller": "10.1.1", "@metamask/swappable-obj-proxy": "^2.1.0", "@metamask/swaps-controller": "^9.0.0", "@metamask/transaction-controller": "^13.0.0", @@ -367,7 +367,7 @@ "@metamask/mobile-provider": "^3.0.0", "@metamask/object-multiplex": "^1.1.0", "@metamask/providers": "^13.1.0", - "@metamask/test-dapp": "^8.9.0", + "@metamask/test-dapp": "^8.0.0", "@react-native-community/datetimepicker": "^7.5.0", "@react-native-community/eslint-config": "^2.0.0", "@react-native-community/slider": "^4.4.3", From 8d129da19980a54516ba4ddd1cfabb1fa083137c Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 07:34:24 +0100 Subject: [PATCH 33/75] feat: update yarn.lock --- yarn.lock | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5a9e8d23aaa..8919dfa8d4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4641,10 +4641,10 @@ human-standard-token-abi "^2.0.0" web3 "^4.2.2" -"@metamask/test-dapp@^8.9.0": - version "8.9.0" - resolved "https://registry.yarnpkg.com/@metamask/test-dapp/-/test-dapp-8.9.0.tgz#bac680e8f0007b3a11440f7e311674d6457d37ed" - integrity sha512-N/WfmdrzJm+xbpuqJsfMrlrAhiNDsllIpwt9gDDeEKDlQAfJnMtT9xvOvBJbXY7zgMdtGZuD+KY64jNKabbuVQ== +"@metamask/test-dapp@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@metamask/test-dapp/-/test-dapp-8.0.0.tgz#72f64b18a6157400ef3a3901c35819883524a048" + integrity sha512-yy1eZ9fZfJSEJIUjqcigBehFv6vdPGmDkVnzpBpEJqxiMPZGnbR5dypMfo0XsPdp5Lvmawx47Kamuy4Ocsy+iw== "@metamask/transaction-controller@^13.0.0": version "13.0.0" @@ -17762,7 +17762,16 @@ hastscript@^5.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" -hdkey@^2.0.1, hdkey@^2.1.0: +hdkey@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.0.1.tgz#0a211d0c510bfc44fa3ec9d44b13b634641cad74" + integrity sha512-c+tl9PHG9/XkGgG0tD7CJpRVaE0jfZizDNmnErUAKQ4EjQSOcOUcV3EN9ZEZS8pZ4usaeiiK0H7stzuzna8feA== + dependencies: + bs58check "^2.1.2" + safe-buffer "^5.1.1" + secp256k1 "^4.0.0" + +hdkey@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.1.0.tgz#755b30b73f54e93c31919c1b2f19205a8e57cb92" integrity sha512-i9Wzi0Dy49bNS4tXXeGeu0vIcn86xXdPQUpEYg+SO1YiO8HtomjmmRMaRyqL0r59QfcD4PfVbSF3qmsWFwAemA== From 646cec4ade5379c417e064c8f82508bb31d2cd66 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 07:43:09 +0100 Subject: [PATCH 34/75] feat: update yarn.lock --- yarn.lock | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8919dfa8d4f..6ea6b6b0df2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17762,16 +17762,7 @@ hastscript@^5.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" -hdkey@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.0.1.tgz#0a211d0c510bfc44fa3ec9d44b13b634641cad74" - integrity sha512-c+tl9PHG9/XkGgG0tD7CJpRVaE0jfZizDNmnErUAKQ4EjQSOcOUcV3EN9ZEZS8pZ4usaeiiK0H7stzuzna8feA== - dependencies: - bs58check "^2.1.2" - safe-buffer "^5.1.1" - secp256k1 "^4.0.0" - -hdkey@^2.1.0: +hdkey@^2.0.1, hdkey@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.1.0.tgz#755b30b73f54e93c31919c1b2f19205a8e57cb92" integrity sha512-i9Wzi0Dy49bNS4tXXeGeu0vIcn86xXdPQUpEYg+SO1YiO8HtomjmmRMaRyqL0r59QfcD4PfVbSF3qmsWFwAemA== From 449fab396733ebcc44add614461ab41bd636381a Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 26 Jun 2024 08:15:06 +0100 Subject: [PATCH 35/75] feat: Remove TODO in the file. --- app/components/Views/LedgerSelectAccount/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 7b29248aff5..c0a55734a1d 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -148,7 +148,6 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { device_type: 'Ledger', - //TODO Do we need to add address here? }); navigation.pop(2); }, From 1999fe84a3d3fe1393be53f329b9efbb2c50a771 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Thu, 27 Jun 2024 11:51:08 +0100 Subject: [PATCH 36/75] feat: Backup the application to test ios later. --- app/components/Views/LedgerConnect/index.test.tsx | 4 ---- app/components/Views/LedgerSelectAccount/index.tsx | 10 ++++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/components/Views/LedgerConnect/index.test.tsx b/app/components/Views/LedgerConnect/index.test.tsx index afc604fa44c..f2e6a5deb8c 100644 --- a/app/components/Views/LedgerConnect/index.test.tsx +++ b/app/components/Views/LedgerConnect/index.test.tsx @@ -30,10 +30,6 @@ jest.mock('../../../util/device', () => ({ getDeviceHeight: jest.fn(), })); -jest.mock('../../../core/Ledger/Ledger', () => ({ - unlockLedgerDefaultAccount: jest.fn(), -})); - jest.mock('../../../core/Engine', () => ({ context: { KeyringController: { diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index c0a55734a1d..141a1ce804f 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -24,16 +24,12 @@ import { } from '../../../core/Ledger/Ledger'; import LedgerConnect from '../LedgerConnect'; import { setReloadAccounts } from '../../../actions/accounts'; -import { StackActions } from '@react-navigation/native'; +import { StackActions, useNavigation } from '@react-navigation/native'; import { useDispatch } from 'react-redux'; import { Colors } from '../../../util/theme/models'; import { KeyringController } from '@metamask/keyring-controller'; import { StackNavigationProp } from '@react-navigation/stack'; -interface ILedgerSelectAccountProps { - navigation: StackNavigationProp; -} - const createStyles = (colors: Colors) => StyleSheet.create({ container: { @@ -75,7 +71,8 @@ const createStyles = (colors: Colors) => }, }); -const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { +const LedgerSelectAccount = () => { + const navigation = useNavigation>(); const dispatch = useDispatch(); const { colors } = useTheme(); const { trackEvent } = useMetrics(); @@ -148,6 +145,7 @@ const LedgerSelectAccount = ({ navigation }: ILedgerSelectAccountProps) => { trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { device_type: 'Ledger', + // TODO add added address here. }); navigation.pop(2); }, From 354db1117f1d815006badfa70261cc99ceb4f585 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Thu, 27 Jun 2024 11:52:03 +0100 Subject: [PATCH 37/75] feat: Backup the application to test ios later and remove the TODO comments. --- app/components/Views/LedgerSelectAccount/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 141a1ce804f..8dd204b4d1d 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -145,7 +145,6 @@ const LedgerSelectAccount = () => { trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { device_type: 'Ledger', - // TODO add added address here. }); navigation.pop(2); }, From c0a85a97976630d487d30f41074a52f168e08615 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Thu, 27 Jun 2024 17:24:25 +0100 Subject: [PATCH 38/75] feat: Fix a deviceDiscontinued issue in ios. --- app/core/Ledger/Ledger.ts | 7 ++-- ...mask+eth-ledger-bridge-keyring+4.0.0.patch | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 668d5e5a2e9..b39f0c1dd23 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -46,8 +46,11 @@ export const connectLedgerHardware = async ( ): Promise => { const appAndVersion = await withLedgerKeyring( async (keyring: LedgerKeyring) => { - keyring.setHdPath("m/44'/60'/0'/0"); - keyring.setDeviceId(deviceId); + if (keyring.getDeviceId() !== deviceId) { + keyring.setHdPath("m/44'/60'/0'/0"); + keyring.setDeviceId(deviceId); + } + const bridge = keyring.bridge as LedgerMobileBridge; await bridge.updateTransportMethod(transport); return await bridge.getAppNameAndVersion(); diff --git a/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch b/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch new file mode 100644 index 00000000000..9d0dadd9a2e --- /dev/null +++ b/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch @@ -0,0 +1,32 @@ +diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js +index b649ff2..d32b858 100644 +--- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js ++++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js +@@ -39,6 +39,12 @@ class LedgerTransportMiddleware { + */ + setTransport(transport) { + __classPrivateFieldSet(this, _LedgerTransportMiddleware_transport, transport, "f"); ++ /** ++ * PATCH to fix the issue with iOS ++ * As the transport is set, we need to recreate the eth app instance otherwise iOS will throw disconnect error when we reconnect the device. ++ * we should remove this PATCH once the new release of library is available. ++ */ ++ __classPrivateFieldSet(this, _LedgerTransportMiddleware_app, new ledger_hw_app_1.MetaMaskLedgerHwAppEth(this.getTransport()), "f"); + } + /** + * Method to retrieve the transport object. +@@ -58,9 +64,11 @@ class LedgerTransportMiddleware { + * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation. + */ + getEthApp() { +- if (!__classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f")) { +- __classPrivateFieldSet(this, _LedgerTransportMiddleware_app, new ledger_hw_app_1.MetaMaskLedgerHwAppEth(this.getTransport()), "f"); +- } ++ /** ++ * PATCH to fix the issue with iOS ++ * Move the recreate of ethApp code to setTransport method. ++ * we should remove this PATCH once the new release of library is available. ++ */ + return __classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f"); + } + } From ca5293d32b6949ceb7f16ec2299c101d397bd995 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Thu, 27 Jun 2024 17:30:35 +0100 Subject: [PATCH 39/75] feat: Fix the build issue after rebase. --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index ca6e1b5b20b..b1c4d61f797 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4850,10 +4850,10 @@ human-standard-token-abi "^2.0.0" web3 "^4.2.2" -"@metamask/test-dapp@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@metamask/test-dapp/-/test-dapp-8.0.0.tgz#72f64b18a6157400ef3a3901c35819883524a048" - integrity sha512-yy1eZ9fZfJSEJIUjqcigBehFv6vdPGmDkVnzpBpEJqxiMPZGnbR5dypMfo0XsPdp5Lvmawx47Kamuy4Ocsy+iw== +"@metamask/test-dapp@^8.9.0": + version "8.9.0" + resolved "https://registry.yarnpkg.com/@metamask/test-dapp/-/test-dapp-8.9.0.tgz#bac680e8f0007b3a11440f7e311674d6457d37ed" + integrity sha512-N/WfmdrzJm+xbpuqJsfMrlrAhiNDsllIpwt9gDDeEKDlQAfJnMtT9xvOvBJbXY7zgMdtGZuD+KY64jNKabbuVQ== "@metamask/transaction-controller@^13.0.0": version "13.0.0" From 915edbe5dd629eb2dccd5f53cfd0bf7343784b31 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 1 Jul 2024 04:08:49 +0100 Subject: [PATCH 40/75] feat: Improve code coverage for `LedgerSelectAccount` components. --- .../__snapshots__/index.test.tsx.snap | 1336 +++++++++++++++++ .../Views/LedgerSelectAccount/index.styles.ts | 47 + .../Views/LedgerSelectAccount/index.test.tsx | 55 + .../Views/LedgerSelectAccount/index.tsx | 59 +- 4 files changed, 1443 insertions(+), 54 deletions(-) create mode 100644 app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap create mode 100644 app/components/Views/LedgerSelectAccount/index.styles.ts create mode 100644 app/components/Views/LedgerSelectAccount/index.test.tsx diff --git a/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap b/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap new file mode 100644 index 00000000000..593be85eb0d --- /dev/null +++ b/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap @@ -0,0 +1,1336 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LedgerSelectAccount renders correctly to match snapshot 1`] = ` + + + + + Connect Ledger + + + + + + + + Looking for device + + + + + Please make sure your Ledger Nano X is: + + + 1. Unlock your Ledger Nano X + + + 2. Install and open the Ethereum app + + + 3. Enable Bluetooth + + + 5. Do not disturb must be turned off + + + How to install the Ethereum app on a Ledger device + + + + + + + +`; + +exports[`LedgerSelectAccount renders correctly to match snapshot when getAccounts return empty accounts 1`] = ` + + + + + Connect Ledger + + + + + + + + Looking for device + + + + + Please make sure your Ledger Nano X is: + + + 1. Unlock your Ledger Nano X + + + 2. Install and open the Ethereum app + + + 3. Enable Bluetooth + + + 5. Do not disturb must be turned off + + + How to install the Ethereum app on a Ledger device + + + + + + + +`; + +exports[`LedgerSelectAccount renders correctly to match snapshot when getAccounts return valid accounts 1`] = ` + + + + + Connect Ledger + + + + + + + + Looking for device + + + + + Please make sure your Ledger Nano X is: + + + 1. Unlock your Ledger Nano X + + + 2. Install and open the Ethereum app + + + 3. Enable Bluetooth + + + 5. Do not disturb must be turned off + + + How to install the Ethereum app on a Ledger device + + + + + + + +`; diff --git a/app/components/Views/LedgerSelectAccount/index.styles.ts b/app/components/Views/LedgerSelectAccount/index.styles.ts new file mode 100644 index 00000000000..c56ad035164 --- /dev/null +++ b/app/components/Views/LedgerSelectAccount/index.styles.ts @@ -0,0 +1,47 @@ +import { Colors } from '../../../util/theme/models'; +import { StyleSheet } from 'react-native'; +import Device from '../../../util/device'; +import { fontStyles } from '../../../styles/common'; + +const createStyles = (colors: Colors) => + StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'column', + alignItems: 'center', + }, + ledgerIcon: { + width: 60, + height: 60, + }, + header: { + marginTop: Device.isIphoneX() ? 50 : 20, + flexDirection: 'row', + width: '100%', + paddingHorizontal: 32, + alignItems: 'center', + }, + navbarRightButton: { + flexDirection: 'row', + justifyContent: 'flex-end', + height: 48, + width: 48, + flex: 1, + }, + closeIcon: { + fontSize: 28, + color: colors.text.default, + }, + error: { + ...fontStyles.normal, + fontSize: 14, + color: colors.error, + }, + text: { + color: colors.text.default, + fontSize: 14, + ...fontStyles.normal, + }, + }); + +export default createStyles; diff --git a/app/components/Views/LedgerSelectAccount/index.test.tsx b/app/components/Views/LedgerSelectAccount/index.test.tsx new file mode 100644 index 00000000000..e18cca9577a --- /dev/null +++ b/app/components/Views/LedgerSelectAccount/index.test.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import LedgerSelectAccount from './index'; +import renderWithProvider from '../../../util/test/renderWithProvider'; +import Engine from '../../../core/Engine'; + +const mockedNavigate = jest.fn(); + +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native'); + return { + ...actualNav, + useNavigation: () => ({ + navigate: mockedNavigate, + setOptions: jest.fn(), + }), + }; +}); + +jest.mock('../../../core/Engine', () => ({ + context: { + KeyringController: { + state: { + keyrings: [], + }, + getAccounts: jest.fn(), + }, + }, +})); +const MockEngine = jest.mocked(Engine); + +describe('LedgerSelectAccount', () => { + const mockKeyringController = MockEngine.context.KeyringController; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly to match snapshot', () => { + mockKeyringController.getAccounts.mockResolvedValue([]); + const wrapper = renderWithProvider(); + + expect(wrapper).toMatchSnapshot(); + }); + + it('renders correctly to match snapshot when getAccounts return valid accounts', () => { + mockKeyringController.getAccounts.mockResolvedValue([ + '0xd0a1e359811322d97991e03f863a0c30c2cf029c', + '0xa1e359811322d97991e03f863a0c30c2cf029cd', + ]); + const wrapper = renderWithProvider(); + + expect(wrapper).toMatchSnapshot(); + }); + +}); diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 8dd204b4d1d..89cc60dd8bd 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -1,18 +1,10 @@ -import React, { - Fragment, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; -import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { Image, Text, TouchableOpacity, View } from 'react-native'; import Engine from '../../../core/Engine'; import AccountSelector from '../../UI/HardwareWallet/AccountSelector'; import BlockingActionModal from '../../UI/BlockingActionModal'; import { strings } from '../../../../locales/i18n'; import { MetaMetricsEvents } from '../../../core/Analytics'; -import Device from '../../../util/device'; -import { fontStyles } from '../../../styles/common'; import Logger from '../../../util/Logger'; import { useAssetFromTheme, useTheme } from '../../../util/theme'; import useMetrics from '../../hooks/useMetrics/useMetrics'; @@ -26,50 +18,9 @@ import LedgerConnect from '../LedgerConnect'; import { setReloadAccounts } from '../../../actions/accounts'; import { StackActions, useNavigation } from '@react-navigation/native'; import { useDispatch } from 'react-redux'; -import { Colors } from '../../../util/theme/models'; import { KeyringController } from '@metamask/keyring-controller'; import { StackNavigationProp } from '@react-navigation/stack'; - -const createStyles = (colors: Colors) => - StyleSheet.create({ - container: { - flex: 1, - flexDirection: 'column', - alignItems: 'center', - }, - ledgerIcon: { - width: 60, - height: 60, - }, - header: { - marginTop: Device.isIphoneX() ? 50 : 20, - flexDirection: 'row', - width: '100%', - paddingHorizontal: 32, - alignItems: 'center', - }, - navbarRightButton: { - flexDirection: 'row', - justifyContent: 'flex-end', - height: 48, - width: 48, - flex: 1, - }, - closeIcon: { - fontSize: 28, - color: colors.text.default, - }, - error: { - ...fontStyles.normal, - fontSize: 14, - color: colors.error, - }, - text: { - color: colors.text.default, - fontSize: 14, - ...fontStyles.normal, - }, - }); +import createStyles from './index.styles'; const LedgerSelectAccount = () => { const navigation = useNavigation>(); @@ -186,7 +137,7 @@ const LedgerSelectAccount = () => { return accounts.length <= 0 ? ( ) : ( - + <> { {strings('connect_qr_hardware.please_wait')} - + ); }; From aced12921be1e6c3ea66f663c4593688105c8129 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 1 Jul 2024 04:30:47 +0100 Subject: [PATCH 41/75] feat: Fix the lint error in test --- .../__snapshots__/index.test.tsx.snap | 1336 ----------------- .../Views/LedgerSelectAccount/index.test.tsx | 1 - 2 files changed, 1337 deletions(-) delete mode 100644 app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap diff --git a/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap b/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 593be85eb0d..00000000000 --- a/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,1336 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LedgerSelectAccount renders correctly to match snapshot 1`] = ` - - - - - Connect Ledger - - - - - - - - Looking for device - - - - - Please make sure your Ledger Nano X is: - - - 1. Unlock your Ledger Nano X - - - 2. Install and open the Ethereum app - - - 3. Enable Bluetooth - - - 5. Do not disturb must be turned off - - - How to install the Ethereum app on a Ledger device - - - - - - - -`; - -exports[`LedgerSelectAccount renders correctly to match snapshot when getAccounts return empty accounts 1`] = ` - - - - - Connect Ledger - - - - - - - - Looking for device - - - - - Please make sure your Ledger Nano X is: - - - 1. Unlock your Ledger Nano X - - - 2. Install and open the Ethereum app - - - 3. Enable Bluetooth - - - 5. Do not disturb must be turned off - - - How to install the Ethereum app on a Ledger device - - - - - - - -`; - -exports[`LedgerSelectAccount renders correctly to match snapshot when getAccounts return valid accounts 1`] = ` - - - - - Connect Ledger - - - - - - - - Looking for device - - - - - Please make sure your Ledger Nano X is: - - - 1. Unlock your Ledger Nano X - - - 2. Install and open the Ethereum app - - - 3. Enable Bluetooth - - - 5. Do not disturb must be turned off - - - How to install the Ethereum app on a Ledger device - - - - - - - -`; diff --git a/app/components/Views/LedgerSelectAccount/index.test.tsx b/app/components/Views/LedgerSelectAccount/index.test.tsx index e18cca9577a..e777de406af 100644 --- a/app/components/Views/LedgerSelectAccount/index.test.tsx +++ b/app/components/Views/LedgerSelectAccount/index.test.tsx @@ -51,5 +51,4 @@ describe('LedgerSelectAccount', () => { expect(wrapper).toMatchSnapshot(); }); - }); From 98b01755e456184fd5d8887f955694d2515916e9 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 1 Jul 2024 04:30:55 +0100 Subject: [PATCH 42/75] feat: Fix the lint error in test --- .../__snapshots__/index.test.tsx.snap | 891 ++++++++++++++++++ 1 file changed, 891 insertions(+) create mode 100644 app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap diff --git a/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap b/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap new file mode 100644 index 00000000000..53a675fbf00 --- /dev/null +++ b/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap @@ -0,0 +1,891 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LedgerSelectAccount renders correctly to match snapshot 1`] = ` + + + + + Connect Ledger + + + + + + + + Looking for device + + + + + Please make sure your Ledger Nano X is: + + + 1. Unlock your Ledger Nano X + + + 2. Install and open the Ethereum app + + + 3. Enable Bluetooth + + + 5. Do not disturb must be turned off + + + How to install the Ethereum app on a Ledger device + + + + + + + +`; + +exports[`LedgerSelectAccount renders correctly to match snapshot when getAccounts return valid accounts 1`] = ` + + + + + Connect Ledger + + + + + + + + Looking for device + + + + + Please make sure your Ledger Nano X is: + + + 1. Unlock your Ledger Nano X + + + 2. Install and open the Ethereum app + + + 3. Enable Bluetooth + + + 5. Do not disturb must be turned off + + + How to install the Ethereum app on a Ledger device + + + + + + + +`; From 073260cecfcda427a635efc588716dbef85cd850 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 1 Jul 2024 04:48:59 +0100 Subject: [PATCH 43/75] feat: code the branch code in `ledger` core --- app/core/Ledger/Ledger.test.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 3d567d935f0..98949dce4f9 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -109,67 +109,73 @@ describe('Ledger core', () => { describe('connectLedgerHardware', () => { const mockTransport = 'foo' as unknown as BleTransport; - it('should call keyring.setTransport', async () => { + it('calls keyring.setTransport', async () => { await connectLedgerHardware(mockTransport, 'bar'); expect(ledgerKeyring.bridge.updateTransportMethod).toHaveBeenCalled(); }); - it('should call keyring.getAppAndVersion', async () => { + it('calls keyring.getAppAndVersion', async () => { await connectLedgerHardware(mockTransport, 'bar'); expect(ledgerKeyring.bridge.getAppNameAndVersion).toHaveBeenCalled(); }); - it('should return app name', async () => { + it('returns app name correctly', async () => { const value = await connectLedgerHardware(mockTransport, 'bar'); expect(value).toBe('appName'); }); + + it('calls keyring.setHdPath and keyring.setDeviceId if deviceId is different', async () => { + await connectLedgerHardware(mockTransport, 'bar'); + expect(ledgerKeyring.setHdPath).toHaveBeenCalled(); + expect(ledgerKeyring.setDeviceId).toHaveBeenCalled(); + }); }); describe('openEthereumAppOnLedger', () => { - it('should call keyring.openEthApp', async () => { + it('calls keyring.openEthApp', async () => { await openEthereumAppOnLedger(); expect(ledgerKeyring.bridge.openEthApp).toHaveBeenCalled(); }); }); describe('closeRunningAppOnLedger', () => { - it('should call keyring.quitApp', async () => { + it('calls keyring.quitApp', async () => { await closeRunningAppOnLedger(); expect(ledgerKeyring.bridge.closeApps).toHaveBeenCalled(); }); }); describe('forgetLedger', () => { - it('should call keyring.forgetDevice', async () => { + it('calls keyring.forgetDevice', async () => { await forgetLedger(); expect(ledgerKeyring.forgetDevice).toHaveBeenCalled(); }); }); describe('getDeviceId', () => { - it('should return deviceId', async () => { + it('returns deviceId', async () => { const value = await getDeviceId(); expect(value).toBe('deviceId'); }); }); describe('getLedgerAccountsByPage', () => { - it('should call getNextPage on ledgerKeyring', async () => { + it('calls ledgerKeyring.getNextPage on ledgerKeyring', async () => { await getLedgerAccountsByPage(1); expect(ledgerKeyring.getNextPage).toHaveBeenCalled(); }); - it('should call getPreviousPage on ledgerKeyring', async () => { + it('calls getPreviousPage on ledgerKeyring', async () => { await getLedgerAccountsByPage(-1); expect(ledgerKeyring.getPreviousPage).toHaveBeenCalled(); }); - it('should call getFirstPage on ledgerKeyring', async () => { + it('calls getFirstPage on ledgerKeyring', async () => { await getLedgerAccountsByPage(0); expect(ledgerKeyring.getFirstPage).toHaveBeenCalled(); }); }); describe('ledgerSignTypedMessage', () => { - it('should call signTypedMessage from keyring controller and return correct signature', async () => { + it('calls signTypedMessage from keyring controller and return correct signature', async () => { const expectedArg = { from: '0x49b6FFd1BD9d1c64EEf400a64a1e4bBC33E2CAB2', data: 'data', From feca7e0b6fbf2b5da0f319c3884eabcfc67e824f Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 2 Jul 2024 09:24:33 +0100 Subject: [PATCH 44/75] feat: add unit tests for AccountActions components --- .../AccountActions/AccountActions.test.tsx | 111 ++++++++++++++++-- 1 file changed, 104 insertions(+), 7 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index 7522b62fb68..eadb37143b8 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -1,7 +1,9 @@ import React from 'react'; import Share from 'react-native-share'; -import { fireEvent } from '@testing-library/react-native'; +import { Alert } from 'react-native'; + +import { fireEvent, waitFor } from '@testing-library/react-native'; import renderWithProvider from '../../../util/test/renderWithProvider'; @@ -10,6 +12,7 @@ import Routes from '../../../constants/navigation/Routes'; import AccountActions from './AccountActions'; import { EDIT_ACCOUNT, + REMOVE_HARDWARE_ACCOUNT, SHARE_ADDRESS, SHOW_PRIVATE_KEY, VIEW_ETHERSCAN, @@ -17,8 +20,8 @@ import { import initialBackgroundState from '../../../util/test/initial-background-state.json'; import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils'; import { toChecksumHexAddress } from '@metamask/controller-utils'; - -const mockEngine = Engine; +import { strings } from '../../../../locales/i18n'; +import { act } from '@testing-library/react-hooks'; const initialState = { swaps: { '0x1': { isLive: true }, hasOnboarded: false, isLive: true }, @@ -31,7 +34,7 @@ const initialState = { }; jest.mock('../../../core/Engine', () => ({ - init: () => mockEngine.init({}), + ...jest.requireActual('../../../core/Engine'), context: { PreferencesController: { selectedAddress: `0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756`, @@ -40,15 +43,24 @@ jest.mock('../../../core/Engine', () => ({ state: { keyrings: [ { - type: 'HD Key Tree', + type: 'Ledger Hardware', accounts: ['0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756'], }, + { + type: 'HD Key Tree', + accounts: ['0xa1e359811322d97991e03f863a0c30c2cf029cd'], + }, ], }, + getAccounts: jest.fn(), + removeAccount: jest.fn(), }, }, + setSelectedAddress: jest.fn(), })); +const mockEngine = jest.mocked(Engine); + const mockNavigate = jest.fn(); const mockGoBack = jest.fn(); @@ -80,10 +92,19 @@ jest.mock('react-native-share', () => ({ open: jest.fn(() => Promise.resolve()), })); +jest.mock('../../../core/Permissions', () => ({ + removeAccountsFromPermissions: jest.fn().mockResolvedValue(true), +})); + describe('AccountActions', () => { - afterEach(() => { - mockNavigate.mockClear(); + const mockKeyringController = mockEngine.context.KeyringController; + + beforeEach(() => { + jest.clearAllMocks(); + + jest.spyOn(Alert, 'alert'); }); + it('renders all actions', () => { const { getByTestId } = renderWithProvider(, { state: initialState, @@ -140,4 +161,80 @@ describe('AccountActions', () => { }, ); }); + + it('clicks edit account', () => { + const { getByTestId } = renderWithProvider(, { + state: initialState, + }); + + fireEvent.press(getByTestId(EDIT_ACCOUNT)); + + expect(mockNavigate).toHaveBeenCalledWith('EditAccountName'); + }); + + describe('clicks remove account', () => { + it('clicks cancel button after popup shows should do nothing', async () => { + const { getByTestId } = renderWithProvider(, { + state: initialState, + }); + + fireEvent.press(getByTestId(REMOVE_HARDWARE_ACCOUNT)); + + expect(Alert.alert).toHaveBeenCalled(); + + //Check Alert title and description match. + expect(Alert.alert.mock.calls[0][0]).toBe( + strings('accounts.remove_account_title'), + ); + expect(Alert.alert.mock.calls[0][1]).toBe( + strings('accounts.remove_account_alert_description'), + ); + + //Click cancel button + act(() => { + Alert.alert.mock.calls[0][2][0].onPress(); + }); + + //Check if removeAccount is not called + await waitFor(() => { + expect(mockKeyringController.removeAccount).not.toHaveBeenCalled(); + }); + }); + + it('clicks remove button after popup shows triggers remove process', async () => { + mockKeyringController.getAccounts.mockResolvedValue([ + '0xa1e359811322d97991e03f863a0c30c2cf029cd', + ]); + + const { getByTestId, getByText, queryByText } = renderWithProvider( + , + { + state: initialState, + }, + ); + + fireEvent.press(getByTestId(REMOVE_HARDWARE_ACCOUNT)); + + expect(Alert.alert).toHaveBeenCalled(); + + //Check Alert title and description match. + expect(Alert.alert.mock.calls[0][0]).toBe( + strings('accounts.remove_account_title'), + ); + expect(Alert.alert.mock.calls[0][1]).toBe( + strings('accounts.remove_account_alert_description'), + ); + + //Click remove button + await act(async () => { + Alert.alert.mock.calls[0][2][1].onPress(); + }); + + await waitFor(() => { + expect( + getByText(strings('connect_qr_hardware.please_wait')), + ).toBeDefined(); + }); + }); + }); }); From 59687e485dcc9778cb91a8d7c21ab2ea9abf8b48 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 2 Jul 2024 09:27:47 +0100 Subject: [PATCH 45/75] feat: Fix the lint issue in unit tests. --- app/components/Views/AccountActions/AccountActions.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index eadb37143b8..17094e1810a 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -206,7 +206,7 @@ describe('AccountActions', () => { '0xa1e359811322d97991e03f863a0c30c2cf029cd', ]); - const { getByTestId, getByText, queryByText } = renderWithProvider( + const { getByTestId, getByText } = renderWithProvider( , { state: initialState, From bdb32b079b014cc7ae24c6f72480bf53e090cd4e Mon Sep 17 00:00:00 2001 From: Xiaoming Wang <7315988+dawnseeker8@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:54:38 +0800 Subject: [PATCH 46/75] Apply suggestions from code review commit Gustavo Antunes's code suggestion. Co-authored-by: Gustavo Antunes <17601467+gantunesr@users.noreply.github.com> --- app/components/Nav/App/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index c6e0432fd3e..ef90fd8ac58 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -104,7 +104,7 @@ import { MetaMetrics } from '../../../core/Analytics'; import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics'; import generateDeviceAnalyticsMetaData from '../../../util/metrics/DeviceAnalyticsMetaData/generateDeviceAnalyticsMetaData'; import generateUserSettingsAnalyticsMetaData from '../../../util/metrics/UserSettingsAnalyticsMetaData/generateUserProfileAnalyticsMetaData'; -import Index from '../../Views/LedgerSelectAccount'; +import LedgerSelectAccount from '../../Views/LedgerSelectAccount'; import OnboardingSuccess from '../../Views/OnboardingSuccess'; import DefaultSettings from '../../Views/OnboardingSuccess/DefaultSettings'; import BasicFunctionalityModal from '../../UI/BasicFunctionality/BasicFunctionalityModal/BasicFunctionalityModal'; @@ -734,7 +734,7 @@ const App = ({ userLoggedIn }) => { const LedgerConnectFlow = () => ( - + ); From 2c6bceebac3bd55025356a886e020dc987e8822e Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 8 Jul 2024 17:07:44 +0800 Subject: [PATCH 47/75] feat: Rebase from main to resolve the conflict. --- .../AccountActions.constants.ts | 6 ----- .../AccountActions/AccountActions.test.tsx | 22 ++++++------------- .../Views/AccountActions/AccountActions.tsx | 20 +++++------------ .../Modals/AccountActionsModal.selectors.js | 1 + 4 files changed, 14 insertions(+), 35 deletions(-) delete mode 100644 app/components/Views/AccountActions/AccountActions.constants.ts diff --git a/app/components/Views/AccountActions/AccountActions.constants.ts b/app/components/Views/AccountActions/AccountActions.constants.ts deleted file mode 100644 index 12b68050ab6..00000000000 --- a/app/components/Views/AccountActions/AccountActions.constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Test IDs -export const EDIT_ACCOUNT = 'edit-account-action'; -export const VIEW_ETHERSCAN = 'view-etherscan-action'; -export const SHARE_ADDRESS = 'share-address-action'; -export const SHOW_PRIVATE_KEY = 'show-private-key-action'; -export const REMOVE_HARDWARE_ACCOUNT = 'remove-hardward-account-action'; diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index dd4835c58fa..8273facdce9 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -12,18 +12,6 @@ import Routes from '../../../constants/navigation/Routes'; import AccountActions from './AccountActions'; import { AccountActionsModalSelectorsIDs } from '../../../../e2e/selectors/Modals/AccountActionsModal.selectors'; import { backgroundState } from '../../../util/test/initial-root-state'; -import { - MOCK_ACCOUNTS_CONTROLLER_STATE, - MOCK_ADDRESS_2, -} from '../../../util/test/accountsControllerTestUtils'; -import { - EDIT_ACCOUNT, - REMOVE_HARDWARE_ACCOUNT, - SHARE_ADDRESS, - SHOW_PRIVATE_KEY, - VIEW_ETHERSCAN, -} from './AccountActions.constants'; -import initialBackgroundState from '../../../util/test/initial-background-state.json'; import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils'; import { toChecksumHexAddress } from '@metamask/controller-utils'; @@ -186,7 +174,7 @@ describe('AccountActions', () => { state: initialState, }); - fireEvent.press(getByTestId(EDIT_ACCOUNT)); + fireEvent.press(getByTestId(AccountActionsModalSelectorsIDs.EDIT_ACCOUNT)); expect(mockNavigate).toHaveBeenCalledWith('EditAccountName'); }); @@ -197,7 +185,9 @@ describe('AccountActions', () => { state: initialState, }); - fireEvent.press(getByTestId(REMOVE_HARDWARE_ACCOUNT)); + fireEvent.press( + getByTestId(AccountActionsModalSelectorsIDs.REMOVE_HARDWARE_ACCOUNT), + ); expect(Alert.alert).toHaveBeenCalled(); @@ -232,7 +222,9 @@ describe('AccountActions', () => { }, ); - fireEvent.press(getByTestId(REMOVE_HARDWARE_ACCOUNT)); + fireEvent.press( + getByTestId(AccountActionsModalSelectorsIDs.REMOVE_HARDWARE_ACCOUNT), + ); expect(Alert.alert).toHaveBeenCalled(); diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 66bebcc59dd..3d481c039cf 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -1,6 +1,6 @@ // Third party dependencies. -import React, { useMemo, useRef } from 'react'; -import { Platform, View } from 'react-native'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { Alert, View, Text } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { useDispatch, useSelector } from 'react-redux'; import Share from 'react-native-share'; @@ -34,14 +34,6 @@ import Logger from '../../../util/Logger'; import { protectWalletModalVisible } from '../../../actions/user'; import Routes from '../../../constants/navigation/Routes'; import { AccountActionsModalSelectorsIDs } from '../../../../e2e/selectors/Modals/AccountActionsModal.selectors'; -import generateTestId from '../../../../wdio/utils/generateTestId'; -import { - EDIT_ACCOUNT, - REMOVE_HARDWARE_ACCOUNT, - SHARE_ADDRESS, - SHOW_PRIVATE_KEY, - VIEW_ETHERSCAN, -} from './AccountActions.constants'; import { useMetrics } from '../../../components/hooks/useMetrics'; import { getKeyringByAddress, isHardwareAccount } from '../../../util/address'; import { removeAccountsFromPermissions } from '../../../core/Permissions'; @@ -175,7 +167,7 @@ const AccountActions = () => { await removeAccountsFromPermissions([selectedAddress]); const newAccounts = await Controller.KeyringController.getAccounts(); trackEvent(MetaMetricsEvents.WALLET_REMOVED, { - accountType: kr.type, + accountType: kr?.type, address: selectedAddress, }); @@ -185,7 +177,7 @@ const AccountActions = () => { const { keyrings } = Controller.KeyringController.state; const updatedKeyring = keyrings.find( - (keyring) => keyring.type === kr.type, + (keyring) => keyring.type === kr?.type, ); // If there are no more accounts in the keyring, forget the device @@ -197,7 +189,7 @@ const AccountActions = () => { requestForgetDevice = true; } if (requestForgetDevice) { - switch (kr.type) { + switch (kr?.type) { case ExtendedKeyringTypes.ledger: await forgetLedger(); trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { @@ -269,7 +261,7 @@ const AccountActions = () => { actionTitle={strings('accounts.remove_hardware_account')} iconName={IconName.Close} onPress={showRemoveHWAlert} - {...generateTestId(Platform, REMOVE_HARDWARE_ACCOUNT)} + testID={AccountActionsModalSelectorsIDs.REMOVE_HARDWARE_ACCOUNT} /> )} diff --git a/e2e/selectors/Modals/AccountActionsModal.selectors.js b/e2e/selectors/Modals/AccountActionsModal.selectors.js index 69127fb8436..f958cf5dd77 100644 --- a/e2e/selectors/Modals/AccountActionsModal.selectors.js +++ b/e2e/selectors/Modals/AccountActionsModal.selectors.js @@ -4,4 +4,5 @@ export const AccountActionsModalSelectorsIDs = { VIEW_ETHERSCAN: 'view-etherscan-action', SHARE_ADDRESS: 'share-address-action', SHOW_PRIVATE_KEY: 'show-private-key-action', + REMOVE_HARDWARE_ACCOUNT: 'remove-hardward-account-action', }; From 17c21ad239b035ac2b6d34141b5cac6899589eb7 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 8 Jul 2024 17:09:44 +0800 Subject: [PATCH 48/75] feat: fix lint error. --- app/components/Nav/App/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index ef90fd8ac58..8b4d1f294c4 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -734,7 +734,10 @@ const App = ({ userLoggedIn }) => { const LedgerConnectFlow = () => ( - + ); From 9982b72bced84ba45aa88d14d9b568329c3af6c3 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 8 Jul 2024 17:10:32 +0800 Subject: [PATCH 49/75] feat: fix deduplicate in yarn.lock --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 607ea5f8379..e4943bc60f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1872,7 +1872,7 @@ dependencies: "@ethereumjs/util" "^9.0.3" -"@ethereumjs/rlp@^4.0.1": +"@ethereumjs/rlp@^4.0.0", "@ethereumjs/rlp@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== From 4c545e1eddb92879c042d5d6b439de918db60663 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 8 Jul 2024 21:48:00 +0800 Subject: [PATCH 50/75] feat: Remove the patch to Keyring-controller 16 and move unlockLedgerWalletAccount to Ledger.ts --- .../Views/LedgerSelectAccount/index.tsx | 5 +- app/core/Ledger/Ledger.ts | 12 ++ .../@metamask+keyring-controller+16.0.0.patch | 140 ++++-------------- 3 files changed, 43 insertions(+), 114 deletions(-) diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 89cc60dd8bd..72de25aad09 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -13,6 +13,7 @@ import ledgerDeviceDarkImage from 'images/ledger-device-dark.png'; import { forgetLedger, getLedgerAccountsByPage, + unlockLedgerWalletAccount, } from '../../../core/Ledger/Ledger'; import LedgerConnect from '../LedgerConnect'; import { setReloadAccounts } from '../../../actions/accounts'; @@ -87,7 +88,7 @@ const LedgerSelectAccount = () => { try { for (const index of accountIndexes) { - await keyringController.unlockLedgerWalletAccount(index); + await unlockLedgerWalletAccount(index); } } catch (err) { Logger.log('Error: Connecting QR hardware wallet', err); @@ -99,7 +100,7 @@ const LedgerSelectAccount = () => { }); navigation.pop(2); }, - [keyringController, navigation, trackEvent], + [navigation, trackEvent], ); const onForget = useCallback(async () => { diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index b39f0c1dd23..3452412380c 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -153,3 +153,15 @@ export const ledgerSignTypedMessage = async ( version, ); }; + +/** + * Unlock Ledger Wallet Account with index, and add it that account to metamask + * + * @param index - The index of the account to unlock + */ +export const unlockLedgerWalletAccount = async (index: number) => { + await withLedgerKeyring(async (keyring: LedgerKeyring) => { + keyring.setAccountToUnlock(index); + await keyring.addAccounts(1); + }); +}; diff --git a/patches/@metamask+keyring-controller+16.0.0.patch b/patches/@metamask+keyring-controller+16.0.0.patch index eb9f4d5458e..9c653264f45 100644 --- a/patches/@metamask+keyring-controller+16.0.0.patch +++ b/patches/@metamask+keyring-controller+16.0.0.patch @@ -17,18 +17,18 @@ index 5b557f5..ff65f14 100644 --- a/node_modules/@metamask/keyring-controller/dist/KeyringController.js +++ b/node_modules/@metamask/keyring-controller/dist/KeyringController.js @@ -7,7 +7,7 @@ - - - + + + -var _chunk2GJQ6KDWjs = require('./chunk-2GJQ6KDW.js'); +var _chunkEGSSQX6Ljs = require('./chunk-EGSSQX6L.js'); require('./chunk-ZGV2QNCG.js'); - - + + @@ -18,5 +18,5 @@ require('./chunk-ZGV2QNCG.js'); - - - + + + -exports.AccountImportStrategy = _chunk2GJQ6KDWjs.AccountImportStrategy; exports.KeyringController = _chunk2GJQ6KDWjs.KeyringController; exports.KeyringTypes = _chunk2GJQ6KDWjs.KeyringTypes; exports.SignTypedDataVersion = _chunk2GJQ6KDWjs.SignTypedDataVersion; exports.default = _chunk2GJQ6KDWjs.KeyringController_default; exports.getDefaultKeyringState = _chunk2GJQ6KDWjs.getDefaultKeyringState; exports.isCustodyKeyring = _chunk2GJQ6KDWjs.isCustodyKeyring; exports.keyringBuilderFactory = _chunk2GJQ6KDWjs.keyringBuilderFactory; +exports.AccountImportStrategy = _chunkEGSSQX6Ljs.AccountImportStrategy; exports.KeyringController = _chunkEGSSQX6Ljs.KeyringController; exports.KeyringTypes = _chunkEGSSQX6Ljs.KeyringTypes; exports.SignTypedDataVersion = _chunkEGSSQX6Ljs.SignTypedDataVersion; exports.default = _chunkEGSSQX6Ljs.KeyringController_default; exports.getDefaultKeyringState = _chunkEGSSQX6Ljs.getDefaultKeyringState; exports.isCustodyKeyring = _chunkEGSSQX6Ljs.isCustodyKeyring; exports.keyringBuilderFactory = _chunkEGSSQX6Ljs.keyringBuilderFactory; //# sourceMappingURL=KeyringController.js.map @@ -47,46 +47,13 @@ index 6d75e83..f45940f 100644 export { AccountImportStrategy, diff --git a/node_modules/@metamask/keyring-controller/dist/chunk-2GJQ6KDW.js b/node_modules/@metamask/keyring-controller/dist/chunk-EGSSQX6L.js -similarity index 98% +similarity index 99% rename from node_modules/@metamask/keyring-controller/dist/chunk-2GJQ6KDW.js rename to node_modules/@metamask/keyring-controller/dist/chunk-EGSSQX6L.js -index 81fdecf..e3e959c 100644 +index 81fdecf..3844651 100644 --- a/node_modules/@metamask/keyring-controller/dist/chunk-2GJQ6KDW.js +++ b/node_modules/@metamask/keyring-controller/dist/chunk-EGSSQX6L.js -@@ -1022,6 +1022,32 @@ var KeyringController = class extends _basecontroller.BaseController { - await keyring.addAccounts(1); - }); - } -+ -+ /** -+ * Get Ledger keyring. -+ * -+ * @returns The Ledger Keyring if defined, otherwise undefined -+ */ -+ getLedgerKeyring() { -+ return this.getKeyringsByType("Ledger Hardware" /* qr */)[0]; -+ } -+ -+ /** -+ * This Patch is generated to support the multiple accounts support for ledger devices, -+ * The reason to create this is to access the #keyring private method is not possible from the outside. -+ * To add multiple accounts support for ledger Hardware wallet -+ * -+ * @param index - Index of the account to unlock -+ * @return {*} - Promise resolving to the current state -+ */ -+ async unlockLedgerWalletAccount(index) { -+ return _chunkZGV2QNCGjs.__privateMethod.call(void 0, this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ const keyring = this.getLedgerKeyring(); // the keyring will not be none since scan stage already create ledgerKeyring -+ keyring.setAccountToUnlock(index); -+ await keyring.addAccounts(1); -+ }); -+ } -+ - async getAccountKeyringType(account) { - const keyring = await this.getKeyringForAccount( - account -@@ -1113,9 +1139,7 @@ getKeyringBuilderForType_fn = function(type) { +@@ -1113,9 +1113,7 @@ getKeyringBuilderForType_fn = function(type) { _addQRKeyring = new WeakSet(); addQRKeyring_fn = async function() { _chunkZGV2QNCGjs.__privateMethod.call(void 0, this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); @@ -97,53 +64,22 @@ index 81fdecf..e3e959c 100644 }; _subscribeToQRKeyringEvents = new WeakSet(); subscribeToQRKeyringEvents_fn = function(qrKeyring) { -@@ -1465,4 +1489,4 @@ var KeyringController_default = KeyringController; - - +@@ -1465,4 +1463,4 @@ var KeyringController_default = KeyringController; + + exports.KeyringTypes = KeyringTypes; exports.isCustodyKeyring = isCustodyKeyring; exports.AccountImportStrategy = AccountImportStrategy; exports.SignTypedDataVersion = SignTypedDataVersion; exports.keyringBuilderFactory = keyringBuilderFactory; exports.getDefaultKeyringState = getDefaultKeyringState; exports.KeyringController = KeyringController; exports.KeyringController_default = KeyringController_default; -//# sourceMappingURL=chunk-2GJQ6KDW.js.map \ No newline at end of file +//# sourceMappingURL=chunk-EGSSQX6L.js.map +\ No newline at end of file diff --git a/node_modules/@metamask/keyring-controller/dist/chunk-USAGXPFN.mjs b/node_modules/@metamask/keyring-controller/dist/chunk-MQVFNKTH.mjs -similarity index 98% +similarity index 99% rename from node_modules/@metamask/keyring-controller/dist/chunk-USAGXPFN.mjs rename to node_modules/@metamask/keyring-controller/dist/chunk-MQVFNKTH.mjs -index 0cf6255..5147bd0 100644 +index 0cf6255..6b769d8 100644 --- a/node_modules/@metamask/keyring-controller/dist/chunk-USAGXPFN.mjs +++ b/node_modules/@metamask/keyring-controller/dist/chunk-MQVFNKTH.mjs -@@ -1022,6 +1022,31 @@ var KeyringController = class extends BaseController { - await keyring.addAccounts(1); - }); - } -+ -+ /** -+ * Get Ledger Hardware keyring. -+ * -+ * @returns The Ledger Keyring if defined, otherwise undefined -+ */ -+ getLedgerKeyring() { -+ return this.getKeyringsByType("Ledger Hardware" /* qr */)[0]; -+ } -+ /** -+ * This Patch is generated to support the multiple accounts support for ledger devices, -+ * The reason to create this is to access the #keyring private method is not possible from the outside. -+ * To add multiple accounts support for ledger Hardware wallet -+ * -+ * @param index - Index of the account to unlock -+ * @param keyring - Ledger Keyring instance to unlock -+ * @return {*} - Promise resolving to the current state -+ */ -+ async unlockLedgerWalletAccount(index, keyring) { -+ return __privateMethod(this, _persistOrRollback, persistOrRollback_fn).call(this, async () => { -+ keyring.setAccountToUnlock(index); -+ await keyring.addAccounts(1); -+ }); -+ } -+ - async getAccountKeyringType(account) { - const keyring = await this.getKeyringForAccount( - account -@@ -1113,9 +1138,7 @@ getKeyringBuilderForType_fn = function(type) { +@@ -1113,9 +1113,7 @@ getKeyringBuilderForType_fn = function(type) { _addQRKeyring = new WeakSet(); addQRKeyring_fn = async function() { __privateMethod(this, _assertControllerMutexIsLocked, assertControllerMutexIsLocked_fn).call(this); @@ -154,30 +90,31 @@ index 0cf6255..5147bd0 100644 }; _subscribeToQRKeyringEvents = new WeakSet(); subscribeToQRKeyringEvents_fn = function(qrKeyring) { -@@ -1465,4 +1488,4 @@ export { +@@ -1465,4 +1463,4 @@ export { KeyringController, KeyringController_default }; -//# sourceMappingURL=chunk-USAGXPFN.mjs.map \ No newline at end of file +//# sourceMappingURL=chunk-MQVFNKTH.mjs.map +\ No newline at end of file diff --git a/node_modules/@metamask/keyring-controller/dist/index.js b/node_modules/@metamask/keyring-controller/dist/index.js index 35b2a0f..655541f 100644 --- a/node_modules/@metamask/keyring-controller/dist/index.js +++ b/node_modules/@metamask/keyring-controller/dist/index.js @@ -6,7 +6,7 @@ - - - + + + -var _chunk2GJQ6KDWjs = require('./chunk-2GJQ6KDW.js'); +var _chunkEGSSQX6Ljs = require('./chunk-EGSSQX6L.js'); require('./chunk-ZGV2QNCG.js'); - - + + @@ -16,5 +16,5 @@ require('./chunk-ZGV2QNCG.js'); - - - + + + -exports.AccountImportStrategy = _chunk2GJQ6KDWjs.AccountImportStrategy; exports.KeyringController = _chunk2GJQ6KDWjs.KeyringController; exports.KeyringTypes = _chunk2GJQ6KDWjs.KeyringTypes; exports.SignTypedDataVersion = _chunk2GJQ6KDWjs.SignTypedDataVersion; exports.getDefaultKeyringState = _chunk2GJQ6KDWjs.getDefaultKeyringState; exports.isCustodyKeyring = _chunk2GJQ6KDWjs.isCustodyKeyring; exports.keyringBuilderFactory = _chunk2GJQ6KDWjs.keyringBuilderFactory; +exports.AccountImportStrategy = _chunkEGSSQX6Ljs.AccountImportStrategy; exports.KeyringController = _chunkEGSSQX6Ljs.KeyringController; exports.KeyringTypes = _chunkEGSSQX6Ljs.KeyringTypes; exports.SignTypedDataVersion = _chunkEGSSQX6Ljs.SignTypedDataVersion; exports.getDefaultKeyringState = _chunkEGSSQX6Ljs.getDefaultKeyringState; exports.isCustodyKeyring = _chunkEGSSQX6Ljs.isCustodyKeyring; exports.keyringBuilderFactory = _chunkEGSSQX6Ljs.keyringBuilderFactory; //# sourceMappingURL=index.js.map @@ -195,24 +132,3 @@ index ba28e4a..443b073 100644 import "./chunk-4OE2G6WW.mjs"; export { AccountImportStrategy, -diff --git a/node_modules/@metamask/keyring-controller/dist/types/KeyringController.d.ts b/node_modules/@metamask/keyring-controller/dist/types/KeyringController.d.ts -index b976e18..16d882f 100644 ---- a/node_modules/@metamask/keyring-controller/dist/types/KeyringController.d.ts -+++ b/node_modules/@metamask/keyring-controller/dist/types/KeyringController.d.ts -@@ -7,6 +7,7 @@ import type { EthBaseTransaction, EthBaseUserOperation, EthKeyring, EthUserOpera - import type { PersonalMessageParams, TypedMessageParams } from '@metamask/message-manager'; - import type { Eip1024EncryptedData, Hex, Json, KeyringClass } from '@metamask/utils'; - import type { Patch } from 'immer'; -+import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; - declare const name = "KeyringController"; - /** - * Available keyring types -@@ -602,6 +603,8 @@ export declare class KeyringController extends BaseController; - unlockQRHardwareWalletAccount(index: number): Promise; -+ getLedgerKeyring(): LedgerKeyring | undefined; -+ unlockLedgerWalletAccount(index: number): Promise; - getAccountKeyringType(account: string): Promise; - forgetQRDevice(): Promise<{ - removedAccounts: string[]; From 2276ce7f8e15ba3504c3a857618f409949931719 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 9 Jul 2024 00:27:38 +0800 Subject: [PATCH 51/75] feat: improve code coverage to Ledger.ts to 100%, remove the whitespace difference in patch file. --- app/core/Ledger/Ledger.test.ts | 17 +++++++++ .../@metamask+keyring-controller+16.0.0.patch | 36 +++++++++---------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 98949dce4f9..c2dc2071b26 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -6,6 +6,7 @@ import { getLedgerAccountsByPage, ledgerSignTypedMessage, openEthereumAppOnLedger, + unlockLedgerWalletAccount, } from './Ledger'; import Engine from '../../core/Engine'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; @@ -37,6 +38,7 @@ interface mockKeyringType { getPreviousPage: jest.Mock; setDeviceId: jest.Mock; setHdPath: jest.Mock; + setAccountToUnlock: jest.Mock; } describe('Ledger core', () => { @@ -98,6 +100,7 @@ describe('Ledger core', () => { ]), setDeviceId: jest.fn(), setHdPath: jest.fn(), + setAccountToUnlock: jest.fn(), }; mockKeyringController.withKeyring.mockImplementation( @@ -129,6 +132,12 @@ describe('Ledger core', () => { expect(ledgerKeyring.setHdPath).toHaveBeenCalled(); expect(ledgerKeyring.setDeviceId).toHaveBeenCalled(); }); + + it('does not call keyring.setHdPath and keyring.setDeviceId if deviceId is the same', async () => { + await connectLedgerHardware(mockTransport, 'deviceId'); + expect(ledgerKeyring.setHdPath).not.toHaveBeenCalled(); + expect(ledgerKeyring.setDeviceId).not.toHaveBeenCalled(); + }); }); describe('openEthereumAppOnLedger', () => { @@ -190,4 +199,12 @@ describe('Ledger core', () => { expect(value).toBe('signature'); }); }); + + describe(`unlockLedgerWalletAccount`, () => { + it(`calls keyring.setAccountToUnlock and addAccounts`, async () => { + await unlockLedgerWalletAccount(1); + expect(ledgerKeyring.setAccountToUnlock).toHaveBeenCalled(); + expect(ledgerKeyring.addAccounts).toHaveBeenCalledWith(1); + }); + }); }); diff --git a/patches/@metamask+keyring-controller+16.0.0.patch b/patches/@metamask+keyring-controller+16.0.0.patch index 9c653264f45..30d86b784e6 100644 --- a/patches/@metamask+keyring-controller+16.0.0.patch +++ b/patches/@metamask+keyring-controller+16.0.0.patch @@ -17,18 +17,18 @@ index 5b557f5..ff65f14 100644 --- a/node_modules/@metamask/keyring-controller/dist/KeyringController.js +++ b/node_modules/@metamask/keyring-controller/dist/KeyringController.js @@ -7,7 +7,7 @@ - - - + + + -var _chunk2GJQ6KDWjs = require('./chunk-2GJQ6KDW.js'); +var _chunkEGSSQX6Ljs = require('./chunk-EGSSQX6L.js'); require('./chunk-ZGV2QNCG.js'); - - + + @@ -18,5 +18,5 @@ require('./chunk-ZGV2QNCG.js'); - - - + + + -exports.AccountImportStrategy = _chunk2GJQ6KDWjs.AccountImportStrategy; exports.KeyringController = _chunk2GJQ6KDWjs.KeyringController; exports.KeyringTypes = _chunk2GJQ6KDWjs.KeyringTypes; exports.SignTypedDataVersion = _chunk2GJQ6KDWjs.SignTypedDataVersion; exports.default = _chunk2GJQ6KDWjs.KeyringController_default; exports.getDefaultKeyringState = _chunk2GJQ6KDWjs.getDefaultKeyringState; exports.isCustodyKeyring = _chunk2GJQ6KDWjs.isCustodyKeyring; exports.keyringBuilderFactory = _chunk2GJQ6KDWjs.keyringBuilderFactory; +exports.AccountImportStrategy = _chunkEGSSQX6Ljs.AccountImportStrategy; exports.KeyringController = _chunkEGSSQX6Ljs.KeyringController; exports.KeyringTypes = _chunkEGSSQX6Ljs.KeyringTypes; exports.SignTypedDataVersion = _chunkEGSSQX6Ljs.SignTypedDataVersion; exports.default = _chunkEGSSQX6Ljs.KeyringController_default; exports.getDefaultKeyringState = _chunkEGSSQX6Ljs.getDefaultKeyringState; exports.isCustodyKeyring = _chunkEGSSQX6Ljs.isCustodyKeyring; exports.keyringBuilderFactory = _chunkEGSSQX6Ljs.keyringBuilderFactory; //# sourceMappingURL=KeyringController.js.map @@ -65,8 +65,8 @@ index 81fdecf..3844651 100644 _subscribeToQRKeyringEvents = new WeakSet(); subscribeToQRKeyringEvents_fn = function(qrKeyring) { @@ -1465,4 +1463,4 @@ var KeyringController_default = KeyringController; - - + + exports.KeyringTypes = KeyringTypes; exports.isCustodyKeyring = isCustodyKeyring; exports.AccountImportStrategy = AccountImportStrategy; exports.SignTypedDataVersion = SignTypedDataVersion; exports.keyringBuilderFactory = keyringBuilderFactory; exports.getDefaultKeyringState = getDefaultKeyringState; exports.KeyringController = KeyringController; exports.KeyringController_default = KeyringController_default; -//# sourceMappingURL=chunk-2GJQ6KDW.js.map \ No newline at end of file @@ -103,18 +103,18 @@ index 35b2a0f..655541f 100644 --- a/node_modules/@metamask/keyring-controller/dist/index.js +++ b/node_modules/@metamask/keyring-controller/dist/index.js @@ -6,7 +6,7 @@ - - - + + + -var _chunk2GJQ6KDWjs = require('./chunk-2GJQ6KDW.js'); +var _chunkEGSSQX6Ljs = require('./chunk-EGSSQX6L.js'); require('./chunk-ZGV2QNCG.js'); - - + + @@ -16,5 +16,5 @@ require('./chunk-ZGV2QNCG.js'); - - - + + + -exports.AccountImportStrategy = _chunk2GJQ6KDWjs.AccountImportStrategy; exports.KeyringController = _chunk2GJQ6KDWjs.KeyringController; exports.KeyringTypes = _chunk2GJQ6KDWjs.KeyringTypes; exports.SignTypedDataVersion = _chunk2GJQ6KDWjs.SignTypedDataVersion; exports.getDefaultKeyringState = _chunk2GJQ6KDWjs.getDefaultKeyringState; exports.isCustodyKeyring = _chunk2GJQ6KDWjs.isCustodyKeyring; exports.keyringBuilderFactory = _chunk2GJQ6KDWjs.keyringBuilderFactory; +exports.AccountImportStrategy = _chunkEGSSQX6Ljs.AccountImportStrategy; exports.KeyringController = _chunkEGSSQX6Ljs.KeyringController; exports.KeyringTypes = _chunkEGSSQX6Ljs.KeyringTypes; exports.SignTypedDataVersion = _chunkEGSSQX6Ljs.SignTypedDataVersion; exports.getDefaultKeyringState = _chunkEGSSQX6Ljs.getDefaultKeyringState; exports.isCustodyKeyring = _chunkEGSSQX6Ljs.isCustodyKeyring; exports.keyringBuilderFactory = _chunkEGSSQX6Ljs.keyringBuilderFactory; //# sourceMappingURL=index.js.map From ac0d26d7c6b895945a3c08307373b1e8d63565ef Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 9 Jul 2024 17:41:48 +0800 Subject: [PATCH 52/75] feat: Change error log title to correct devices. --- app/components/Views/LedgerSelectAccount/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 72de25aad09..e6e72d09aa4 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -91,7 +91,7 @@ const LedgerSelectAccount = () => { await unlockLedgerWalletAccount(index); } } catch (err) { - Logger.log('Error: Connecting QR hardware wallet', err); + Logger.log('Error: Connecting Ledger hardware wallet', err); } setBlockingModalVisible(false); From bf963658b6b13cd72e88d30b33fdc43d30786499 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 9 Jul 2024 21:16:50 +0800 Subject: [PATCH 53/75] feat: Fix the data migration issue from ledger accounts from V7.24.4 --- app/core/Ledger/Ledger.test.ts | 6 ------ app/core/Ledger/Ledger.ts | 6 ++---- ...mask+eth-ledger-bridge-keyring+4.0.0.patch | 20 ++++--------------- 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index c2dc2071b26..820387c462e 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -132,12 +132,6 @@ describe('Ledger core', () => { expect(ledgerKeyring.setHdPath).toHaveBeenCalled(); expect(ledgerKeyring.setDeviceId).toHaveBeenCalled(); }); - - it('does not call keyring.setHdPath and keyring.setDeviceId if deviceId is the same', async () => { - await connectLedgerHardware(mockTransport, 'deviceId'); - expect(ledgerKeyring.setHdPath).not.toHaveBeenCalled(); - expect(ledgerKeyring.setDeviceId).not.toHaveBeenCalled(); - }); }); describe('openEthereumAppOnLedger', () => { diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 3452412380c..662ce38a044 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -46,10 +46,8 @@ export const connectLedgerHardware = async ( ): Promise => { const appAndVersion = await withLedgerKeyring( async (keyring: LedgerKeyring) => { - if (keyring.getDeviceId() !== deviceId) { - keyring.setHdPath("m/44'/60'/0'/0"); - keyring.setDeviceId(deviceId); - } + keyring.setHdPath("m/44'/60'/0'/0"); + keyring.setDeviceId(deviceId); const bridge = keyring.bridge as LedgerMobileBridge; await bridge.updateTransportMethod(transport); diff --git a/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch b/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch index 9d0dadd9a2e..a28cde43391 100644 --- a/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch +++ b/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch @@ -1,21 +1,8 @@ diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js -index b649ff2..d32b858 100644 +index b649ff2..49e3cad 100644 --- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js +++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js -@@ -39,6 +39,12 @@ class LedgerTransportMiddleware { - */ - setTransport(transport) { - __classPrivateFieldSet(this, _LedgerTransportMiddleware_transport, transport, "f"); -+ /** -+ * PATCH to fix the issue with iOS -+ * As the transport is set, we need to recreate the eth app instance otherwise iOS will throw disconnect error when we reconnect the device. -+ * we should remove this PATCH once the new release of library is available. -+ */ -+ __classPrivateFieldSet(this, _LedgerTransportMiddleware_app, new ledger_hw_app_1.MetaMaskLedgerHwAppEth(this.getTransport()), "f"); - } - /** - * Method to retrieve the transport object. -@@ -58,9 +64,11 @@ class LedgerTransportMiddleware { +@@ -58,9 +58,12 @@ class LedgerTransportMiddleware { * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation. */ getEthApp() { @@ -24,9 +11,10 @@ index b649ff2..d32b858 100644 - } + /** + * PATCH to fix the issue with iOS -+ * Move the recreate of ethApp code to setTransport method. ++ * As the transport is set, we need to recreate the eth app instance otherwise iOS will throw disconnect error when we reconnect the device. + * we should remove this PATCH once the new release of library is available. + */ ++ __classPrivateFieldSet(this, _LedgerTransportMiddleware_app, new ledger_hw_app_1.MetaMaskLedgerHwAppEth(this.getTransport()), "f"); return __classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f"); } } From 8163e84b3e268d85be77c8baf93344a294e770d6 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang <7315988+dawnseeker8@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:41:33 +0800 Subject: [PATCH 54/75] Apply suggestions from code review Co-authored-by: Gustavo Antunes <17601467+gantunesr@users.noreply.github.com> --- .../UI/LedgerModals/LedgerConfirmationModal.test.tsx | 2 +- app/components/Views/AccountActions/AccountActions.tsx | 2 +- app/components/Views/LedgerSelectAccount/index.tsx | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx index 18124741e6b..7ebdb272f26 100644 --- a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx +++ b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx @@ -356,7 +356,7 @@ describe('LedgerConfirmationModal', () => { expect(onConfirmation).toHaveBeenCalled(); }); - it('logs LEDGER_HARDWARE_WALLET_ERROR thrown when ledger error occurs', async () => { + it('logs LEDGER_HARDWARE_WALLET_ERROR event when the ledger error occurs', async () => { const onConfirmation = jest.fn(); const ledgerLogicToRun = jest.fn(); diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 3d481c039cf..48db0a47f0b 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -177,7 +177,7 @@ const AccountActions = () => { const { keyrings } = Controller.KeyringController.state; const updatedKeyring = keyrings.find( - (keyring) => keyring.type === kr?.type, + (kr) => kr.type === keyring?.type, ); // If there are no more accounts in the keyring, forget the device diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index e6e72d09aa4..c7042484229 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -115,9 +115,13 @@ const LedgerSelectAccount = () => { }, [dispatch, navigation, trackEvent]); const onAnimationCompleted = useCallback(async () => { - if (blockingModalVisible) { - if (forgetDevice) { - await onForget(); + if (!blockingModalVisible) { + return; + } + + if (forgetDevice) { + await onForget(); + // ... setBlockingModalVisible(false); setForgetDevice(false); } else if (unlockAccounts.trigger) { From 938c08b20bff36ec440e38976b9d24cbbd059fc7 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 10 Jul 2024 14:53:18 +0800 Subject: [PATCH 55/75] feat: commit some change after code review,, Following change has beren done. 1. create a constants.ts under core/ledger directory to contain the LEDGER_HD_PATH 2. add `common.please_wait` message key in en.json, and remove the not common message key for ledger and qr code. --- app/components/Views/AccountActions/AccountActions.test.tsx | 2 +- app/components/Views/AccountActions/AccountActions.tsx | 2 +- app/components/Views/ConnectQRHardware/index.tsx | 4 +--- app/components/Views/LedgerSelectAccount/index.tsx | 4 +--- app/core/Ledger/Ledger.ts | 3 ++- app/core/Ledger/constants.ts | 1 + locales/languages/en.json | 6 ++++-- 7 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 app/core/Ledger/constants.ts diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index 8273facdce9..c26fe806f1d 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -243,7 +243,7 @@ describe('AccountActions', () => { await waitFor(() => { expect( - getByText(strings('connect_qr_hardware.please_wait')), + getByText(strings('common.please_wait')), ).toBeDefined(); }); }); diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 3d481c039cf..f36a3982b4c 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -271,7 +271,7 @@ const AccountActions = () => { onAnimationCompleted={triggerRemoveHWAccount} > - {strings('connect_qr_hardware.please_wait')} + {strings('common.please_wait')} diff --git a/app/components/Views/ConnectQRHardware/index.tsx b/app/components/Views/ConnectQRHardware/index.tsx index 19f8a1711fc..0597a690ff7 100644 --- a/app/components/Views/ConnectQRHardware/index.tsx +++ b/app/components/Views/ConnectQRHardware/index.tsx @@ -368,9 +368,7 @@ const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { hideModal={hideScanner} /> - - {strings('connect_qr_hardware.please_wait')} - + {strings('common.please_wait')} ); diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index e6e72d09aa4..7a8128ce3b7 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -173,9 +173,7 @@ const LedgerSelectAccount = () => { isLoadingAction onAnimationCompleted={onAnimationCompleted} > - - {strings('connect_qr_hardware.please_wait')} - + {strings('common.please_wait')} ); diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 662ce38a044..8c526aa7ed3 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -6,6 +6,7 @@ import { LedgerKeyring, LedgerMobileBridge, } from '@metamask/eth-ledger-bridge-keyring'; +import { LEDGER_HD_PATH } from './constants'; /** * Perform an operation with the Ledger keyring. @@ -46,7 +47,7 @@ export const connectLedgerHardware = async ( ): Promise => { const appAndVersion = await withLedgerKeyring( async (keyring: LedgerKeyring) => { - keyring.setHdPath("m/44'/60'/0'/0"); + keyring.setHdPath(LEDGER_HD_PATH); keyring.setDeviceId(deviceId); const bridge = keyring.bridge as LedgerMobileBridge; diff --git a/app/core/Ledger/constants.ts b/app/core/Ledger/constants.ts new file mode 100644 index 00000000000..cc7a5e434d4 --- /dev/null +++ b/app/core/Ledger/constants.ts @@ -0,0 +1 @@ +export const LEDGER_HD_PATH = `m/44'/60'/0'/0`; diff --git a/locales/languages/en.json b/locales/languages/en.json index a1919dce540..0ef936e3db5 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -672,8 +672,7 @@ "hint_text": "Scan your Keystone wallet to ", "purpose_connect": "connect", "purpose_sign": "confirm the transaction", - "select_accounts": "Select an Account", - "please_wait": "Please wait" + "select_accounts": "Select an Account" }, "data_collection_modal": { "accept": "Okay", @@ -3128,5 +3127,8 @@ "title": "Estimated changes", "tooltip_description": "Estimated changes are what might happen if you go through with this transaction. This is just a prediction, not a guarantee.", "total_fiat": "Total = {{currency}}" + }, + "common": { + "please_wait": "Please wait" } } From 076b661c0f066ccc56e741b72cef8cd00531f574 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 10 Jul 2024 15:31:38 +0800 Subject: [PATCH 56/75] feat: change MetrixEvent for HARDWARE_WALLET_FORGOTTEN with deviceType property. --- .../AccountActions/AccountActions.test.tsx | 4 +--- .../Views/AccountActions/AccountActions.tsx | 20 +++++++++---------- .../Views/LedgerSelectAccount/index.tsx | 17 ++++++++-------- app/core/Analytics/MetaMetrics.events.ts | 8 ++++---- app/core/Ledger/Ledger.ts | 2 +- app/core/Ledger/constants.ts | 4 +++- 6 files changed, 26 insertions(+), 29 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index c26fe806f1d..488f1bdab8e 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -242,9 +242,7 @@ describe('AccountActions', () => { }); await waitFor(() => { - expect( - getByText(strings('common.please_wait')), - ).toBeDefined(); + expect(getByText(strings('common.please_wait'))).toBeDefined(); }); }); }); diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index b3b90bcb4af..440d3977d53 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -159,7 +159,7 @@ const AccountActions = () => { const triggerRemoveHWAccount = useCallback(async () => { if (blockingModalVisible && selectedAddress) { - const kr = getKeyringByAddress(selectedAddress); + const keyring = getKeyringByAddress(selectedAddress); let requestForgetDevice = false; // Remove account from KeyringController @@ -167,7 +167,7 @@ const AccountActions = () => { await removeAccountsFromPermissions([selectedAddress]); const newAccounts = await Controller.KeyringController.getAccounts(); trackEvent(MetaMetricsEvents.WALLET_REMOVED, { - accountType: kr?.type, + accountType: keyring?.type, address: selectedAddress, }); @@ -176,9 +176,7 @@ const AccountActions = () => { const { keyrings } = Controller.KeyringController.state; - const updatedKeyring = keyrings.find( - (kr) => kr.type === keyring?.type, - ); + const updatedKeyring = keyrings.find((kr) => kr.type === keyring?.type); // If there are no more accounts in the keyring, forget the device if (updatedKeyring) { @@ -189,16 +187,18 @@ const AccountActions = () => { requestForgetDevice = true; } if (requestForgetDevice) { - switch (kr?.type) { + switch (keyring?.type) { case ExtendedKeyringTypes.ledger: await forgetLedger(); - trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { + trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { device_type: 'Ledger', }); break; case ExtendedKeyringTypes.qr: await Controller.KeyringController.forgetQRDevice(); - // there is not a MetaMetricsEvent for this action?? + trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { + device_type: 'QR Hardware Wallet', + }); break; default: break; @@ -270,9 +270,7 @@ const AccountActions = () => { isLoadingAction onAnimationCompleted={triggerRemoveHWAccount} > - - {strings('common.please_wait')} - + {strings('common.please_wait')} ); diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index cfb2f17689f..3f864507e23 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -107,7 +107,7 @@ const LedgerSelectAccount = () => { setBlockingModalVisible(true); await forgetLedger(); dispatch(setReloadAccounts(true)); - trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { + trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { device_type: 'Ledger', }); setBlockingModalVisible(false); @@ -118,17 +118,16 @@ const LedgerSelectAccount = () => { if (!blockingModalVisible) { return; } - + if (forgetDevice) { await onForget(); // ... - setBlockingModalVisible(false); - setForgetDevice(false); - } else if (unlockAccounts.trigger) { - await onUnlock(unlockAccounts.accountIndexes); - setBlockingModalVisible(false); - setUnlockAccounts({ trigger: false, accountIndexes: [] }); - } + setBlockingModalVisible(false); + setForgetDevice(false); + } else if (unlockAccounts.trigger) { + await onUnlock(unlockAccounts.accountIndexes); + setBlockingModalVisible(false); + setUnlockAccounts({ trigger: false, accountIndexes: [] }); } }, [ blockingModalVisible, diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts index f3bffdb3584..b203ecd7c73 100644 --- a/app/core/Analytics/MetaMetrics.events.ts +++ b/app/core/Analytics/MetaMetrics.events.ts @@ -353,7 +353,9 @@ enum EVENT_NAME { CONNECT_LEDGER_SUCCESS = 'Connected Account with hardware wallet', LEDGER_HARDWARE_TRANSACTION_CANCELLED = 'User canceled Ledger hardware transaction', LEDGER_HARDWARE_WALLET_ERROR = 'Ledger hardware wallet error', - LEDGER_HARDWARE_WALLET_FORGOTTEN = 'Ledger hardware wallet forgotten', + + // common hardware wallet + HARDWARE_WALLET_FORGOTTEN = 'Hardware wallet forgotten', //Notifications ALL_NOTIFICATIONS = 'All Notifications', @@ -842,9 +844,7 @@ const events = { LEDGER_HARDWARE_WALLET_ERROR: generateOpt( EVENT_NAME.LEDGER_HARDWARE_WALLET_ERROR, ), - LEDGER_HARDWARE_WALLET_FORGOTTEN: generateOpt( - EVENT_NAME.LEDGER_HARDWARE_WALLET_FORGOTTEN, - ), + HARDWARE_WALLET_FORGOTTEN: generateOpt(EVENT_NAME.HARDWARE_WALLET_FORGOTTEN), // Smart transactions SMART_TRANSACTION_OPT_IN: generateOpt(EVENT_NAME.SMART_TRANSACTION_OPT_IN), diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 8c526aa7ed3..7cb48dfdd2b 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -6,7 +6,7 @@ import { LedgerKeyring, LedgerMobileBridge, } from '@metamask/eth-ledger-bridge-keyring'; -import { LEDGER_HD_PATH } from './constants'; +import LEDGER_HD_PATH from './constants'; /** * Perform an operation with the Ledger keyring. diff --git a/app/core/Ledger/constants.ts b/app/core/Ledger/constants.ts index cc7a5e434d4..f9bb42547fe 100644 --- a/app/core/Ledger/constants.ts +++ b/app/core/Ledger/constants.ts @@ -1 +1,3 @@ -export const LEDGER_HD_PATH = `m/44'/60'/0'/0`; +const LEDGER_HD_PATH = `m/44'/60'/0'/0`; + +export default LEDGER_HD_PATH; From eb3319e0438c21802b14dd3dac21def46a7fbcdc Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 10 Jul 2024 17:24:18 +0800 Subject: [PATCH 57/75] feat: Refactor the `getLedgerAccountsByPage`to `getLedgerAccountsByOperation`. and put opeartionType enuim to constants.ts --- .../Views/LedgerSelectAccount/index.tsx | 17 +++++++++++++---- app/core/Ledger/Ledger.test.ts | 10 +++++----- app/core/Ledger/Ledger.ts | 17 ++++++++++------- app/core/Ledger/constants.ts | 8 ++++++-- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 3f864507e23..08911f5e537 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -12,7 +12,7 @@ import ledgerDeviceLightImage from 'images/ledger-device-light.png'; import ledgerDeviceDarkImage from 'images/ledger-device-dark.png'; import { forgetLedger, - getLedgerAccountsByPage, + getLedgerAccountsByOperation, unlockLedgerWalletAccount, } from '../../../core/Ledger/Ledger'; import LedgerConnect from '../LedgerConnect'; @@ -22,6 +22,9 @@ import { useDispatch } from 'react-redux'; import { KeyringController } from '@metamask/keyring-controller'; import { StackNavigationProp } from '@react-navigation/stack'; import createStyles from './index.styles'; +import { + OperationTypes +} from '../../../core/Ledger/constants'; const LedgerSelectAccount = () => { const navigation = useNavigation>(); @@ -65,17 +68,23 @@ const LedgerSelectAccount = () => { trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { device_type: 'Ledger', }); - const _accounts = await getLedgerAccountsByPage(0); + const _accounts = await getLedgerAccountsByOperation( + OperationTypes.GET_FIRST_PAGE, + ); setAccounts(_accounts); }, [trackEvent]); const nextPage = useCallback(async () => { - const _accounts = await getLedgerAccountsByPage(1); + const _accounts = await getLedgerAccountsByOperation( + OperationTypes.GET_NEXT_PAGE, + ); setAccounts(_accounts); }, []); const prevPage = useCallback(async () => { - const _accounts = await getLedgerAccountsByPage(-1); + const _accounts = await getLedgerAccountsByOperation( + OperationTypes.GET_PREVIOUS_PAGE, + ); setAccounts(_accounts); }, []); diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 820387c462e..6f82b71d09b 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -3,7 +3,7 @@ import { connectLedgerHardware, forgetLedger, getDeviceId, - getLedgerAccountsByPage, + getLedgerAccountsByOperation, ledgerSignTypedMessage, openEthereumAppOnLedger, unlockLedgerWalletAccount, @@ -162,17 +162,17 @@ describe('Ledger core', () => { }); }); - describe('getLedgerAccountsByPage', () => { + describe('getLedgerAccountsByOperation', () => { it('calls ledgerKeyring.getNextPage on ledgerKeyring', async () => { - await getLedgerAccountsByPage(1); + await getLedgerAccountsByOperation(1); expect(ledgerKeyring.getNextPage).toHaveBeenCalled(); }); it('calls getPreviousPage on ledgerKeyring', async () => { - await getLedgerAccountsByPage(-1); + await getLedgerAccountsByOperation(-1); expect(ledgerKeyring.getPreviousPage).toHaveBeenCalled(); }); it('calls getFirstPage on ledgerKeyring', async () => { - await getLedgerAccountsByPage(0); + await getLedgerAccountsByOperation(0); expect(ledgerKeyring.getFirstPage).toHaveBeenCalled(); }); }); diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 7cb48dfdd2b..e28f0507a74 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -6,7 +6,7 @@ import { LedgerKeyring, LedgerMobileBridge, } from '@metamask/eth-ledger-bridge-keyring'; -import LEDGER_HD_PATH from './constants'; +import { OperationTypes, LEDGER_HD_PATH } from './constants'; /** * Perform an operation with the Ledger keyring. @@ -100,23 +100,26 @@ export const getDeviceId = async (): Promise => /** * Unlock Ledger Accounts by page - * @param page - The page number to unlock + * @param operation - the operation number,
0: Get First Page
1: Get Next Page
-1: Get Previous Page + * @return The Ledger Accounts */ -export const getLedgerAccountsByPage = async ( - page: number, +export const getLedgerAccountsByOperation = async ( + operation: number, ): Promise<{ balance: string; address: string; index: number }[]> => { try { const accounts = await withLedgerKeyring(async (keyring: LedgerKeyring) => { - switch (page) { - case -1: + switch (operation) { + case OperationTypes.GET_PREVIOUS_PAGE: return await keyring.getPreviousPage(); - case 1: + case OperationTypes.GET_NEXT_PAGE: return await keyring.getNextPage(); default: return await keyring.getFirstPage(); } }); + console.warn(`Returning accounts by ${operation}`, accounts); + return accounts.map((account) => ({ ...account, balance: '0x0', diff --git a/app/core/Ledger/constants.ts b/app/core/Ledger/constants.ts index f9bb42547fe..4733bb4bc86 100644 --- a/app/core/Ledger/constants.ts +++ b/app/core/Ledger/constants.ts @@ -1,3 +1,7 @@ -const LEDGER_HD_PATH = `m/44'/60'/0'/0`; +export const LEDGER_HD_PATH = `m/44'/60'/0'/0`; -export default LEDGER_HD_PATH; +export enum OperationTypes { + GET_FIRST_PAGE = 0, + GET_NEXT_PAGE = 1, + GET_PREVIOUS_PAGE = -1, +} From 474b1e7e382b3f6c6dfb299a51c52f6708cc932e Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 10 Jul 2024 17:42:36 +0800 Subject: [PATCH 58/75] feat: fix the format issue in the file. --- app/components/Views/LedgerSelectAccount/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 08911f5e537..2efc0e4f48b 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -22,9 +22,7 @@ import { useDispatch } from 'react-redux'; import { KeyringController } from '@metamask/keyring-controller'; import { StackNavigationProp } from '@react-navigation/stack'; import createStyles from './index.styles'; -import { - OperationTypes -} from '../../../core/Ledger/constants'; +import { OperationTypes } from '../../../core/Ledger/constants'; const LedgerSelectAccount = () => { const navigation = useNavigation>(); From 06e6f6aee2477532dc7e2b4ad66017f54cc1b351 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 10 Jul 2024 18:37:26 +0800 Subject: [PATCH 59/75] feat: refactor to put OperationTypes to enum in `types.ts` file. --- app/components/Views/LedgerSelectAccount/index.tsx | 2 +- app/core/Ledger/Ledger.test.ts | 7 ++++--- app/core/Ledger/Ledger.ts | 3 ++- app/core/Ledger/constants.ts | 8 ++------ app/core/Ledger/types.ts | 7 +++++++ 5 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 app/core/Ledger/types.ts diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 2efc0e4f48b..6784535975a 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -22,7 +22,7 @@ import { useDispatch } from 'react-redux'; import { KeyringController } from '@metamask/keyring-controller'; import { StackNavigationProp } from '@react-navigation/stack'; import createStyles from './index.styles'; -import { OperationTypes } from '../../../core/Ledger/constants'; +import OperationTypes from '../../../core/Ledger/types'; const LedgerSelectAccount = () => { const navigation = useNavigation>(); diff --git a/app/core/Ledger/Ledger.test.ts b/app/core/Ledger/Ledger.test.ts index 6f82b71d09b..e078666d087 100644 --- a/app/core/Ledger/Ledger.test.ts +++ b/app/core/Ledger/Ledger.test.ts @@ -11,6 +11,7 @@ import { import Engine from '../../core/Engine'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; import type BleTransport from '@ledgerhq/react-native-hw-transport-ble'; +import OperationTypes from './types'; jest.mock('../../core/Engine', () => ({ context: { @@ -164,15 +165,15 @@ describe('Ledger core', () => { describe('getLedgerAccountsByOperation', () => { it('calls ledgerKeyring.getNextPage on ledgerKeyring', async () => { - await getLedgerAccountsByOperation(1); + await getLedgerAccountsByOperation(OperationTypes.GET_NEXT_PAGE); expect(ledgerKeyring.getNextPage).toHaveBeenCalled(); }); it('calls getPreviousPage on ledgerKeyring', async () => { - await getLedgerAccountsByOperation(-1); + await getLedgerAccountsByOperation(OperationTypes.GET_PREVIOUS_PAGE); expect(ledgerKeyring.getPreviousPage).toHaveBeenCalled(); }); it('calls getFirstPage on ledgerKeyring', async () => { - await getLedgerAccountsByOperation(0); + await getLedgerAccountsByOperation(OperationTypes.GET_FIRST_PAGE); expect(ledgerKeyring.getFirstPage).toHaveBeenCalled(); }); }); diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index e28f0507a74..8d03ff10b43 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -6,7 +6,8 @@ import { LedgerKeyring, LedgerMobileBridge, } from '@metamask/eth-ledger-bridge-keyring'; -import { OperationTypes, LEDGER_HD_PATH } from './constants'; +import LEDGER_HD_PATH from './constants'; +import OperationTypes from './types'; /** * Perform an operation with the Ledger keyring. diff --git a/app/core/Ledger/constants.ts b/app/core/Ledger/constants.ts index 4733bb4bc86..f9bb42547fe 100644 --- a/app/core/Ledger/constants.ts +++ b/app/core/Ledger/constants.ts @@ -1,7 +1,3 @@ -export const LEDGER_HD_PATH = `m/44'/60'/0'/0`; +const LEDGER_HD_PATH = `m/44'/60'/0'/0`; -export enum OperationTypes { - GET_FIRST_PAGE = 0, - GET_NEXT_PAGE = 1, - GET_PREVIOUS_PAGE = -1, -} +export default LEDGER_HD_PATH; diff --git a/app/core/Ledger/types.ts b/app/core/Ledger/types.ts new file mode 100644 index 00000000000..c3f62ae8d93 --- /dev/null +++ b/app/core/Ledger/types.ts @@ -0,0 +1,7 @@ +enum OperationTypes { + GET_FIRST_PAGE = 0, + GET_NEXT_PAGE = 1, + GET_PREVIOUS_PAGE = -1, +} + +export default OperationTypes; From f05e7dd8fbce020b26849d20ce20df8345347172 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 10 Jul 2024 19:20:25 +0800 Subject: [PATCH 60/75] feat: Create HardwareDeviceTypes enum to MetaMetrics.types.ts, and refactor all hardware relative metricEvent to use that new enum to keep all deviceTypes consistency. --- .../UI/LedgerModals/LedgerConfirmationModal.test.tsx | 3 ++- .../UI/LedgerModals/LedgerConfirmationModal.tsx | 11 ++++++----- .../Views/AccountActions/AccountActions.tsx | 5 +++-- .../Views/ConnectHardware/SelectHardware/index.tsx | 3 ++- app/components/Views/ConnectQRHardware/index.tsx | 5 +++-- app/components/Views/LedgerSelectAccount/index.tsx | 7 ++++--- app/core/Analytics/MetaMetrics.types.ts | 5 +++++ 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx index 7ebdb272f26..642b6a2a0c5 100644 --- a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx +++ b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx @@ -19,6 +19,7 @@ import { strings } from '../../../../locales/i18n'; import { useMetrics } from '../../hooks/useMetrics'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { fireEvent } from '@testing-library/react-native'; +import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; jest.mock('../../hooks/Ledger/useBluetooth', () => ({ __esModule: true, @@ -393,7 +394,7 @@ describe('LedgerConfirmationModal', () => { 1, MetaMetricsEvents.LEDGER_HARDWARE_WALLET_ERROR, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, error: 'LEDGER_ETH_APP_NOT_INSTALLED', }, ); diff --git a/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx b/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx index ad7df903de6..cd5bce379ee 100644 --- a/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx +++ b/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx @@ -16,6 +16,7 @@ import { BluetoothPermissionErrors, LedgerCommunicationErrors, } from '../../../core/Ledger/ledgerErrors'; +import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; const createStyles = (colors: Colors) => StyleSheet.create({ @@ -76,7 +77,7 @@ const LedgerConfirmationModal = ({ // Handle a super edge case of the user starting a transaction with the device connected // After arriving to confirmation the ETH app is not installed anymore this causes a crash. trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_ERROR, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, error: 'LEDGER_ETH_APP_NOT_INSTALLED', }); } @@ -88,7 +89,7 @@ const LedgerConfirmationModal = ({ onRejection(); } finally { trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_TRANSACTION_CANCELLED, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, }); } }; @@ -177,7 +178,7 @@ const LedgerConfirmationModal = ({ } if (ledgerError !== LedgerCommunicationErrors.UserRefusedConfirmation) { trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_ERROR, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, error: `${ledgerError}`, }); } @@ -206,7 +207,7 @@ const LedgerConfirmationModal = ({ } setPermissionErrorShown(true); trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_ERROR, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, error: 'LEDGER_BLUETOOTH_PERMISSION_ERR', }); } @@ -217,7 +218,7 @@ const LedgerConfirmationModal = ({ subtitle: strings('ledger.bluetooth_off_message'), }); trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_ERROR, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, error: 'LEDGER_BLUETOOTH_CONNECTION_ERR', }); } diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 440d3977d53..3a2742b7d56 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -43,6 +43,7 @@ import Engine from '../../../core/Engine'; import BlockingActionModal from '../../UI/BlockingActionModal'; import { useTheme } from '../../../util/theme'; import { Hex } from '@metamask/utils'; +import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; const AccountActions = () => { const { colors } = useTheme(); @@ -191,13 +192,13 @@ const AccountActions = () => { case ExtendedKeyringTypes.ledger: await forgetLedger(); trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, }); break; case ExtendedKeyringTypes.qr: await Controller.KeyringController.forgetQRDevice(); trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { - device_type: 'QR Hardware Wallet', + device_type: HardwareDeviceTypes.QR, }); break; default: diff --git a/app/components/Views/ConnectHardware/SelectHardware/index.tsx b/app/components/Views/ConnectHardware/SelectHardware/index.tsx index 225165bcb13..1ee9f94507d 100644 --- a/app/components/Views/ConnectHardware/SelectHardware/index.tsx +++ b/app/components/Views/ConnectHardware/SelectHardware/index.tsx @@ -24,6 +24,7 @@ import { } from '../../../../util/theme'; import { getNavigationOptionsTitle } from '../../../UI/Navbar'; import { useMetrics } from '../../../../components/hooks/useMetrics'; +import { HardwareDeviceTypes } from '../../../../core/Analytics/MetaMetrics.types'; // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -107,7 +108,7 @@ const SelectHardwareWallet = () => { const navigateToConnectLedger = async () => { trackEvent(MetaMetricsEvents.CONNECT_LEDGER, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, }); navigation.navigate(Routes.HW.CONNECT_LEDGER); diff --git a/app/components/Views/ConnectQRHardware/index.tsx b/app/components/Views/ConnectQRHardware/index.tsx index 0597a690ff7..ba1c40a4741 100644 --- a/app/components/Views/ConnectQRHardware/index.tsx +++ b/app/components/Views/ConnectQRHardware/index.tsx @@ -29,6 +29,7 @@ import { safeToChecksumAddress } from '../../../util/address'; import { useMetrics } from '../../../components/hooks/useMetrics'; import type { MetaMaskKeyring as QRKeyring } from '@keystonehq/metamask-airgapped-keyring'; import { KeyringTypes } from '@metamask/keyring-controller'; +import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; interface IConnectQRHardwareProps { // TODO: Replace "any" with type @@ -214,7 +215,7 @@ const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { const onConnectHardware = useCallback(async () => { trackEvent(MetaMetricsEvents.CONTINUE_QR_HARDWARE_WALLET, { - device_type: 'QR Hardware', + device_type: HardwareDeviceTypes.QR, }); resetError(); const [qrInteractions, connectQRHardwarePromise] = @@ -231,7 +232,7 @@ const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { (ur: UR) => { hideScanner(); trackEvent(MetaMetricsEvents.CONNECT_HARDWARE_WALLET_SUCCESS, { - device_type: 'QR Hardware', + device_type: HardwareDeviceTypes.QR, }); if (!qrInteractionsRef.current) { const errorMessage = 'Missing QR keyring interactions'; diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 6784535975a..34d898ddb7c 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -23,6 +23,7 @@ import { KeyringController } from '@metamask/keyring-controller'; import { StackNavigationProp } from '@react-navigation/stack'; import createStyles from './index.styles'; import OperationTypes from '../../../core/Ledger/types'; +import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; const LedgerSelectAccount = () => { const navigation = useNavigation>(); @@ -64,7 +65,7 @@ const LedgerSelectAccount = () => { const onConnectHardware = useCallback(async () => { trackEvent(MetaMetricsEvents.CONTINUE_LEDGER_HARDWARE_WALLET, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, }); const _accounts = await getLedgerAccountsByOperation( OperationTypes.GET_FIRST_PAGE, @@ -103,7 +104,7 @@ const LedgerSelectAccount = () => { setBlockingModalVisible(false); trackEvent(MetaMetricsEvents.CONNECT_LEDGER_SUCCESS, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, }); navigation.pop(2); }, @@ -115,7 +116,7 @@ const LedgerSelectAccount = () => { await forgetLedger(); dispatch(setReloadAccounts(true)); trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { - device_type: 'Ledger', + device_type: HardwareDeviceTypes.LEDGER, }); setBlockingModalVisible(false); navigation.dispatch(StackActions.pop(2)); diff --git a/app/core/Analytics/MetaMetrics.types.ts b/app/core/Analytics/MetaMetrics.types.ts index 8e8a59a5632..a7fbe2a1a07 100644 --- a/app/core/Analytics/MetaMetrics.types.ts +++ b/app/core/Analytics/MetaMetrics.types.ts @@ -152,3 +152,8 @@ export interface IDeleteRegulationStatus { hasCollectedDataSinceDeletionRequest: boolean; dataDeletionRequestStatus: DataDeleteStatus; } + +export enum HardwareDeviceTypes { + LEDGER = 'Ledger', + QR = 'QR Hardware', +} From 662fb09994fb5b74e8a4265031adb9f549471df3 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Wed, 10 Jul 2024 20:02:42 +0800 Subject: [PATCH 61/75] feat: refactor `triggerRemoveHWAccount` method to smaller pieces, introduce `removeHardwareAccount`, `reselectFirstAccount` and `forgetDeviceIfRequired` --- .../Views/AccountActions/AccountActions.tsx | 68 +++++++++++++++---- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 3a2742b7d56..304af3a72e7 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -44,6 +44,7 @@ import BlockingActionModal from '../../UI/BlockingActionModal'; import { useTheme } from '../../../util/theme'; import { Hex } from '@metamask/utils'; import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; +import { KeyringObject } from '@metamask/keyring-controller'; const AccountActions = () => { const { colors } = useTheme(); @@ -158,28 +159,46 @@ const AccountActions = () => { ); }, []); - const triggerRemoveHWAccount = useCallback(async () => { - if (blockingModalVisible && selectedAddress) { - const keyring = getKeyringByAddress(selectedAddress); - let requestForgetDevice = false; - - // Remove account from KeyringController - await Controller.KeyringController.removeAccount(selectedAddress as Hex); - await removeAccountsFromPermissions([selectedAddress]); - const newAccounts = await Controller.KeyringController.getAccounts(); + /** + * Remove the hardware account from the keyring + * @param keyring - The keyring object + * @param address - The address to remove + */ + const removeHardwareAccount = useCallback( + async (keyring: KeyringObject, address: Hex) => { + await Controller.KeyringController.removeAccount(address); + await removeAccountsFromPermissions([address]); trackEvent(MetaMetricsEvents.WALLET_REMOVED, { accountType: keyring?.type, - address: selectedAddress, + address, }); + }, + [Controller.KeyringController, trackEvent], + ); - // setSelectedAddress to the initial account + /** + * Reselect the first account after removing the selected account + */ + const reselectFirstAccount = useCallback(async () => { + const newAccounts = await Controller.KeyringController.getAccounts(); + if (newAccounts && newAccounts.length > 0) { Engine.setSelectedAddress(newAccounts[0]); + } + }, [Controller.KeyringController]); + /** + * Forget the device if there are no more accounts in the keyring + * @param keyring - The keyring object + */ + const forgetDeviceIfRequired = useCallback( + async (keyring: KeyringObject) => { + // re-fetch the latest keyrings from KeyringController state. const { keyrings } = Controller.KeyringController.state; - const updatedKeyring = keyrings.find((kr) => kr.type === keyring?.type); // If there are no more accounts in the keyring, forget the device + let requestForgetDevice = false; + if (updatedKeyring) { if (updatedKeyring.accounts.length === 0) { requestForgetDevice = true; @@ -205,14 +224,35 @@ const AccountActions = () => { break; } } + }, + [Controller.KeyringController, trackEvent], + ); + + /** + * Trigger the remove hardware account action when user click on the remove account button + */ + const triggerRemoveHWAccount = useCallback(async () => { + if (blockingModalVisible && selectedAddress) { + const keyring = getKeyringByAddress(selectedAddress); + if (!keyring) { + console.error('Keyring not found for address:', selectedAddress); + return; + } + + await removeHardwareAccount(keyring, selectedAddress as Hex); + + await reselectFirstAccount(); + + await forgetDeviceIfRequired(keyring); setBlockingModalVisible(false); } }, [ - Controller.KeyringController, blockingModalVisible, + forgetDeviceIfRequired, + removeHardwareAccount, + reselectFirstAccount, selectedAddress, - trackEvent, ]); const goToEditAccountName = () => { From eb5c91f4cbee62ead3f1d1a056a6afb1fdc04b0f Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Fri, 12 Jul 2024 16:30:54 +0800 Subject: [PATCH 62/75] feat: remove console log. --- app/core/Ledger/Ledger.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/core/Ledger/Ledger.ts b/app/core/Ledger/Ledger.ts index 8d03ff10b43..d5ca3aacc5f 100644 --- a/app/core/Ledger/Ledger.ts +++ b/app/core/Ledger/Ledger.ts @@ -119,8 +119,6 @@ export const getLedgerAccountsByOperation = async ( } }); - console.warn(`Returning accounts by ${operation}`, accounts); - return accounts.map((account) => ({ ...account, balance: '0x0', From 65871e4766ad8bbbe95778d70509fcc7b619eaaa Mon Sep 17 00:00:00 2001 From: Xiaoming Wang <7315988+dawnseeker8@users.noreply.github.com> Date: Fri, 12 Jul 2024 16:36:44 +0800 Subject: [PATCH 63/75] Apply suggestions from code review Co-authored-by: Gustavo Antunes <17601467+gantunesr@users.noreply.github.com> --- .../Views/AccountActions/AccountActions.test.tsx | 2 +- .../Views/AccountActions/AccountActions.tsx | 12 ++++++------ app/components/Views/LedgerSelectAccount/index.tsx | 2 +- locales/languages/en.json | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index 488f1bdab8e..bc37b75b629 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -210,7 +210,7 @@ describe('AccountActions', () => { }); }); - it('clicks remove button after popup shows triggers remove process', async () => { + it('clicks remove button after popup shows to trigger the remove account process', async () => { mockKeyringController.getAccounts.mockResolvedValue([ '0xa1e359811322d97991e03f863a0c30c2cf029cd', ]); diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 304af3a72e7..3e74632e284 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -56,7 +56,7 @@ const AccountActions = () => { const [blockingModalVisible, setBlockingModalVisible] = useState(false); - const Controller = useMemo(() => { + const controllers = useMemo(() => { const { KeyringController, PreferencesController } = Engine.context; return { KeyringController, PreferencesController }; }, []); @@ -177,12 +177,12 @@ const AccountActions = () => { ); /** - * Reselect the first account after removing the selected account + * Selects the first account after removing the previous selected account */ - const reselectFirstAccount = useCallback(async () => { - const newAccounts = await Controller.KeyringController.getAccounts(); - if (newAccounts && newAccounts.length > 0) { - Engine.setSelectedAddress(newAccounts[0]); + const selectFirstAccount = useCallback(async () => { + const accounts = await Controller.KeyringController.getAccounts(); + if (accounts && accounts.length > 0) { + Engine.setSelectedAddress(accounts[0]); } }, [Controller.KeyringController]); diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 34d898ddb7c..b211913955b 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -99,7 +99,7 @@ const LedgerSelectAccount = () => { await unlockLedgerWalletAccount(index); } } catch (err) { - Logger.log('Error: Connecting Ledger hardware wallet', err); + // Do nothing } setBlockingModalVisible(false); diff --git a/locales/languages/en.json b/locales/languages/en.json index 0ef936e3db5..e9da44d03d6 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -614,7 +614,7 @@ "no": "No", "yes_remove_it": "Yes, remove it", "remove_hardware_account": "Remove hardware account", - "remove_account_alert_description": "Are you sure you want to remove this hardware wallet account? You’ll have to resync your hardware wallet if you want to use this account again with MetaMask Mobile.", + "remove_hw_account_alert_description": "Are you sure you want to remove this hardware wallet account? You’ll have to resync your hardware wallet if you want to use this account again with MetaMask Mobile.", "remove_account_alert_remove_btn": "Remove", "remove_account_alert_cancel_btn": "Nevermind", "accounts_title": "Accounts", From 7c77ce19d9b001b3bc2a0089f2f2bab48c1e2328 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Fri, 12 Jul 2024 19:19:53 +0800 Subject: [PATCH 64/75] feat: refactor the code according to Gustova's code review comments --- .../LedgerConfirmationModal.test.tsx | 2 +- .../LedgerModals/LedgerConfirmationModal.tsx | 2 +- .../Views/AccountActions/AccountActions.tsx | 33 ++++++++++--------- .../ConnectHardware/SelectHardware/index.tsx | 2 +- .../Views/ConnectQRHardware/index.tsx | 2 +- .../Views/LedgerSelectAccount/index.tsx | 4 +-- app/constants/keyringTypes.ts | 5 +++ app/core/Analytics/MetaMetrics.types.ts | 5 --- 8 files changed, 27 insertions(+), 28 deletions(-) diff --git a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx index 642b6a2a0c5..e4445891966 100644 --- a/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx +++ b/app/components/UI/LedgerModals/LedgerConfirmationModal.test.tsx @@ -19,7 +19,7 @@ import { strings } from '../../../../locales/i18n'; import { useMetrics } from '../../hooks/useMetrics'; import { MetaMetricsEvents } from '../../../core/Analytics'; import { fireEvent } from '@testing-library/react-native'; -import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; +import { HardwareDeviceTypes } from '../../../constants/keyringTypes'; jest.mock('../../hooks/Ledger/useBluetooth', () => ({ __esModule: true, diff --git a/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx b/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx index cd5bce379ee..5a765624e04 100644 --- a/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx +++ b/app/components/UI/LedgerModals/LedgerConfirmationModal.tsx @@ -16,7 +16,7 @@ import { BluetoothPermissionErrors, LedgerCommunicationErrors, } from '../../../core/Ledger/ledgerErrors'; -import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; +import { HardwareDeviceTypes } from '../../../constants/keyringTypes'; const createStyles = (colors: Colors) => StyleSheet.create({ diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 3e74632e284..e8d617995bb 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -37,13 +37,14 @@ import { AccountActionsModalSelectorsIDs } from '../../../../e2e/selectors/Modal import { useMetrics } from '../../../components/hooks/useMetrics'; import { getKeyringByAddress, isHardwareAccount } from '../../../util/address'; import { removeAccountsFromPermissions } from '../../../core/Permissions'; -import ExtendedKeyringTypes from '../../../constants/keyringTypes'; +import ExtendedKeyringTypes, { + HardwareDeviceTypes, +} from '../../../constants/keyringTypes'; import { forgetLedger } from '../../../core/Ledger/Ledger'; import Engine from '../../../core/Engine'; import BlockingActionModal from '../../UI/BlockingActionModal'; import { useTheme } from '../../../util/theme'; import { Hex } from '@metamask/utils'; -import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; import { KeyringObject } from '@metamask/keyring-controller'; const AccountActions = () => { @@ -166,35 +167,35 @@ const AccountActions = () => { */ const removeHardwareAccount = useCallback( async (keyring: KeyringObject, address: Hex) => { - await Controller.KeyringController.removeAccount(address); + await controllers.KeyringController.removeAccount(address); await removeAccountsFromPermissions([address]); trackEvent(MetaMetricsEvents.WALLET_REMOVED, { accountType: keyring?.type, address, }); }, - [Controller.KeyringController, trackEvent], + [controllers.KeyringController, trackEvent], ); /** * Selects the first account after removing the previous selected account */ const selectFirstAccount = useCallback(async () => { - const accounts = await Controller.KeyringController.getAccounts(); + const accounts = await controllers.KeyringController.getAccounts(); if (accounts && accounts.length > 0) { Engine.setSelectedAddress(accounts[0]); } - }, [Controller.KeyringController]); + }, [controllers.KeyringController]); /** * Forget the device if there are no more accounts in the keyring - * @param keyring - The keyring object + * @param keyringType - The keyring type */ const forgetDeviceIfRequired = useCallback( - async (keyring: KeyringObject) => { + async (keyringType: string) => { // re-fetch the latest keyrings from KeyringController state. - const { keyrings } = Controller.KeyringController.state; - const updatedKeyring = keyrings.find((kr) => kr.type === keyring?.type); + const { keyrings } = controllers.KeyringController.state; + const updatedKeyring = keyrings.find((kr) => kr.type === keyringType); // If there are no more accounts in the keyring, forget the device let requestForgetDevice = false; @@ -207,7 +208,7 @@ const AccountActions = () => { requestForgetDevice = true; } if (requestForgetDevice) { - switch (keyring?.type) { + switch (keyringType) { case ExtendedKeyringTypes.ledger: await forgetLedger(); trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { @@ -215,7 +216,7 @@ const AccountActions = () => { }); break; case ExtendedKeyringTypes.qr: - await Controller.KeyringController.forgetQRDevice(); + await controllers.KeyringController.forgetQRDevice(); trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { device_type: HardwareDeviceTypes.QR, }); @@ -225,7 +226,7 @@ const AccountActions = () => { } } }, - [Controller.KeyringController, trackEvent], + [controllers.KeyringController, trackEvent], ); /** @@ -241,9 +242,9 @@ const AccountActions = () => { await removeHardwareAccount(keyring, selectedAddress as Hex); - await reselectFirstAccount(); + await selectFirstAccount(); - await forgetDeviceIfRequired(keyring); + await forgetDeviceIfRequired(keyring.type); setBlockingModalVisible(false); } @@ -251,7 +252,7 @@ const AccountActions = () => { blockingModalVisible, forgetDeviceIfRequired, removeHardwareAccount, - reselectFirstAccount, + selectFirstAccount, selectedAddress, ]); diff --git a/app/components/Views/ConnectHardware/SelectHardware/index.tsx b/app/components/Views/ConnectHardware/SelectHardware/index.tsx index 1ee9f94507d..91a1ad19309 100644 --- a/app/components/Views/ConnectHardware/SelectHardware/index.tsx +++ b/app/components/Views/ConnectHardware/SelectHardware/index.tsx @@ -24,7 +24,7 @@ import { } from '../../../../util/theme'; import { getNavigationOptionsTitle } from '../../../UI/Navbar'; import { useMetrics } from '../../../../components/hooks/useMetrics'; -import { HardwareDeviceTypes } from '../../../../core/Analytics/MetaMetrics.types'; +import { HardwareDeviceTypes } from '../../../../constants/keyringTypes'; // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/app/components/Views/ConnectQRHardware/index.tsx b/app/components/Views/ConnectQRHardware/index.tsx index ba1c40a4741..7bbbb5248e3 100644 --- a/app/components/Views/ConnectQRHardware/index.tsx +++ b/app/components/Views/ConnectQRHardware/index.tsx @@ -29,7 +29,7 @@ import { safeToChecksumAddress } from '../../../util/address'; import { useMetrics } from '../../../components/hooks/useMetrics'; import type { MetaMaskKeyring as QRKeyring } from '@keystonehq/metamask-airgapped-keyring'; import { KeyringTypes } from '@metamask/keyring-controller'; -import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; +import { HardwareDeviceTypes } from '../../../constants/keyringTypes'; interface IConnectQRHardwareProps { // TODO: Replace "any" with type diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index b211913955b..78ec129325f 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -5,7 +5,6 @@ import AccountSelector from '../../UI/HardwareWallet/AccountSelector'; import BlockingActionModal from '../../UI/BlockingActionModal'; import { strings } from '../../../../locales/i18n'; import { MetaMetricsEvents } from '../../../core/Analytics'; -import Logger from '../../../util/Logger'; import { useAssetFromTheme, useTheme } from '../../../util/theme'; import useMetrics from '../../hooks/useMetrics/useMetrics'; import ledgerDeviceLightImage from 'images/ledger-device-light.png'; @@ -23,7 +22,7 @@ import { KeyringController } from '@metamask/keyring-controller'; import { StackNavigationProp } from '@react-navigation/stack'; import createStyles from './index.styles'; import OperationTypes from '../../../core/Ledger/types'; -import { HardwareDeviceTypes } from '../../../core/Analytics/MetaMetrics.types'; +import { HardwareDeviceTypes } from '../../../constants/keyringTypes'; const LedgerSelectAccount = () => { const navigation = useNavigation>(); @@ -129,7 +128,6 @@ const LedgerSelectAccount = () => { if (forgetDevice) { await onForget(); - // ... setBlockingModalVisible(false); setForgetDevice(false); } else if (unlockAccounts.trigger) { diff --git a/app/constants/keyringTypes.ts b/app/constants/keyringTypes.ts index 45f2975a1ba..19811d67c29 100644 --- a/app/constants/keyringTypes.ts +++ b/app/constants/keyringTypes.ts @@ -6,3 +6,8 @@ enum ExtendedKeyringTypes { } export default ExtendedKeyringTypes; + +export enum HardwareDeviceTypes { + LEDGER = 'Ledger', + QR = 'QR Hardware', +} diff --git a/app/core/Analytics/MetaMetrics.types.ts b/app/core/Analytics/MetaMetrics.types.ts index a7fbe2a1a07..8e8a59a5632 100644 --- a/app/core/Analytics/MetaMetrics.types.ts +++ b/app/core/Analytics/MetaMetrics.types.ts @@ -152,8 +152,3 @@ export interface IDeleteRegulationStatus { hasCollectedDataSinceDeletionRequest: boolean; dataDeletionRequestStatus: DataDeleteStatus; } - -export enum HardwareDeviceTypes { - LEDGER = 'Ledger', - QR = 'QR Hardware', -} From 2251d211bf600797529eb9b459be42f8b4efb6a4 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Fri, 12 Jul 2024 20:06:25 +0800 Subject: [PATCH 65/75] feat: Remove cancel button onPress event and also remove corresponding unit test for cancel button click. --- .../AccountActions/AccountActions.test.tsx | 34 ++----------------- .../Views/AccountActions/AccountActions.tsx | 5 ++- 2 files changed, 4 insertions(+), 35 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index bc37b75b629..87eaf816702 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -180,36 +180,6 @@ describe('AccountActions', () => { }); describe('clicks remove account', () => { - it('clicks cancel button after popup shows should do nothing', async () => { - const { getByTestId } = renderWithProvider(, { - state: initialState, - }); - - fireEvent.press( - getByTestId(AccountActionsModalSelectorsIDs.REMOVE_HARDWARE_ACCOUNT), - ); - - expect(Alert.alert).toHaveBeenCalled(); - - //Check Alert title and description match. - expect(Alert.alert.mock.calls[0][0]).toBe( - strings('accounts.remove_account_title'), - ); - expect(Alert.alert.mock.calls[0][1]).toBe( - strings('accounts.remove_account_alert_description'), - ); - - //Click cancel button - act(() => { - Alert.alert.mock.calls[0][2][0].onPress(); - }); - - //Check if removeAccount is not called - await waitFor(() => { - expect(mockKeyringController.removeAccount).not.toHaveBeenCalled(); - }); - }); - it('clicks remove button after popup shows to trigger the remove account process', async () => { mockKeyringController.getAccounts.mockResolvedValue([ '0xa1e359811322d97991e03f863a0c30c2cf029cd', @@ -230,10 +200,10 @@ describe('AccountActions', () => { //Check Alert title and description match. expect(Alert.alert.mock.calls[0][0]).toBe( - strings('accounts.remove_account_title'), + strings('accounts.remove_hardware_account'), ); expect(Alert.alert.mock.calls[0][1]).toBe( - strings('accounts.remove_account_alert_description'), + strings('accounts.remove_hw_account_alert_description'), ); //Click remove button diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index e8d617995bb..015c4f0127d 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -142,12 +142,11 @@ const AccountActions = () => { const showRemoveHWAlert = useCallback(() => { Alert.alert( - strings('accounts.remove_account_title'), - strings('accounts.remove_account_alert_description'), + strings('accounts.remove_hardware_account'), + strings('accounts.remove_hw_account_alert_description'), [ { text: strings('accounts.remove_account_alert_cancel_btn'), - onPress: () => false, style: 'cancel', }, { From 611d5a4ea58780c81fd9ce4e5bbbbdecb4a72a02 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Fri, 12 Jul 2024 20:44:43 +0800 Subject: [PATCH 66/75] feat: Revert the metric change for `HARDWARE_WALLET_FORGOTTEN` event. --- app/components/Views/AccountActions/AccountActions.tsx | 6 ++---- app/components/Views/LedgerSelectAccount/index.tsx | 2 +- app/core/Analytics/MetaMetrics.events.ts | 8 ++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 015c4f0127d..55049c9763b 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -210,15 +210,13 @@ const AccountActions = () => { switch (keyringType) { case ExtendedKeyringTypes.ledger: await forgetLedger(); - trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { + trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { device_type: HardwareDeviceTypes.LEDGER, }); break; case ExtendedKeyringTypes.qr: await controllers.KeyringController.forgetQRDevice(); - trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { - device_type: HardwareDeviceTypes.QR, - }); + // we dont have metrics for qr device forget yet. break; default: break; diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 78ec129325f..876d9f5cff2 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -114,7 +114,7 @@ const LedgerSelectAccount = () => { setBlockingModalVisible(true); await forgetLedger(); dispatch(setReloadAccounts(true)); - trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { + trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { device_type: HardwareDeviceTypes.LEDGER, }); setBlockingModalVisible(false); diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts index b203ecd7c73..f3bffdb3584 100644 --- a/app/core/Analytics/MetaMetrics.events.ts +++ b/app/core/Analytics/MetaMetrics.events.ts @@ -353,9 +353,7 @@ enum EVENT_NAME { CONNECT_LEDGER_SUCCESS = 'Connected Account with hardware wallet', LEDGER_HARDWARE_TRANSACTION_CANCELLED = 'User canceled Ledger hardware transaction', LEDGER_HARDWARE_WALLET_ERROR = 'Ledger hardware wallet error', - - // common hardware wallet - HARDWARE_WALLET_FORGOTTEN = 'Hardware wallet forgotten', + LEDGER_HARDWARE_WALLET_FORGOTTEN = 'Ledger hardware wallet forgotten', //Notifications ALL_NOTIFICATIONS = 'All Notifications', @@ -844,7 +842,9 @@ const events = { LEDGER_HARDWARE_WALLET_ERROR: generateOpt( EVENT_NAME.LEDGER_HARDWARE_WALLET_ERROR, ), - HARDWARE_WALLET_FORGOTTEN: generateOpt(EVENT_NAME.HARDWARE_WALLET_FORGOTTEN), + LEDGER_HARDWARE_WALLET_FORGOTTEN: generateOpt( + EVENT_NAME.LEDGER_HARDWARE_WALLET_FORGOTTEN, + ), // Smart transactions SMART_TRANSACTION_OPT_IN: generateOpt(EVENT_NAME.SMART_TRANSACTION_OPT_IN), From efefb63a04fac7e4c996619158cd8e4f1a8ed509 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Fri, 12 Jul 2024 22:14:24 +0800 Subject: [PATCH 67/75] feat: Change to use `selectSelectedInternalAccount` to get the selectedAccount which contain selectedAddress, and metadata --- .../AccountActions/AccountActions.test.tsx | 6 +- .../Views/AccountActions/AccountActions.tsx | 97 ++++++++++--------- 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index 87eaf816702..9b248dfbf2c 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -131,7 +131,7 @@ describe('AccountActions', () => { expect(mockNavigate).toHaveBeenCalledWith('Webview', { screen: 'SimpleWebview', params: { - url: 'https://etherscan.io/address/0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756', + url: 'https://etherscan.io/address/0xc4966c0d659d99699bfd7eb54d8fafee40e4a756', title: 'etherscan.io', }, }); @@ -145,9 +145,7 @@ describe('AccountActions', () => { fireEvent.press(getByTestId(AccountActionsModalSelectorsIDs.SHARE_ADDRESS)); expect(Share.open).toHaveBeenCalledWith({ - message: toChecksumHexAddress( - '0xC4966c0D659D99699BFD7EB54D8fafEE40e4a756', - ), + message: '0xc4966c0d659d99699bfd7eb54d8fafee40e4a756', }); }); diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index 55049c9763b..d2cef824f21 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -25,9 +25,8 @@ import { selectNetworkConfigurations, selectProviderConfig, } from '../../../selectors/networkController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController'; +import { selectSelectedInternalAccount } from '../../../selectors/accountsController'; import { strings } from '../../../../locales/i18n'; - // Internal dependencies import styleSheet from './AccountActions.styles'; import Logger from '../../../util/Logger'; @@ -35,7 +34,7 @@ import { protectWalletModalVisible } from '../../../actions/user'; import Routes from '../../../constants/navigation/Routes'; import { AccountActionsModalSelectorsIDs } from '../../../../e2e/selectors/Modals/AccountActionsModal.selectors'; import { useMetrics } from '../../../components/hooks/useMetrics'; -import { getKeyringByAddress, isHardwareAccount } from '../../../util/address'; +import { isHardwareAccount } from '../../../util/address'; import { removeAccountsFromPermissions } from '../../../core/Permissions'; import ExtendedKeyringTypes, { HardwareDeviceTypes, @@ -45,7 +44,6 @@ import Engine from '../../../core/Engine'; import BlockingActionModal from '../../UI/BlockingActionModal'; import { useTheme } from '../../../util/theme'; import { Hex } from '@metamask/utils'; -import { KeyringObject } from '@metamask/keyring-controller'; const AccountActions = () => { const { colors } = useTheme(); @@ -64,9 +62,10 @@ const AccountActions = () => { const providerConfig = useSelector(selectProviderConfig); - const selectedAddress = useSelector( - selectSelectedInternalAccountChecksummedAddress, - ); + const selectedAccount = useSelector(selectSelectedInternalAccount); + const selectedAddress = selectedAccount?.address; + const keyring = selectedAccount?.metadata.keyring; + const networkConfigurations = useSelector(selectNetworkConfigurations); const blockExplorer = useMemo(() => { @@ -164,17 +163,21 @@ const AccountActions = () => { * @param keyring - The keyring object * @param address - The address to remove */ - const removeHardwareAccount = useCallback( - async (keyring: KeyringObject, address: Hex) => { - await controllers.KeyringController.removeAccount(address); - await removeAccountsFromPermissions([address]); + const removeHardwareAccount = useCallback(async () => { + if (selectedAddress) { + await controllers.KeyringController.removeAccount(selectedAddress as Hex); + await removeAccountsFromPermissions([selectedAddress]); trackEvent(MetaMetricsEvents.WALLET_REMOVED, { accountType: keyring?.type, - address, + selectedAddress, }); - }, - [controllers.KeyringController, trackEvent], - ); + } + }, [ + controllers.KeyringController, + keyring?.type, + selectedAddress, + trackEvent, + ]); /** * Selects the first account after removing the previous selected account @@ -190,64 +193,62 @@ const AccountActions = () => { * Forget the device if there are no more accounts in the keyring * @param keyringType - The keyring type */ - const forgetDeviceIfRequired = useCallback( - async (keyringType: string) => { - // re-fetch the latest keyrings from KeyringController state. - const { keyrings } = controllers.KeyringController.state; - const updatedKeyring = keyrings.find((kr) => kr.type === keyringType); + const forgetDeviceIfRequired = useCallback(async () => { + // re-fetch the latest keyrings from KeyringController state. + const { keyrings } = controllers.KeyringController.state; + const keyringType = keyring?.type; + const updatedKeyring = keyrings.find((kr) => kr.type === keyringType); - // If there are no more accounts in the keyring, forget the device - let requestForgetDevice = false; + // If there are no more accounts in the keyring, forget the device + let requestForgetDevice = false; - if (updatedKeyring) { - if (updatedKeyring.accounts.length === 0) { - requestForgetDevice = true; - } - } else { + if (updatedKeyring) { + if (updatedKeyring.accounts.length === 0) { requestForgetDevice = true; } - if (requestForgetDevice) { - switch (keyringType) { - case ExtendedKeyringTypes.ledger: - await forgetLedger(); - trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { - device_type: HardwareDeviceTypes.LEDGER, - }); - break; - case ExtendedKeyringTypes.qr: - await controllers.KeyringController.forgetQRDevice(); - // we dont have metrics for qr device forget yet. - break; - default: - break; - } + } else { + requestForgetDevice = true; + } + if (requestForgetDevice) { + switch (keyringType) { + case ExtendedKeyringTypes.ledger: + await forgetLedger(); + trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { + device_type: HardwareDeviceTypes.LEDGER, + }); + break; + case ExtendedKeyringTypes.qr: + await controllers.KeyringController.forgetQRDevice(); + // we dont have metrics for qr device forget yet. + break; + default: + break; } - }, - [controllers.KeyringController, trackEvent], - ); + } + }, [controllers.KeyringController, keyring?.type, trackEvent]); /** * Trigger the remove hardware account action when user click on the remove account button */ const triggerRemoveHWAccount = useCallback(async () => { if (blockingModalVisible && selectedAddress) { - const keyring = getKeyringByAddress(selectedAddress); if (!keyring) { console.error('Keyring not found for address:', selectedAddress); return; } - await removeHardwareAccount(keyring, selectedAddress as Hex); + await removeHardwareAccount(); await selectFirstAccount(); - await forgetDeviceIfRequired(keyring.type); + await forgetDeviceIfRequired(); setBlockingModalVisible(false); } }, [ blockingModalVisible, forgetDeviceIfRequired, + keyring, removeHardwareAccount, selectFirstAccount, selectedAddress, From c182fc1f07c69bc9642f517b0b6a596dfc492675 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Sat, 13 Jul 2024 01:24:00 +0800 Subject: [PATCH 68/75] feat: Alex allow to use Hardware_wallet_forgotten metric event. --- app/components/Views/AccountActions/AccountActions.tsx | 6 ++++-- app/components/Views/LedgerSelectAccount/index.tsx | 2 +- app/core/Analytics/MetaMetrics.events.ts | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index d2cef824f21..ca584d4db50 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -213,13 +213,15 @@ const AccountActions = () => { switch (keyringType) { case ExtendedKeyringTypes.ledger: await forgetLedger(); - trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { + trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { device_type: HardwareDeviceTypes.LEDGER, }); break; case ExtendedKeyringTypes.qr: await controllers.KeyringController.forgetQRDevice(); - // we dont have metrics for qr device forget yet. + trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { + device_type: HardwareDeviceTypes.QR, + }); break; default: break; diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 876d9f5cff2..78ec129325f 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -114,7 +114,7 @@ const LedgerSelectAccount = () => { setBlockingModalVisible(true); await forgetLedger(); dispatch(setReloadAccounts(true)); - trackEvent(MetaMetricsEvents.LEDGER_HARDWARE_WALLET_FORGOTTEN, { + trackEvent(MetaMetricsEvents.HARDWARE_WALLET_FORGOTTEN, { device_type: HardwareDeviceTypes.LEDGER, }); setBlockingModalVisible(false); diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts index f3bffdb3584..b203ecd7c73 100644 --- a/app/core/Analytics/MetaMetrics.events.ts +++ b/app/core/Analytics/MetaMetrics.events.ts @@ -353,7 +353,9 @@ enum EVENT_NAME { CONNECT_LEDGER_SUCCESS = 'Connected Account with hardware wallet', LEDGER_HARDWARE_TRANSACTION_CANCELLED = 'User canceled Ledger hardware transaction', LEDGER_HARDWARE_WALLET_ERROR = 'Ledger hardware wallet error', - LEDGER_HARDWARE_WALLET_FORGOTTEN = 'Ledger hardware wallet forgotten', + + // common hardware wallet + HARDWARE_WALLET_FORGOTTEN = 'Hardware wallet forgotten', //Notifications ALL_NOTIFICATIONS = 'All Notifications', @@ -842,9 +844,7 @@ const events = { LEDGER_HARDWARE_WALLET_ERROR: generateOpt( EVENT_NAME.LEDGER_HARDWARE_WALLET_ERROR, ), - LEDGER_HARDWARE_WALLET_FORGOTTEN: generateOpt( - EVENT_NAME.LEDGER_HARDWARE_WALLET_FORGOTTEN, - ), + HARDWARE_WALLET_FORGOTTEN: generateOpt(EVENT_NAME.HARDWARE_WALLET_FORGOTTEN), // Smart transactions SMART_TRANSACTION_OPT_IN: generateOpt(EVENT_NAME.SMART_TRANSACTION_OPT_IN), From c403ad3ef8ddfc4ba0ac287c2f33b40cc874d8a2 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Sat, 13 Jul 2024 01:25:34 +0800 Subject: [PATCH 69/75] feat: fix the lint issue --- app/components/Views/AccountActions/AccountActions.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/components/Views/AccountActions/AccountActions.test.tsx b/app/components/Views/AccountActions/AccountActions.test.tsx index 9b248dfbf2c..b4b12204bee 100644 --- a/app/components/Views/AccountActions/AccountActions.test.tsx +++ b/app/components/Views/AccountActions/AccountActions.test.tsx @@ -14,7 +14,6 @@ import { AccountActionsModalSelectorsIDs } from '../../../../e2e/selectors/Modal import { backgroundState } from '../../../util/test/initial-root-state'; import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils'; -import { toChecksumHexAddress } from '@metamask/controller-utils'; import { strings } from '../../../../locales/i18n'; import { act } from '@testing-library/react-hooks'; From 368de0f427f19528a0f5f7f634adadf76be61a85 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Sat, 13 Jul 2024 01:40:52 +0800 Subject: [PATCH 70/75] feat: fix the yarn deduplicate --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 903b3b691fd..a5df03fbdf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3983,6 +3983,15 @@ "@metamask/safe-event-emitter" "^3.0.0" "@metamask/utils" "^8.3.0" +"@metamask/eth-json-rpc-provider@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-4.0.0.tgz#0ab54548fceda1829f313f2f8ec072ef91cce687" + integrity sha512-HB/I5eNsS67rE5C+px5zASyEuAoK/UFeWe4c4FIm2U4zMo7Y2EED1p10A4zHRHjpctObHdvNDcZQbfu2gHcqsQ== + dependencies: + "@metamask/json-rpc-engine" "^9.0.0" + "@metamask/safe-event-emitter" "^3.0.0" + "@metamask/utils" "^8.3.0" + "@metamask/eth-ledger-bridge-keyring@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@metamask/eth-ledger-bridge-keyring/-/eth-ledger-bridge-keyring-4.0.0.tgz#95e3ba575ea24c9059f4bec1ea418def450b3f55" @@ -3995,15 +4004,6 @@ "@metamask/eth-sig-util" "^7.0.1" hdkey "^2.1.0" -"@metamask/eth-json-rpc-provider@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-4.0.0.tgz#0ab54548fceda1829f313f2f8ec072ef91cce687" - integrity sha512-HB/I5eNsS67rE5C+px5zASyEuAoK/UFeWe4c4FIm2U4zMo7Y2EED1p10A4zHRHjpctObHdvNDcZQbfu2gHcqsQ== - dependencies: - "@metamask/json-rpc-engine" "^9.0.0" - "@metamask/safe-event-emitter" "^3.0.0" - "@metamask/utils" "^8.3.0" - "@metamask/eth-query@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-query/-/eth-query-3.0.1.tgz#3439eb6c7d5ccff1d6a66df1d1802bae0c890444" From c355be4f4b8390dd7eb350dd029f089b1c0b329d Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 15 Jul 2024 14:10:50 +0800 Subject: [PATCH 71/75] feat: upgrade the eth-ledger-keyring-bridge library, remove the patch for that library --- package.json | 2 +- ...mask+eth-ledger-bridge-keyring+4.0.0.patch | 20 ------------------- yarn.lock | 8 ++++---- 3 files changed, 5 insertions(+), 25 deletions(-) delete mode 100644 patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch diff --git a/package.json b/package.json index 2f2fb195f66..ff33efda1a6 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "@metamask/contract-metadata": "^2.1.0", "@metamask/controller-utils": "^10.0.0", "@metamask/design-tokens": "^4.0.0", - "@metamask/eth-ledger-bridge-keyring": "^4.0.0", + "@metamask/eth-ledger-bridge-keyring": "^4.1.0", "@metamask/eth-sig-util": "^7.0.2", "@metamask/etherscan-link": "^2.0.0", "@metamask/gas-fee-controller": "^18.0.0", diff --git a/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch b/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch deleted file mode 100644 index a28cde43391..00000000000 --- a/patches/@metamask+eth-ledger-bridge-keyring+4.0.0.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js -index b649ff2..49e3cad 100644 ---- a/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js -+++ b/node_modules/@metamask/eth-ledger-bridge-keyring/dist/ledger-transport-middleware.js -@@ -58,9 +58,12 @@ class LedgerTransportMiddleware { - * @returns An generic interface for communicating with a Ledger hardware wallet to perform operation. - */ - getEthApp() { -- if (!__classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f")) { -- __classPrivateFieldSet(this, _LedgerTransportMiddleware_app, new ledger_hw_app_1.MetaMaskLedgerHwAppEth(this.getTransport()), "f"); -- } -+ /** -+ * PATCH to fix the issue with iOS -+ * As the transport is set, we need to recreate the eth app instance otherwise iOS will throw disconnect error when we reconnect the device. -+ * we should remove this PATCH once the new release of library is available. -+ */ -+ __classPrivateFieldSet(this, _LedgerTransportMiddleware_app, new ledger_hw_app_1.MetaMaskLedgerHwAppEth(this.getTransport()), "f"); - return __classPrivateFieldGet(this, _LedgerTransportMiddleware_app, "f"); - } - } diff --git a/yarn.lock b/yarn.lock index a5df03fbdf8..8450848903d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3992,10 +3992,10 @@ "@metamask/safe-event-emitter" "^3.0.0" "@metamask/utils" "^8.3.0" -"@metamask/eth-ledger-bridge-keyring@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-ledger-bridge-keyring/-/eth-ledger-bridge-keyring-4.0.0.tgz#95e3ba575ea24c9059f4bec1ea418def450b3f55" - integrity sha512-37UaVFoC+51q/NnW7kUlg/3cZ8hsh0FXTuRyv+ZIg7EVZUW550tIBYyOGicVSCQhU6DvXi1k6zRCFDvwDAwjug== +"@metamask/eth-ledger-bridge-keyring@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-ledger-bridge-keyring/-/eth-ledger-bridge-keyring-4.1.0.tgz#90bb94b931ecba5c8ed9f0023b35f32f4ed8ac5a" + integrity sha512-ZNNV6zLwyEbzIAN8WHdTA372xst7/ajX/lvafbZDrSiiA+UuC0CfRSDOS+NOyCNnP+3NRBcJlo1ilDRYRe3ZZg== dependencies: "@ethereumjs/rlp" "^4.0.0" "@ethereumjs/tx" "^4.2.0" From fe4d92e4027157610dd9fa88c4478fe9a785a5cb Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 15 Jul 2024 18:00:49 +0800 Subject: [PATCH 72/75] feat: to make account selection screen bigger so that more items can be shown in the screen. --- app/components/UI/HardwareWallet/AccountSelector/styles.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/UI/HardwareWallet/AccountSelector/styles.tsx b/app/components/UI/HardwareWallet/AccountSelector/styles.tsx index 306d52a971e..9d8463fa895 100644 --- a/app/components/UI/HardwareWallet/AccountSelector/styles.tsx +++ b/app/components/UI/HardwareWallet/AccountSelector/styles.tsx @@ -50,7 +50,7 @@ export const createStyle = (colors: any) => bottom: { alignItems: 'center', justifyContent: 'space-between', - paddingTop: 70, + paddingTop: 30, paddingBottom: Device.isIphoneX() ? 20 : 10, }, button: { From 4ed3572551dfaa293b07c4db365d5e3ef375357e Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Mon, 15 Jul 2024 19:41:49 +0800 Subject: [PATCH 73/75] feat: Fix the remove account not log metric in mixpanel issue. --- .../Views/AccountActions/AccountActions.tsx | 2 +- app/core/Analytics/MetaMetrics.events.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/components/Views/AccountActions/AccountActions.tsx b/app/components/Views/AccountActions/AccountActions.tsx index ca584d4db50..22feb6a46b9 100644 --- a/app/components/Views/AccountActions/AccountActions.tsx +++ b/app/components/Views/AccountActions/AccountActions.tsx @@ -167,7 +167,7 @@ const AccountActions = () => { if (selectedAddress) { await controllers.KeyringController.removeAccount(selectedAddress as Hex); await removeAccountsFromPermissions([selectedAddress]); - trackEvent(MetaMetricsEvents.WALLET_REMOVED, { + trackEvent(MetaMetricsEvents.ACCOUNT_REMOVED, { accountType: keyring?.type, selectedAddress, }); diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts index b203ecd7c73..b45c55f0bc6 100644 --- a/app/core/Analytics/MetaMetrics.events.ts +++ b/app/core/Analytics/MetaMetrics.events.ts @@ -357,6 +357,9 @@ enum EVENT_NAME { // common hardware wallet HARDWARE_WALLET_FORGOTTEN = 'Hardware wallet forgotten', + // Remove an account + ACCOUNT_REMOVED = 'Account removed', + //Notifications ALL_NOTIFICATIONS = 'All Notifications', WALLET_NOTIFICATIONS = 'Wallet Notifications', @@ -846,6 +849,9 @@ const events = { ), HARDWARE_WALLET_FORGOTTEN: generateOpt(EVENT_NAME.HARDWARE_WALLET_FORGOTTEN), + // Remove an account + ACCOUNT_REMOVED: generateOpt(EVENT_NAME.ACCOUNT_REMOVED), + // Smart transactions SMART_TRANSACTION_OPT_IN: generateOpt(EVENT_NAME.SMART_TRANSACTION_OPT_IN), @@ -925,7 +931,7 @@ enum DESCRIPTION { WALLET_QR_SCANNER = 'QR scanner', WALLET_COPIED_ADDRESS = 'Copied Address', WALLET_ADD_COLLECTIBLES = 'Add Collectibles', - WALLET_REMOVED = 'Account removed', + // Transactions TRANSACTIONS_CONFIRM_STARTED = 'Confirm Started', TRANSACTIONS_EDIT_TRANSACTION = 'Edit Transaction', @@ -1177,11 +1183,7 @@ const legacyMetaMetricsEvents = { ACTIONS.WALLET_VIEW, DESCRIPTION.WALLET_ADD_COLLECTIBLES, ), - WALLET_REMOVED: generateOpt( - EVENT_NAME.WALLET_VIEW, - ACTIONS.WALLET_VIEW, - DESCRIPTION.WALLET_REMOVED, - ), + // Transactions TRANSACTIONS_CONFIRM_STARTED: generateOpt( EVENT_NAME.TRANSACTIONS, From c1a24b2c2ac04ea02690a271d6e5d9823d17acfd Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 16 Jul 2024 10:28:04 +0800 Subject: [PATCH 74/75] feat: Modify the AccountSelector component to make `onCheck` optional, and then modify `LedgerSelectAccount` component and `ConnectQRHardware` component to reflect the property changed. --- .../UI/HardwareWallet/AccountSelector/index.tsx | 11 +++++++---- app/components/Views/ConnectQRHardware/index.tsx | 6 +++--- app/components/Views/LedgerSelectAccount/index.tsx | 4 ---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/components/UI/HardwareWallet/AccountSelector/index.tsx b/app/components/UI/HardwareWallet/AccountSelector/index.tsx index ed5cc1a6725..ab2378c90c0 100644 --- a/app/components/UI/HardwareWallet/AccountSelector/index.tsx +++ b/app/components/UI/HardwareWallet/AccountSelector/index.tsx @@ -19,7 +19,7 @@ interface ISelectQRAccountsProps { selectedAccounts: string[]; nextPage: () => void; prevPage: () => void; - toggleAccount: (index: number) => void; + onCheck?: (index: number) => void; onUnlock: (accountIndex: number[]) => void; onForget: () => void; title: string; @@ -30,7 +30,7 @@ const AccountSelector = (props: ISelectQRAccountsProps) => { accounts, prevPage, nextPage, - toggleAccount, + onCheck, selectedAccounts, onForget, onUnlock, @@ -69,9 +69,12 @@ const AccountSelector = (props: ISelectQRAccountsProps) => { prev.has(index) ? prev.delete(index) : prev.add(index); return new Set(prev); }); - toggleAccount(index); + + if (onCheck) { + onCheck(index); + } }, - [toggleAccount], + [onCheck], ); return ( diff --git a/app/components/Views/ConnectQRHardware/index.tsx b/app/components/Views/ConnectQRHardware/index.tsx index 7bbbb5248e3..09507a2e0e1 100644 --- a/app/components/Views/ConnectQRHardware/index.tsx +++ b/app/components/Views/ConnectQRHardware/index.tsx @@ -275,7 +275,7 @@ const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { const prevPage = useCallback(async () => { resetError(); const [qrInteractions, connectQRHardwarePromise] = - await initiateQRHardwareConnection(1); + await initiateQRHardwareConnection(-1); qrInteractionsRef.current = qrInteractions; const previousPageAccounts = await connectQRHardwarePromise; @@ -284,7 +284,7 @@ const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { setAccounts(previousPageAccounts); }, [resetError]); - const onToggle = useCallback(() => { + const onCheck = useCallback(() => { resetError(); }, [resetError]); @@ -354,7 +354,7 @@ const ConnectQRHardware = ({ navigation }: IConnectQRHardwareProps) => { selectedAccounts={existingAccounts} nextPage={nextPage} prevPage={prevPage} - toggleAccount={onToggle} + onCheck={onCheck} onUnlock={onUnlock} onForget={onForget} title={strings('connect_qr_hardware.select_accounts')} diff --git a/app/components/Views/LedgerSelectAccount/index.tsx b/app/components/Views/LedgerSelectAccount/index.tsx index 78ec129325f..211b1dc8578 100644 --- a/app/components/Views/LedgerSelectAccount/index.tsx +++ b/app/components/Views/LedgerSelectAccount/index.tsx @@ -86,9 +86,6 @@ const LedgerSelectAccount = () => { setAccounts(_accounts); }, []); - // eslint-disable-next-line @typescript-eslint/no-empty-function - const onToggle = useCallback(() => {}, []); - const onUnlock = useCallback( async (accountIndexes: number[]) => { setBlockingModalVisible(true); @@ -165,7 +162,6 @@ const LedgerSelectAccount = () => { selectedAccounts={existingAccounts} nextPage={nextPage} prevPage={prevPage} - toggleAccount={onToggle} onUnlock={(accountIndex: number[]) => { setUnlockAccounts({ trigger: true, accountIndexes: accountIndex }); setBlockingModalVisible(true); From df0bd74d77787f392cf737bb4fc9e86f434c5972 Mon Sep 17 00:00:00 2001 From: Xiaoming Wang Date: Tue, 16 Jul 2024 20:00:15 +0800 Subject: [PATCH 75/75] feat: Change the structure of `LedgerSelectAccount` and `LedgerConnect` component to include a close icon, which will minic how the `ConnectQRHardware` component structure. hide the navigation header for `ledger Connect` screen. --- app/components/Nav/App/index.js | 7 +- .../__snapshots__/index.test.tsx.snap | 66 +++++++-- .../Views/LedgerConnect/index.styles.ts | 94 +++++++++++++ .../Views/LedgerConnect/index.test.tsx | 2 + app/components/Views/LedgerConnect/index.tsx | 109 +++------------ .../__snapshots__/index.test.tsx.snap | 132 +++++++++++++++--- .../Views/LedgerSelectAccount/index.tsx | 6 +- 7 files changed, 297 insertions(+), 119 deletions(-) create mode 100644 app/components/Views/LedgerConnect/index.styles.ts diff --git a/app/components/Nav/App/index.js b/app/components/Nav/App/index.js index f84e9ef6db5..04af644e3f6 100644 --- a/app/components/Nav/App/index.js +++ b/app/components/Nav/App/index.js @@ -741,7 +741,12 @@ const App = ({ userLoggedIn }) => { ); const LedgerConnectFlow = () => ( - + - + > + + + +  + + + + StyleSheet.create({ + container: { + position: 'relative', + flex: 1, + backgroundColor: colors.background.default, + alignItems: 'center', + }, + connectLedgerWrapper: { + marginLeft: Device.getDeviceWidth() * 0.07, + marginRight: Device.getDeviceWidth() * 0.07, + }, + header: { + marginTop: Device.isIphoneX() ? 30 : 20, + flexDirection: 'row', + width: '100%', + alignItems: 'center', + }, + navbarRightButton: { + flexDirection: 'row', + justifyContent: 'flex-end', + height: 48, + width: 48, + flex: 1, + }, + closeIcon: { + fontSize: 28, + color: colors.text.default, + }, + ledgerImage: { + width: 68, + height: 68, + }, + coverImage: { + resizeMode: 'contain', + width: Device.getDeviceWidth() * 0.6, + height: 64, + overflow: 'visible', + }, + connectLedgerText: { + ...(fontStyles.normal as TextStyle), + fontSize: 24, + }, + bodyContainer: { + flex: 1, + marginTop: Device.getDeviceHeight() * 0.025, + }, + textContainer: { + marginTop: Device.getDeviceHeight() * 0.05, + }, + + instructionsText: { + marginTop: Device.getDeviceHeight() * 0.02, + }, + imageContainer: { + alignItems: 'center', + marginTop: Device.getDeviceHeight() * 0.08, + }, + buttonContainer: { + position: 'absolute', + display: 'flex', + bottom: Device.getDeviceHeight() * 0.025, + left: 0, + width: '100%', + }, + lookingForDeviceContainer: { + flexDirection: 'row', + }, + lookingForDeviceText: { + fontSize: 18, + }, + activityIndicatorStyle: { + marginLeft: 10, + }, + ledgerInstructionText: { + paddingLeft: 7, + }, + howToInstallEthAppText: { + marginTop: Device.getDeviceHeight() * 0.025, + }, + openEthAppMessage: { + marginTop: Device.getDeviceHeight() * 0.025, + }, + loader: { + color: colors.background.default, + }, + }); + +export default createStyles; diff --git a/app/components/Views/LedgerConnect/index.test.tsx b/app/components/Views/LedgerConnect/index.test.tsx index f2e6a5deb8c..80f0ca8c344 100644 --- a/app/components/Views/LedgerConnect/index.test.tsx +++ b/app/components/Views/LedgerConnect/index.test.tsx @@ -26,6 +26,7 @@ jest.mock('../../../util/device', () => ({ ...jest.requireActual('../../../util/device'), isAndroid: jest.fn(), isIos: jest.fn(), + isIphoneX: jest.fn(), getDeviceWidth: jest.fn(), getDeviceHeight: jest.fn(), })); @@ -125,6 +126,7 @@ describe('LedgerConnect', () => { getSystemVersion.mockReturnValue('13'); Device.isAndroid.mockReturnValue(true); Device.isIos.mockReturnValue(false); + Device.isIphoneX.mockReturnValue(false); Device.getDeviceWidth.mockReturnValue(50); Device.getDeviceHeight.mockReturnValue(50); }); diff --git a/app/components/Views/LedgerConnect/index.tsx b/app/components/Views/LedgerConnect/index.tsx index fa951fdfb51..166061d09d9 100644 --- a/app/components/Views/LedgerConnect/index.tsx +++ b/app/components/Views/LedgerConnect/index.tsx @@ -1,11 +1,10 @@ import React, { useEffect, useMemo, useState } from 'react'; import { - View, - StyleSheet, + ActivityIndicator, Image, SafeAreaView, - TextStyle, - ActivityIndicator, + TouchableOpacity, + View, } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { Device as NanoDevice } from '@ledgerhq/react-native-hw-transport-ble/lib/types'; @@ -19,7 +18,6 @@ import { useAssetFromTheme, } from '../../../util/theme'; import Device from '../../../util/device'; -import { fontStyles } from '../../../styles/common'; import Scan from './Scan'; import useLedgerBluetooth from '../../hooks/Ledger/useLedgerBluetooth'; import { showSimpleNotification } from '../../../actions/notification'; @@ -35,79 +33,8 @@ import ledgerConnectLightImage from '../../../images/ledger-connect-light.png'; import ledgerConnectDarkImage from '../../../images/ledger-connect-dark.png'; import { getSystemVersion } from 'react-native-device-info'; import { LedgerCommunicationErrors } from '../../../core/Ledger/ledgerErrors'; - -// TODO: Replace "any" with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const createStyles = (theme: any) => - StyleSheet.create({ - container: { - position: 'relative', - flex: 1, - backgroundColor: theme.colors.background.default, - alignItems: 'center', - }, - connectLedgerWrapper: { - marginLeft: Device.getDeviceWidth() * 0.07, - marginRight: Device.getDeviceWidth() * 0.07, - }, - ledgerImage: { - width: 68, - height: 68, - }, - coverImage: { - resizeMode: 'contain', - width: Device.getDeviceWidth() * 0.6, - height: 64, - overflow: 'visible', - }, - connectLedgerText: { - ...(fontStyles.normal as TextStyle), - fontSize: 24, - }, - bodyContainer: { - flex: 1, - marginTop: Device.getDeviceHeight() * 0.025, - }, - textContainer: { - marginTop: Device.getDeviceHeight() * 0.05, - }, - - instructionsText: { - marginTop: Device.getDeviceHeight() * 0.02, - }, - imageContainer: { - alignItems: 'center', - marginTop: Device.getDeviceHeight() * 0.08, - }, - buttonContainer: { - position: 'absolute', - display: 'flex', - bottom: Device.getDeviceHeight() * 0.025, - left: 0, - width: '100%', - }, - lookingForDeviceContainer: { - flexDirection: 'row', - }, - lookingForDeviceText: { - fontSize: 18, - }, - activityIndicatorStyle: { - marginLeft: 10, - }, - ledgerInstructionText: { - paddingLeft: 7, - }, - howToInstallEthAppText: { - marginTop: Device.getDeviceHeight() * 0.025, - }, - openEthAppMessage: { - marginTop: Device.getDeviceHeight() * 0.025, - }, - loader: { - color: theme.brandColors.white, - }, - }); +import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; +import createStyles from './index.styles'; interface LedgerConnectProps { onConnectLedger: () => void; @@ -116,7 +43,7 @@ interface LedgerConnectProps { const LedgerConnect = ({ onConnectLedger }: LedgerConnectProps) => { const theme = useAppThemeFromContext() ?? mockTheme; const navigation = useNavigation(); - const styles = useMemo(() => createStyles(theme), [theme]); + const styles = useMemo(() => createStyles(theme.colors), [theme]); const [selectedDevice, setSelectedDevice] = useState(null); const [errorDetail, setErrorDetails] = useState(); const [loading, setLoading] = useState(false); @@ -242,14 +169,22 @@ const LedgerConnect = ({ onConnectLedger }: LedgerConnectProps) => { return ( - + + + + + + {strings('ledger.connect_ledger')} diff --git a/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap b/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap index 53a675fbf00..be0f72c7ffc 100644 --- a/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap +++ b/app/components/Views/LedgerSelectAccount/__snapshots__/index.test.tsx.snap @@ -19,20 +19,66 @@ exports[`LedgerSelectAccount renders correctly to match snapshot 1`] = ` } } > - + > + + + +  + + + - + > + + + +  + + + { const navigation = useNavigation>(); @@ -152,10 +153,13 @@ const LedgerSelectAccount = () => { resizeMode="contain" style={styles.ledgerIcon} /> + + > + +