From 7347b597fab1424df0bf07085fdf7e8651da103c Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Wed, 2 Nov 2022 15:05:41 +0100 Subject: [PATCH 01/15] fix: render an informative error on 0x5515 on LLM --- .../src/components/DeviceAction/index.tsx | 1 + .../src/components/DeviceAction/rendering.tsx | 79 ++++++++++++++++++- .../src/locales/en/common.json | 5 ++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/apps/ledger-live-mobile/src/components/DeviceAction/index.tsx b/apps/ledger-live-mobile/src/components/DeviceAction/index.tsx index fc6734b8b2a..d45c8dd9b0d 100644 --- a/apps/ledger-live-mobile/src/components/DeviceAction/index.tsx +++ b/apps/ledger-live-mobile/src/components/DeviceAction/index.tsx @@ -446,6 +446,7 @@ export function DeviceActionDefaultRendering({ onRetry, colors, theme, + device: device ?? undefined, }); } diff --git a/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx b/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx index c2cd5905c4c..8a2f8f4130c 100644 --- a/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx +++ b/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx @@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react"; import { Platform, ScrollView } from "react-native"; import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components/native"; -import { WrongDeviceForAccount } from "@ledgerhq/errors"; +import { + StatusCodes, + TransportStatusError, + WrongDeviceForAccount, +} from "@ledgerhq/errors"; import { TokenCurrency } from "@ledgerhq/types-cryptoassets"; import { Transaction } from "@ledgerhq/live-common/generated/types"; import { getDeviceModel } from "@ledgerhq/devices"; @@ -19,6 +23,7 @@ import { Log, BoxedIcon, } from "@ledgerhq/native-ui"; +import { LockAltMedium } from "@ledgerhq/native-ui/assets/icons"; import BigNumber from "bignumber.js"; import { ExchangeRate, @@ -535,6 +540,65 @@ export function renderInWrongAppForAccount({ }); } +// Quick fix: the error TransportStatusError with status code LOCKED_DEVICE should be catched +// inside all the device actions and mapped to an event of type "lockedDevice". +// With this fix, we can catch all the error that were not catched upstream. +export function renderLockedDeviceError({ + t, + onRetry, + device, +}: RawProps & { + onRetry?: (() => void) | null; + device?: Device; +}) { + const productName = device + ? getDeviceModel(device.modelId).productName + : null; + + return ( + + + + + + + + {t("errors.LockedDeviceError.title")} + + + {productName + ? t("errors.LockedDeviceError.descriptionWithProductName", { + productName, + }) + : t("errors.LockedDeviceError.description")} + + {onRetry ? ( + + + + ) : null} + + + ); +} + export function renderError({ t, error, @@ -543,6 +607,7 @@ export function renderError({ navigation, Icon, iconColor, + device, }: RawProps & { navigation?: StackNavigationProp; error: Error; @@ -550,6 +615,7 @@ export function renderError({ managerAppName?: string; Icon?: React.ComponentProps["Icon"]; iconColor?: string; + device?: Device; }) { const onPress = () => { if (managerAppName && navigation) { @@ -564,6 +630,17 @@ export function renderError({ onRetry(); } }; + + // Redirects from renderError and not from DeviceActionDefaultRendering because renderError + // can be used directly by other component + if ( + error instanceof TransportStatusError && + (error as unknown as { statusCode: number })?.statusCode === + StatusCodes.LOCKED_DEVICE + ) { + return renderLockedDeviceError({ t, onRetry, device }); + } + return ( Date: Wed, 2 Nov 2022 15:06:07 +0100 Subject: [PATCH 02/15] chore: device object in renderError --- .../src/components/CustomImageDeviceAction.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/ledger-live-mobile/src/components/CustomImageDeviceAction.tsx b/apps/ledger-live-mobile/src/components/CustomImageDeviceAction.tsx index 0d7633753e9..a23a8a6e1a1 100644 --- a/apps/ledger-live-mobile/src/components/CustomImageDeviceAction.tsx +++ b/apps/ledger-live-mobile/src/components/CustomImageDeviceAction.tsx @@ -82,6 +82,7 @@ const CustomImageDeviceAction: React.FC void }> = ({ {renderError({ t, error, + device, ...(isRefusedOnStaxError ? { Icon: Icons.CircledAlertMedium, iconColor: "warning.c100" } : {}), From 6a6fa27c6ef2412731174661ecb9656f9c2e0782 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 08:49:51 +0100 Subject: [PATCH 03/15] chore: add translation and device as arg to renderError --- .../CustomImage/CustomImageDeviceAction.tsx | 2 ++ .../components/DeviceAction/index.tsx | 7 +++++ .../components/DeviceAction/rendering.tsx | 6 +++++ .../src/renderer/components/ErrorDisplay.js | 23 ---------------- .../src/renderer/components/ErrorDisplay.tsx | 26 +++++++++++++++++++ .../modals/SellDeviceConfirm/index.jsx | 3 +++ 6 files changed, 44 insertions(+), 23 deletions(-) delete mode 100644 apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.js create mode 100644 apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.tsx diff --git a/apps/ledger-live-desktop/src/renderer/components/CustomImage/CustomImageDeviceAction.tsx b/apps/ledger-live-desktop/src/renderer/components/CustomImage/CustomImageDeviceAction.tsx index fd0ab093092..1e9a45dffc0 100644 --- a/apps/ledger-live-desktop/src/renderer/components/CustomImage/CustomImageDeviceAction.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/CustomImage/CustomImageDeviceAction.tsx @@ -96,7 +96,9 @@ const CustomImageDeviceAction: React.FC = withRemountableWrapper(props => ) : isError ? ( {renderError({ + t, error, + device: device ?? undefined, ...(isRefusedOnStaxError ? { Icon: Icons.CircledAlertMedium, iconColor: "warning.c100" } : {}), diff --git a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/index.tsx b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/index.tsx index 52286421f54..7ac0027901c 100644 --- a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/index.tsx @@ -241,6 +241,7 @@ export const DeviceActionDefaultRendering = ({ if (inWrongDeviceForAccount) { return renderInWrongAppForAccount({ + t, onRetry, accountName: inWrongDeviceForAccount.accountName, }); @@ -253,6 +254,7 @@ export const DeviceActionDefaultRendering = ({ error instanceof UpdateYourApp ) { return renderError({ + t, error, managerAppName: error.managerAppName, }); @@ -260,6 +262,7 @@ export const DeviceActionDefaultRendering = ({ if (error instanceof LatestFirmwareVersionRequired) { return renderError({ + t, error, requireFirmwareUpdate: true, }); @@ -272,6 +275,7 @@ export const DeviceActionDefaultRendering = ({ (error instanceof TransportStatusError && error.message.includes("0x6d06")) ) { return renderError({ + t, error: new DeviceNotOnboarded(), withOnboardingCTA: true, info: true, @@ -280,6 +284,7 @@ export const DeviceActionDefaultRendering = ({ if (error instanceof NoSuchAppOnProvider) { return renderError({ + t, error, withOpenManager: true, withExportLogs: true, @@ -287,9 +292,11 @@ export const DeviceActionDefaultRendering = ({ } return renderError({ + t, error, onRetry, withExportLogs: true, + device: device ?? undefined, }); } diff --git a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx index 1009498029a..7aabbc96ea0 100644 --- a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx @@ -485,6 +485,7 @@ export const renderWarningOutdated = ({ export const renderError = ({ error, + t, withOpenManager, onRetry, withExportLogs, @@ -495,8 +496,10 @@ export const renderError = ({ managerAppName, requireFirmwareUpdate, withOnboardingCTA, + device, }: { error: Error; + t: TFunction; withOpenManager?: boolean; onRetry?: () => void; withExportLogs?: boolean; @@ -561,13 +564,16 @@ export const renderError = ({ ); export const renderInWrongAppForAccount = ({ + t, onRetry, accountName, }: { + t: TFunction; onRetry: () => void; accountName: string; }) => renderError({ + t, error: new WrongDeviceForAccount(null, { accountName }), withExportLogs: true, onRetry, diff --git a/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.js b/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.js deleted file mode 100644 index 628124d291c..00000000000 --- a/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.js +++ /dev/null @@ -1,23 +0,0 @@ -// @flow -import { renderError } from "~/renderer/components/DeviceAction/rendering"; - -export type ErrorDisplayProps = { - error: Error, - onRetry?: () => void, - withExportLogs?: boolean, - list?: boolean, - supportLink?: string, - warning?: boolean, -}; - -const ErrorDisplay = ({ - error, - onRetry, - withExportLogs, - list, - supportLink, - warning, -}: ErrorDisplayProps) => - renderError({ error, onRetry, withExportLogs, list, supportLink, warning }); - -export default ErrorDisplay; diff --git a/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.tsx b/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.tsx new file mode 100644 index 00000000000..ed3dbfe9f2e --- /dev/null +++ b/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.tsx @@ -0,0 +1,26 @@ +import { useTranslation } from "react-i18next"; +import { renderError } from "~/renderer/components/DeviceAction/rendering"; + +export type ErrorDisplayProps = { + error: Error; + onRetry?: () => void; + withExportLogs?: boolean; + list?: boolean; + supportLink?: string; + warning?: boolean; +}; + +const ErrorDisplay = ({ + error, + onRetry, + withExportLogs, + list, + supportLink, + warning, +}: ErrorDisplayProps) => { + const { t } = useTranslation(); + + renderError({ t, error, onRetry, withExportLogs, list, supportLink, warning }); +}; + +export default ErrorDisplay; diff --git a/apps/ledger-live-desktop/src/renderer/modals/SellDeviceConfirm/index.jsx b/apps/ledger-live-desktop/src/renderer/modals/SellDeviceConfirm/index.jsx index 4a8e5d874cd..4b2d6c929b8 100644 --- a/apps/ledger-live-desktop/src/renderer/modals/SellDeviceConfirm/index.jsx +++ b/apps/ledger-live-desktop/src/renderer/modals/SellDeviceConfirm/index.jsx @@ -61,6 +61,8 @@ const Result = ({ const Root = ({ data, onClose }: Props) => { const { account, parentAccount, getCoinifyContext, onResult, onCancel } = data; + const { t } = useTranslation(); + const tokenCurrency = account && account.type === "TokenAccount" && account.token; // state @@ -138,6 +140,7 @@ const Root = ({ data, onClose }: Props) => { return ( {renderError({ + t, error, })} From 93cd3934d03dafaced890ae0cbb17b5510efae57 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 08:50:37 +0100 Subject: [PATCH 04/15] fix: render an infromative error on 0x5515 on LLD --- .../components/DeviceAction/rendering.tsx | 160 ++++++++++++------ .../static/i18n/en/app.json | 5 + 2 files changed, 113 insertions(+), 52 deletions(-) diff --git a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx index 7aabbc96ea0..4dae2d8da20 100644 --- a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx @@ -9,7 +9,12 @@ import { TokenCurrency } from "@ledgerhq/types-cryptoassets"; import { Transaction, TransactionStatus } from "@ledgerhq/live-common/generated/types"; import { ExchangeRate, Exchange } from "@ledgerhq/live-common/exchange/swap/types"; import { getProviderName } from "@ledgerhq/live-common/exchange/swap/utils/index"; -import { WrongDeviceForAccount, UpdateYourApp } from "@ledgerhq/errors"; +import { + WrongDeviceForAccount, + UpdateYourApp, + TransportStatusError, + StatusCodes, +} from "@ledgerhq/errors"; import { LatestFirmwareVersionRequired } from "@ledgerhq/live-common/errors"; import { DeviceModelId, getDeviceModel } from "@ledgerhq/devices"; import { Device } from "@ledgerhq/live-common/hw/actions/types"; @@ -47,7 +52,8 @@ import { SWAP_VERSION } from "~/renderer/screens/exchange/Swap2/utils/index"; import { context } from "~/renderer/drawers/Provider"; import { track } from "~/renderer/analytics/segment"; import { DrawerFooter } from "~/renderer/screens/exchange/Swap2/Form/DrawerFooter"; -import { Flex, Icons, Text as TextV3, Log, ProgressLoader } from "@ledgerhq/react-ui"; +import { Flex, Icons, Text as TextV3, Log, ProgressLoader, BoxedIcon } from "@ledgerhq/react-ui"; +import { LockAltMedium } from "@ledgerhq/react-ui/assets/icons"; import { withV3StyleProvider } from "~/renderer/styles/StyleProviderV3"; import FramedImage from "../CustomImage/FramedImage"; @@ -483,6 +489,44 @@ export const renderWarningOutdated = ({ ); +// Quick fix: the error TransportStatusError with status code LOCKED_DEVICE should be catched +// inside all the device actions and mapped to an event of type "lockedDevice". +// With this fix, we can catch all the error that were not catched upstream. +export const renderLockedDeviceError = ({ + t, + device, + onRetry, +}: { + t: TFunction; + device?: Device; + onRetry?: () => void; +}) => { + const productName = device ? getDeviceModel(device.modelId).productName : null; + + return ( + + + + + {t("errors.LockedDeviceError.title")} + + {productName + ? t("errors.LockedDeviceError.descriptionWithProductName", { + productName, + }) + : t("errors.LockedDeviceError.description")} + + + {onRetry ? ( + + ) : null} + + + ); +}; + export const renderError = ({ error, t, @@ -510,58 +554,70 @@ export const renderError = ({ managerAppName?: string; requireFirmwareUpdate?: boolean; withOnboardingCTA?: boolean; -}) => ( - - - - - - - - - - - {list ? ( + device?: Device; +}) => { + // Redirects from renderError and not from DeviceActionDefaultRendering because renderError + // can be used directly by other component + if ( + error instanceof TransportStatusError && + ((error as unknown) as { statusCode: number })?.statusCode === StatusCodes.LOCKED_DEVICE + ) { + return renderLockedDeviceError({ t, onRetry, device }); + } + + return ( + + + + + + + -
    - -
+
- ) : null} - - {managerAppName || requireFirmwareUpdate ? ( - - ) : ( - <> - {supportLink ? ( - } url={supportLink} /> - ) : null} - {withExportLogs ? ( - } - small={false} - primary={false} - outlineGrey - mx={1} - /> - ) : null} - {withOpenManager ? ( - - ) : onRetry ? ( - - ) : null} - {withOnboardingCTA ? : null} - - )} - -
-); + {list ? ( + +
    + +
+
+ ) : null} + + {managerAppName || requireFirmwareUpdate ? ( + + ) : ( + <> + {supportLink ? ( + } url={supportLink} /> + ) : null} + {withExportLogs ? ( + } + small={false} + primary={false} + outlineGrey + mx={1} + /> + ) : null} + {withOpenManager ? ( + + ) : onRetry ? ( + + ) : null} + {withOnboardingCTA ? : null} + + )} + +
+ ); +}; export const renderInWrongAppForAccount = ({ t, diff --git a/apps/ledger-live-desktop/static/i18n/en/app.json b/apps/ledger-live-desktop/static/i18n/en/app.json index 2ddc2c28cf8..80e071160c3 100644 --- a/apps/ledger-live-desktop/static/i18n/en/app.json +++ b/apps/ledger-live-desktop/static/i18n/en/app.json @@ -5309,6 +5309,11 @@ "ImageLoadRefusedOnDevice": { "title": "Image load refused on device (TODO: final wording)", "description": "If this image wasn't the right fit, you can try again with another image." + }, + "LockedDeviceError": { + "title": "Your device is locked", + "description": "Unlock your device and try again.", + "descriptionWithProductName": "Unlock your {{ productName }} and try again." } }, "cryptoOrg": { From adbbf5617ab7aee11c38800824ac1c02e9ca12f1 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 15:30:41 +0100 Subject: [PATCH 05/15] fix: maps 5515 to specific error LockedDeviceError --- libs/ledgerjs/packages/errors/src/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libs/ledgerjs/packages/errors/src/index.ts b/libs/ledgerjs/packages/errors/src/index.ts index 688f1ad53ab..d13b2f2e143 100644 --- a/libs/ledgerjs/packages/errors/src/index.ts +++ b/libs/ledgerjs/packages/errors/src/index.ts @@ -52,6 +52,7 @@ export const DeviceSocketFail = createCustomErrorClass("DeviceSocketFail"); export const DeviceSocketNoBulkStatus = createCustomErrorClass( "DeviceSocketNoBulkStatus" ); +export const LockedDeviceError = createCustomErrorClass("LockedDeviceError"); export const DisconnectedDevice = createCustomErrorClass("DisconnectedDevice"); export const DisconnectedDeviceDuringOperation = createCustomErrorClass( "DisconnectedDeviceDuringOperation" @@ -319,13 +320,20 @@ export function getAltStatusMessage(code: number): string | undefined | null { * the error.statusCode is one of the `StatusCodes` exported by this library. */ export function TransportStatusError(statusCode: number): void { - this.name = "TransportStatusError"; const statusText = Object.keys(StatusCodes).find((k) => StatusCodes[k] === statusCode) || "UNKNOWN_ERROR"; const smsg = getAltStatusMessage(statusCode) || statusText; const statusCodeStr = statusCode.toString(16); - this.message = `Ledger device: ${smsg} (0x${statusCodeStr})`; + const message = `Ledger device: ${smsg} (0x${statusCodeStr})`; + + // Maps to a LockedDeviceError + if (statusCode === StatusCodes.LOCKED_DEVICE) { + throw new LockedDeviceError(message); + } + + this.name = "TransportStatusError"; + this.message = message; this.stack = new Error().stack; this.statusCode = statusCode; this.statusText = statusText; From 88397b23ac0fe8bca46515866c44bcbbda25e88d Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 15:31:31 +0100 Subject: [PATCH 06/15] fix: connectApp and connectManager catches LockedDeviceError --- libs/ledger-live-common/src/hw/connectApp.ts | 31 ++++++++++--------- .../src/hw/connectManager.ts | 8 ++--- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/libs/ledger-live-common/src/hw/connectApp.ts b/libs/ledger-live-common/src/hw/connectApp.ts index 35bf0159d2d..38a828090b6 100644 --- a/libs/ledger-live-common/src/hw/connectApp.ts +++ b/libs/ledger-live-common/src/hw/connectApp.ts @@ -10,6 +10,7 @@ import { DisconnectedDeviceDuringOperation, DisconnectedDevice, StatusCodes, + LockedDeviceError, } from "@ledgerhq/errors"; import type Transport from "@ledgerhq/hw-transport"; import type { DeviceModelId } from "@ledgerhq/devices"; @@ -161,12 +162,12 @@ export const openAppFromDashboard = ( case StatusCodes.CONDITIONS_OF_USE_NOT_SATISFIED: case 0x5501: // No StatusCodes definition return throwError(new UserRefusedOnDevice()); - // openAppFromDashboard is exported, so LOCKED_DEVICE should be handled too - case StatusCodes.LOCKED_DEVICE: - return of({ - type: "lockedDevice", - } as ConnectAppEvent); } + } else if (e instanceof LockedDeviceError) { + // openAppFromDashboard is exported, so LockedDeviceError should be handled here too + return of({ + type: "lockedDevice", + } as ConnectAppEvent); } return throwError(e); @@ -253,13 +254,13 @@ const derivationLogic = ( case StatusCodes.INS_NOT_SUPPORTED: // this is likely because it's the wrong app (LNS 1.3.1) return attemptToQuitApp(transport, appAndVersion); - // derivationLogic is also called inside the catchError of cmd below - // so it needs to handle LOCKED_DEVICE - case StatusCodes.LOCKED_DEVICE: - return of({ - type: "lockedDevice", - } as ConnectAppEvent); } + } else if (e instanceof LockedDeviceError) { + // derivationLogic is also called inside the catchError of cmd below + // so it needs to handle LockedDeviceError too + return of({ + type: "lockedDevice", + } as ConnectAppEvent); } return throwError(e); @@ -457,11 +458,11 @@ const cmd = ({ requiresDerivation, appName, }); - case StatusCodes.LOCKED_DEVICE: - return of({ - type: "lockedDevice", - } as ConnectAppEvent); } + } else if (e instanceof LockedDeviceError) { + return of({ + type: "lockedDevice", + } as ConnectAppEvent); } return throwError(e); diff --git a/libs/ledger-live-common/src/hw/connectManager.ts b/libs/ledger-live-common/src/hw/connectManager.ts index 48b1ae54a08..796bd99d2e3 100644 --- a/libs/ledger-live-common/src/hw/connectManager.ts +++ b/libs/ledger-live-common/src/hw/connectManager.ts @@ -4,6 +4,7 @@ import { TransportStatusError, DeviceOnDashboardExpected, StatusCodes, + LockedDeviceError, } from "@ledgerhq/errors"; import { DeviceInfo } from "@ledgerhq/types-live"; import type { ListAppsEvent } from "../apps"; @@ -88,12 +89,7 @@ const cmd = ({ ); }), catchError((e: unknown) => { - if ( - e && - e instanceof TransportStatusError && - // @ts-expect-error typescript not checking agains the instanceof - e.statusCode === StatusCodes.LOCKED_DEVICE - ) { + if (e instanceof LockedDeviceError) { return of({ type: "lockedDevice", } as ConnectManagerEvent); From 43d24a564d4f9eac80398669d4ed46ef7168d869 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 15:33:21 +0100 Subject: [PATCH 07/15] fix: update usage of LockedDeviceError --- .../src/hw/getDeviceRunningMode.test.ts | 17 ++++++++--------- .../src/hw/getDeviceRunningMode.ts | 14 ++------------ 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/libs/ledger-live-common/src/hw/getDeviceRunningMode.test.ts b/libs/ledger-live-common/src/hw/getDeviceRunningMode.test.ts index 704f239121b..d00995916e7 100644 --- a/libs/ledger-live-common/src/hw/getDeviceRunningMode.test.ts +++ b/libs/ledger-live-common/src/hw/getDeviceRunningMode.test.ts @@ -1,10 +1,11 @@ import { from, Observable, of, timer } from "rxjs"; import { delay } from "rxjs/operators"; -import Transport, { - StatusCodes, - TransportStatusError, -} from "@ledgerhq/hw-transport"; -import { CantOpenDevice, DisconnectedDevice } from "@ledgerhq/errors"; +import Transport from "@ledgerhq/hw-transport"; +import { + CantOpenDevice, + DisconnectedDevice, + LockedDeviceError, +} from "@ledgerhq/errors"; import { DeviceInfo } from "@ledgerhq/types-live"; import getDeviceInfo from "./getDeviceInfo"; import { getDeviceRunningMode } from "./getDeviceRunningMode"; @@ -180,11 +181,9 @@ describe("getDeviceRunningMode", () => { }); }); - describe("And the device responds with a LOCKED_DEVICE error", () => { + describe("And the device responds with a locked device error", () => { it("pushes an event lockedDevice", (done) => { - mockedGetDeviceInfo.mockRejectedValue( - new TransportStatusError(StatusCodes.LOCKED_DEVICE) - ); + mockedGetDeviceInfo.mockRejectedValue(new LockedDeviceError()); getDeviceRunningMode({ deviceId: A_DEVICE_ID, diff --git a/libs/ledger-live-common/src/hw/getDeviceRunningMode.ts b/libs/ledger-live-common/src/hw/getDeviceRunningMode.ts index 8ed8271888e..9e636b6d7b4 100644 --- a/libs/ledger-live-common/src/hw/getDeviceRunningMode.ts +++ b/libs/ledger-live-common/src/hw/getDeviceRunningMode.ts @@ -1,8 +1,4 @@ -import { - TransportStatusError, - StatusCodes, - CantOpenDevice, -} from "@ledgerhq/errors"; +import { CantOpenDevice, LockedDeviceError } from "@ledgerhq/errors"; import { DeviceInfo } from "@ledgerhq/types-live"; import { from, Observable, TimeoutError } from "rxjs"; import { retryWhen, timeout } from "rxjs/operators"; @@ -104,11 +100,5 @@ export const getDeviceRunningMode = ({ }); const isLockedDeviceError = (e: Error) => { - return ( - (e && - e instanceof TransportStatusError && - // @ts-expect-error typescript not checking agains the instanceof - e.statusCode === StatusCodes.LOCKED_DEVICE) || - e instanceof TimeoutError - ); + return e && (e instanceof TimeoutError || e instanceof LockedDeviceError); }; From 3cc22e9ce6fbcd85a98e9f2f54089cb413d007a3 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 15:35:30 +0100 Subject: [PATCH 08/15] fix: LLM update renderError usage of LockedDeviceError --- .../src/components/DeviceAction/rendering.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx b/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx index 8a2f8f4130c..980e2e21cf2 100644 --- a/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx +++ b/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx @@ -3,8 +3,7 @@ import { Platform, ScrollView } from "react-native"; import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components/native"; import { - StatusCodes, - TransportStatusError, + LockedDeviceError, WrongDeviceForAccount, } from "@ledgerhq/errors"; import { TokenCurrency } from "@ledgerhq/types-cryptoassets"; @@ -540,9 +539,11 @@ export function renderInWrongAppForAccount({ }); } -// Quick fix: the error TransportStatusError with status code LOCKED_DEVICE should be catched +// Quick fix: the error LockedDeviceError should be catched // inside all the device actions and mapped to an event of type "lockedDevice". -// With this fix, we can catch all the error that were not catched upstream. +// With this fix, we can catch all the device action error that were not catched upstream. +// If LockedDeviceError is thrown from outside a device action and renderError was not called +// it is still handled by GenericErrorView. export function renderLockedDeviceError({ t, onRetry, @@ -633,11 +634,7 @@ export function renderError({ // Redirects from renderError and not from DeviceActionDefaultRendering because renderError // can be used directly by other component - if ( - error instanceof TransportStatusError && - (error as unknown as { statusCode: number })?.statusCode === - StatusCodes.LOCKED_DEVICE - ) { + if (error instanceof LockedDeviceError) { return renderLockedDeviceError({ t, onRetry, device }); } From 50611bd3732c3b6d1f4615194d8f888932bfb580 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 15:47:39 +0100 Subject: [PATCH 09/15] fix: LLD update renderError usage of LockedDeviceError --- .../renderer/components/DeviceAction/rendering.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx index 4dae2d8da20..77456d5f0c1 100644 --- a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx @@ -12,8 +12,7 @@ import { getProviderName } from "@ledgerhq/live-common/exchange/swap/utils/index import { WrongDeviceForAccount, UpdateYourApp, - TransportStatusError, - StatusCodes, + LockedDeviceError, } from "@ledgerhq/errors"; import { LatestFirmwareVersionRequired } from "@ledgerhq/live-common/errors"; import { DeviceModelId, getDeviceModel } from "@ledgerhq/devices"; @@ -489,9 +488,11 @@ export const renderWarningOutdated = ({ ); -// Quick fix: the error TransportStatusError with status code LOCKED_DEVICE should be catched +// Quick fix: the error LockedDeviceError should be catched // inside all the device actions and mapped to an event of type "lockedDevice". -// With this fix, we can catch all the error that were not catched upstream. +// With this fix, we can catch all the device action error that were not catched upstream. +// If LockedDeviceError is thrown from outside a device action and renderError was not called +// it is still handled by GenericErrorView. export const renderLockedDeviceError = ({ t, device, @@ -558,10 +559,7 @@ export const renderError = ({ }) => { // Redirects from renderError and not from DeviceActionDefaultRendering because renderError // can be used directly by other component - if ( - error instanceof TransportStatusError && - ((error as unknown) as { statusCode: number })?.statusCode === StatusCodes.LOCKED_DEVICE - ) { + if (error instanceof LockedDeviceError) { return renderLockedDeviceError({ t, onRetry, device }); } From a92c1420882d9172ea67033ab47bbc9adf1a0e9f Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 15:48:03 +0100 Subject: [PATCH 10/15] fix: handle isLocked device action status in LLD --- .../src/renderer/components/DeviceAction/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/index.tsx b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/index.tsx index 7ac0027901c..b229538e972 100644 --- a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/index.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/index.tsx @@ -34,6 +34,7 @@ import { renderSecureTransferDeviceConfirmation, renderAllowLanguageInstallation, renderInstallingLanguage, + renderLockedDeviceError, } from "./rendering"; type Props = { @@ -73,6 +74,7 @@ export const DeviceActionDefaultRendering = ({ appAndVersion, device, unresponsive, + isLocked, error, isLoading, allowManagerRequestedWording, @@ -300,6 +302,11 @@ export const DeviceActionDefaultRendering = ({ }); } + // Renders an error as long as LLD is using the "event" implementation of device actions + if (isLocked) { + return renderLockedDeviceError({ t, device, onRetry }); + } + if ((!isLoading && !device) || unresponsive) { return renderConnectYourDevice({ modelId, From 64c4e11d1526ae4e6ff8e046d97282373e181518 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 17:44:50 +0100 Subject: [PATCH 11/15] fix: return in ErrorDisplay --- .../src/renderer/components/ErrorDisplay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.tsx b/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.tsx index ed3dbfe9f2e..7e4e63cd2ae 100644 --- a/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/ErrorDisplay.tsx @@ -20,7 +20,7 @@ const ErrorDisplay = ({ }: ErrorDisplayProps) => { const { t } = useTranslation(); - renderError({ t, error, onRetry, withExportLogs, list, supportLink, warning }); + return renderError({ t, error, onRetry, withExportLogs, list, supportLink, warning }); }; export default ErrorDisplay; From c30a61aaecd072298e4b3f0ca629c7ad8a7e58da Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 17:46:08 +0100 Subject: [PATCH 12/15] fix: linter and use of translation --- .../src/renderer/components/DeviceAction/rendering.tsx | 8 ++------ .../src/components/DeviceAction/rendering.tsx | 5 +---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx index 77456d5f0c1..bf2cf586259 100644 --- a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx @@ -9,11 +9,7 @@ import { TokenCurrency } from "@ledgerhq/types-cryptoassets"; import { Transaction, TransactionStatus } from "@ledgerhq/live-common/generated/types"; import { ExchangeRate, Exchange } from "@ledgerhq/live-common/exchange/swap/types"; import { getProviderName } from "@ledgerhq/live-common/exchange/swap/utils/index"; -import { - WrongDeviceForAccount, - UpdateYourApp, - LockedDeviceError, -} from "@ledgerhq/errors"; +import { WrongDeviceForAccount, UpdateYourApp, LockedDeviceError } from "@ledgerhq/errors"; import { LatestFirmwareVersionRequired } from "@ledgerhq/live-common/errors"; import { DeviceModelId, getDeviceModel } from "@ledgerhq/devices"; import { Device } from "@ledgerhq/live-common/hw/actions/types"; @@ -520,7 +516,7 @@ export const renderLockedDeviceError = ({ {onRetry ? ( ) : null} diff --git a/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx b/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx index 980e2e21cf2..32b923ffe80 100644 --- a/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx +++ b/apps/ledger-live-mobile/src/components/DeviceAction/rendering.tsx @@ -2,10 +2,7 @@ import React, { useEffect, useState } from "react"; import { Platform, ScrollView } from "react-native"; import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components/native"; -import { - LockedDeviceError, - WrongDeviceForAccount, -} from "@ledgerhq/errors"; +import { LockedDeviceError, WrongDeviceForAccount } from "@ledgerhq/errors"; import { TokenCurrency } from "@ledgerhq/types-cryptoassets"; import { Transaction } from "@ledgerhq/live-common/generated/types"; import { getDeviceModel } from "@ledgerhq/devices"; From f8a6a9ed4b461e1f18b441faf47faa63018e2fe6 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 17:57:57 +0100 Subject: [PATCH 13/15] chore: changeset --- .changeset/fifty-garlics-marry.md | 5 +++++ .changeset/little-parrots-swim.md | 6 ++++++ .changeset/selfish-lobsters-enjoy.md | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 .changeset/fifty-garlics-marry.md create mode 100644 .changeset/little-parrots-swim.md create mode 100644 .changeset/selfish-lobsters-enjoy.md diff --git a/.changeset/fifty-garlics-marry.md b/.changeset/fifty-garlics-marry.md new file mode 100644 index 00000000000..480e79a86c9 --- /dev/null +++ b/.changeset/fifty-garlics-marry.md @@ -0,0 +1,5 @@ +--- +"live-mobile": patch +--- + +Fix for locked device error diff --git a/.changeset/little-parrots-swim.md b/.changeset/little-parrots-swim.md new file mode 100644 index 00000000000..6a45eb495a5 --- /dev/null +++ b/.changeset/little-parrots-swim.md @@ -0,0 +1,6 @@ +--- +"@ledgerhq/live-common": patch +"@ledgerhq/errors": patch +--- + +Fix for locked device error diff --git a/.changeset/selfish-lobsters-enjoy.md b/.changeset/selfish-lobsters-enjoy.md new file mode 100644 index 00000000000..180d780ec64 --- /dev/null +++ b/.changeset/selfish-lobsters-enjoy.md @@ -0,0 +1,5 @@ +--- +"ledger-live-desktop": patch +--- + +Fix for locked device error From 04c624a5eaca751311c1f16040faf05fc237d6af Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Thu, 3 Nov 2022 18:13:50 +0100 Subject: [PATCH 14/15] chore: use t instead of Trans component --- .../src/renderer/components/DeviceAction/rendering.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx index bf2cf586259..74887e70ecd 100644 --- a/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx +++ b/apps/ledger-live-desktop/src/renderer/components/DeviceAction/rendering.tsx @@ -587,11 +587,11 @@ export const renderError = ({ ) : ( <> {supportLink ? ( - } url={supportLink} /> + ) : null} {withExportLogs ? ( } + title={t("settings.exportLogs.title")} small={false} primary={false} outlineGrey @@ -602,7 +602,7 @@ export const renderError = ({ ) : onRetry ? ( ) : null} {withOnboardingCTA ? : null} From 752d0a4e4788cea17739064eaded64393ce99b85 Mon Sep 17 00:00:00 2001 From: Alexandre Magaud Date: Wed, 9 Nov 2022 11:39:56 +0100 Subject: [PATCH 15/15] fix: handle locked device response in socket --- libs/ledger-live-common/src/api/socket.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libs/ledger-live-common/src/api/socket.ts b/libs/ledger-live-common/src/api/socket.ts index edd38b6d169..5d74f80bbaa 100644 --- a/libs/ledger-live-common/src/api/socket.ts +++ b/libs/ledger-live-common/src/api/socket.ts @@ -9,6 +9,7 @@ import { TransportStatusError, UserRefusedAllowManager, ManagerDeviceLockedError, + StatusCodes, } from "@ledgerhq/errors"; import { cancelDeviceAction } from "../hw/deviceAccess"; import { getEnv } from "../env"; @@ -115,6 +116,13 @@ export const createDeviceSocket = ( if (unsubscribed) return; const status = r.readUInt16BE(r.length - 2); + // Pushes an error on locked device. TransportStatusError will + // be remapped to a LockedDeviceError with a correct error message. + if (status === StatusCodes.LOCKED_DEVICE) { + o.error(new TransportStatusError(status)); + return; + } + // if allow manager was requested, we either throw if deny or emit accepted if (allowManagerAwaitingUser) { if (status === 0x6985 || status === 0x5501) {