Skip to content

Commit

Permalink
feat: display error names instead of codes
Browse files Browse the repository at this point in the history
  • Loading branch information
dawidsowardx committed May 16, 2023
1 parent f3fdfe2 commit 196b313
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 40 deletions.
70 changes: 70 additions & 0 deletions src/ledger/components/error-text.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { render } from '@testing-library/react'
import { ErrorText } from './error-text'
import { MessagingContext } from '../contexts/messaging-context'

describe('ErrorText', () => {
const { location } = window

afterAll(() => {
window.location = location
})

const renderErrorText = (error: string) =>
render(
<MessagingContext.Provider
value={{ switchToFullWindow: () => {} } as any}
>
<ErrorText error={error} />,
</MessagingContext.Provider>
)

describe('GIVEN known error', () => {
it('should render message instead', () => {
const { getByText } = renderErrorText('DeviceMismatch')
expect(
getByText(/Make sure you connected correct Ledger device/i)
).toBeTruthy()
})

describe('GIVEN "FailedToCreateTransport" error inside popup', () => {
it('should display "first time" instructions', () => {
Object.defineProperty(window, 'location', {
value: new URL('https://popup.html/?isPopupWindow=true'),
configurable: true,
writable: true,
})
const { getByText } = renderErrorText('FailedToCreateTransport')
expect(getByText(/navigate to full window/i)).toBeTruthy()
})
})

describe('GIVEN "FailedToCreateTransport" error outside popup', () => {
it('should not display "first time" instructions', () => {
Object.defineProperty(window, 'location', {
value: new URL('https://popup.html'),
configurable: true,
writable: true,
})
const { getByText } = renderErrorText('FailedToCreateTransport')
expect(() => getByText(/navigate to full window/i)).toThrow()
})
})
})

describe('GIVEN unknown error', () => {
it('should render error descriptor', () => {
const { getByText } = renderErrorText('6e14')
expect(getByText(/Unknown Error: BadBip32PathNetworkId/i)).toBeTruthy()
})

it('should render error descriptor', () => {
const { getByText } = renderErrorText('6f0f')
expect(getByText(/Unknown Error: CxErrorEcInvalidPoint/i)).toBeTruthy()
})

it('should render error itself', () => {
const { getByText } = renderErrorText('xxxxx')
expect(getByText(/Unknown Error: xxxxx/i)).toBeTruthy()
})
})
})
32 changes: 25 additions & 7 deletions src/ledger/components/error-text.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import { Link, Text } from 'components'
import { MessagingContext } from 'ledger/contexts/messaging-context'
import { LedgerErrorResponse, errorResponses } from 'ledger/wrapper/constants'
import { isKnownError } from 'ledger/wrapper/utils'
import { LedgerErrorCode } from 'ledger/wrapper/constants'
import { useContext } from 'react'

export const ErrorMessages: Record<string, string> = {
[LedgerErrorCode.MultipleLedgerConnected]:
'Please connect only one ledger device',
[LedgerErrorCode.UnlockDevice]: 'Please unlock Ledger Device and try again',
[LedgerErrorCode.BadIns]:
'Please open Radix Babylon app in your Ledger device and try again',
[LedgerErrorCode.NoDevicesConnected]:
'Did not find any connected Ledger devices. Please connect your Ledger device and try again',
[LedgerErrorCode.FailedToListLedgerDevices]:
'Failed initial check to check list of connected devices',
[LedgerErrorCode.FailedToCreateTransport]:
'Could not recognize Ledger device. Did you connect it to your computer and unlock it?',
[LedgerErrorCode.FailedToExchangeData]:
'Failed to exchange data with Ledger device. Did you disconnect it?',
[LedgerErrorCode.DeviceMismatch]: `Connected device doesn't match requested one. Make sure you connected correct Ledger device`,
}

export const ErrorText = ({ error }: { error?: string }) => {
const errorNames = Object.fromEntries(
Object.entries(LedgerErrorCode).map(([key, value]) => [value, key])
)
const { switchToFullWindow } = useContext(MessagingContext)
const url = new URL(window.location.href)
const isPopupWindow = url.searchParams.get('isPopupWindow') === 'true'
Expand All @@ -14,12 +33,11 @@ export const ErrorText = ({ error }: { error?: string }) => {
bold
style={{ color: 'white', lineHeight: '23px', marginTop: '20px' }}
>
{isKnownError(error)
? errorResponses[error]
: `Unknown error: ${error}`}
{ErrorMessages[error]
? ErrorMessages[error]
: `Unknown error: ${errorNames[error] || error}`}
</Text>
{error === LedgerErrorResponse.FailedToCreateTransport &&
isPopupWindow ? (
{error === LedgerErrorCode.FailedToCreateTransport && isPopupWindow ? (
<Text style={{ color: 'white', marginTop: '20px' }} italic>
If you are connecting Ledger device for the first time, please&nbsp;
<Link
Expand Down
84 changes: 63 additions & 21 deletions src/ledger/wrapper/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,75 @@ export const LedgerInstructionClass = {
ac: 'ac',
} as const

export const LedgerErrorResponse = {
export const LedgerErrorCode = {
FailedToCreateTransport: 'FailedToCreateTransport',
FailedToListLedgerDevices: 'FailedToListLedgerDevices',
FailedToExchangeData: 'FailedToExchangeData',
NoDevicesConnected: 'NoDevicesConnected',
MultipleLedgerConnected: 'MultipleLedgerConnected',
DeviceMismatch: 'DeviceMismatch',
UnlockDevice: '5515',
OpenRadixApp: '6e01',

NothingReceived: '6982',
BadCla: '6e00',
BadIns: '6e01',
BadP1P2: '6e02',
BadLen: '6e03',
UserCancelled: '6e04',

BadBip32PathLen: '6e10',
BadBip32PathDataLen: '6e11',
BadBip32PathLeadWord: '6e12',
BadBip32PathCoinType: '6e13',
BadBip32PathNetworkId: '6e14',
BadBip32PathEntity: '6e15',
BadBip32PathKeyType: '6e16',
BadBip32PathMustBeHardened: '6e17',

BadSecp256k1PublicKeyLen: '6e21',
BadSecp256k1PublicKeyType: '6e22',

BadTxSignSequence: '6e31',
BadTxSignLen: '6e32',
BadTxSignInitialState: '6e33',
BadTxSignStart: '6e34',
BadTxSignType: '6e35',
BadTxSignDigestState: '6e36',
BadTxSignRequestedState: '6e37',

BadTxSignDecoderErrorInvalidInput: '6e41',
BadTxSignDecoderErrorInvalidLen: '6e42',
BadTxSignDecoderErrorInvalidState: '6e43',
BadTxSignDecoderErrorStackOverflow: '6e44',
BadTxSignDecoderErrorStackUnderflow: '6e45',
BadTxSignDecoderErrorUnknownType: '6e46',
BadTxSignDecoderErrorUnknownParameterType: '6e47',
BadTxSignDecoderErrorUnknownEnum: '6e48',

BadTxSignUserRejected: '6e50',

BadAuthSignSequence: '6e60',
BadAuthSignRequest: '6e61',

NotImplemented: '6eff',
Unknown: '6d00',
CxErrorCarry: '6f01',
CxErrorLocked: '6f02',
CxErrorUnlocked: '6f03',
CxErrorNotLocked: '6f04',
CxErrorNotUnlocked: '6f05',
CxErrorInternalError: '6f06',
CxErrorInvalidParameterSize: '6f07',
CxErrorInvalidParameterValue: '6f08',
CxErrorInvalidParameter: '6f09',
CxErrorNotInvertible: '6f0a',
CxErrorOverflow: '6f0b',
CxErrorMemoryFull: '6f0c',
CxErrorNoResidue: '6f0d',
CxErrorEcInfinitePoint: '6f0e',
CxErrorEcInvalidPoint: '6f0f',
CxErrorEcInvalidCurve: '6f10',
Panic: 'e000',
} as const

export const LedgerInstructionCode = {
Expand All @@ -29,24 +89,6 @@ export const LedgerInstructionCode = {
SignAuthEd25519: '61',
} as const

export type LedgerError = Values<typeof LedgerErrorResponse>
export type LedgerError = Values<typeof LedgerErrorCode>
export type LedgerInstructionCode = Values<typeof LedgerInstructionCode>
export type LedgerInstructionClass = Values<typeof LedgerInstructionClass>

export const errorResponses: Record<LedgerError, string> = {
[LedgerErrorResponse.MultipleLedgerConnected]:
'Please connect only one ledger device',
[LedgerErrorResponse.UnlockDevice]:
'Please unlock Ledger Device and try again',
[LedgerErrorResponse.OpenRadixApp]:
'Please open Radix Babylon app in your Ledger device and try again',
[LedgerErrorResponse.NoDevicesConnected]:
'Did not find any connected Ledger devices. Please connect your Ledger device and try again',
[LedgerErrorResponse.FailedToListLedgerDevices]:
'Failed initial check to check list of connected devices',
[LedgerErrorResponse.FailedToCreateTransport]:
'Could not recognize Ledger device. Did you connect it to your computer and unlock it?',
[LedgerErrorResponse.FailedToExchangeData]:
'Failed to exchange data with Ledger device. Did you disconnect it?',
[LedgerErrorResponse.DeviceMismatch]: `Connected device doesn't match requested one. Make sure you connected correct Ledger device`,
}
13 changes: 6 additions & 7 deletions src/ledger/wrapper/ledger-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ import { ResultAsync, err, ok, okAsync } from 'neverthrow'
import { bufferToChunks } from 'utils'
import { logger } from 'utils/logger'
import {
LedgerErrorResponse,
LedgerErrorCode,
LedgerInstructionCode,
LedgerInstructionClass,
errorResponses,
} from './constants'
import { encodeDerivationPath } from './encode-derivation-path'
import { getDataLength } from './utils'
Expand Down Expand Up @@ -65,11 +64,11 @@ export const LedgerWrapper = ({
const createLedgerTransport = () =>
ResultAsync.fromPromise(
transport.list(),
() => LedgerErrorResponse.FailedToListLedgerDevices
() => LedgerErrorCode.FailedToListLedgerDevices
)
.andThen((devices) => {
if (devices.length > 1) {
return err(LedgerErrorResponse.MultipleLedgerConnected)
return err(LedgerErrorCode.MultipleLedgerConnected)
}

if (devices.length === 0) {
Expand All @@ -81,7 +80,7 @@ export const LedgerWrapper = ({
.andThen(() =>
ResultAsync.fromPromise(
transport.create(),
() => LedgerErrorResponse.FailedToCreateTransport
() => LedgerErrorCode.FailedToCreateTransport
).map((transport) => {
setProgressMessage('Creating Ledger device connection')
const exchange: ExchangeFn = (
Expand All @@ -95,7 +94,7 @@ export const LedgerWrapper = ({
const ledgerInput = `${instructionClass}${command}${p1}00${data}`
return ResultAsync.fromPromise(
transport.exchange(Buffer.from(ledgerInput, 'hex')),
() => errorResponses[LedgerErrorResponse.FailedToExchangeData]
() => LedgerErrorCode.FailedToExchangeData
).andThen((buffer) => {
const stringifiedResponse = buffer.toString('hex')
logger.debug(`📒 ↔️ Ledger Exchange`, {
Expand Down Expand Up @@ -143,7 +142,7 @@ export const LedgerWrapper = ({

return ledgerDeviceId === expectedDeviceId
? ok(undefined)
: err(LedgerErrorResponse.DeviceMismatch)
: err(LedgerErrorCode.DeviceMismatch)
}

const parseGetPublicKeyParams =
Expand Down
5 changes: 0 additions & 5 deletions src/ledger/wrapper/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
import { LedgerError, LedgerErrorResponse } from './constants'

export const isKnownError = (statusCode: any): statusCode is LedgerError =>
Object.values(LedgerErrorResponse).includes(statusCode)

export const getDataLength = (data: string) =>
Math.floor(data.length / 2).toString(16)

0 comments on commit 196b313

Please sign in to comment.