Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/hw-wallet-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Add hardware related error mappings and custom hardware error ([#421](https://github.com/MetaMask/accounts/pull/421))
- Add BLE and mobile error mappings with new error codes for Bluetooth permissions, connection states, and mobile support ([#433](https://github.com/MetaMask/accounts/pull/433))

[Unreleased]: https://github.com/MetaMask/accounts/
112 changes: 111 additions & 1 deletion packages/hw-wallet-sdk/src/hardware-error-mappings.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { LEDGER_ERROR_MAPPINGS } from './hardware-error-mappings';
import {
LEDGER_ERROR_MAPPINGS,
BLE_ERROR_MAPPINGS,
MOBILE_ERROR_MAPPINGS,
} from './hardware-error-mappings';
import { ErrorCode, Severity, Category } from './hardware-errors-enums';

describe('HARDWARE_ERROR_MAPPINGS', () => {
Expand Down Expand Up @@ -102,4 +106,110 @@ describe('HARDWARE_ERROR_MAPPINGS', () => {
});
});
});

describe('BLE mappings', () => {
const errorMappings = BLE_ERROR_MAPPINGS;

it('has errorMappings object', () => {
expect(errorMappings).toBeDefined();
expect(typeof errorMappings).toBe('object');
});

describe('permission errors', () => {
it('maps BLUETOOTH_PERMISSION_DENIED correctly', () => {
const mapping = errorMappings.BLUETOOTH_PERMISSION_DENIED;
expect(mapping.code).toBe(ErrorCode.PermissionBluetoothDenied);
expect(mapping.severity).toBe(Severity.Err);
expect(mapping.category).toBe(Category.Configuration);
});

it('maps LOCATION_PERMISSION_DENIED correctly', () => {
const mapping = errorMappings.LOCATION_PERMISSION_DENIED;
expect(mapping.code).toBe(ErrorCode.PermissionLocationDenied);
expect(mapping.severity).toBe(Severity.Err);
expect(mapping.category).toBe(Category.Configuration);
expect(mapping.userMessage).toContain('Location');
});

it('maps NEARBY_DEVICES_PERMISSION_DENIED correctly', () => {
const mapping = errorMappings.NEARBY_DEVICES_PERMISSION_DENIED;
expect(mapping.code).toBe(ErrorCode.PermissionNearbyDevicesDenied);
expect(mapping.severity).toBe(Severity.Err);
expect(mapping.category).toBe(Category.Configuration);
});
});

describe('bluetooth state errors', () => {
it('maps BLUETOOTH_DISABLED correctly', () => {
const mapping = errorMappings.BLUETOOTH_DISABLED;
expect(mapping.code).toBe(ErrorCode.BluetoothDisabled);
expect(mapping.severity).toBe(Severity.Warning);
expect(mapping.category).toBe(Category.Connection);
});

it('maps BLUETOOTH_SCAN_FAILED correctly', () => {
const mapping = errorMappings.BLUETOOTH_SCAN_FAILED;
expect(mapping.code).toBe(ErrorCode.BluetoothScanFailed);
expect(mapping.severity).toBe(Severity.Err);
expect(mapping.category).toBe(Category.Connection);
});

it('maps BLUETOOTH_CONNECTION_FAILED correctly', () => {
const mapping = errorMappings.BLUETOOTH_CONNECTION_FAILED;
expect(mapping.code).toBe(ErrorCode.BluetoothConnectionFailed);
expect(mapping.severity).toBe(Severity.Err);
expect(mapping.category).toBe(Category.Connection);
});
});

it('has valid structure for all mappings', () => {
Object.entries(errorMappings).forEach(([_, mapping]) => {
expect(mapping).toHaveProperty('code');
expect(mapping).toHaveProperty('message');
expect(mapping).toHaveProperty('severity');
expect(mapping).toHaveProperty('category');

const numericErrorCodes = Object.values(ErrorCode).filter(
(value): value is number => typeof value === 'number',
);
expect(numericErrorCodes).toContain(mapping.code);
expect(Object.values(Severity)).toContain(mapping.severity);
expect(Object.values(Category)).toContain(mapping.category);
expect(typeof mapping.message).toBe('string');
});
});
});

describe('Mobile mappings', () => {
const errorMappings = MOBILE_ERROR_MAPPINGS;

it('has errorMappings object', () => {
expect(errorMappings).toBeDefined();
expect(typeof errorMappings).toBe('object');
});

it('maps NOT_SUPPORTED correctly', () => {
const mapping = errorMappings.NOT_SUPPORTED;
expect(mapping.code).toBe(ErrorCode.MobileNotSupported);
expect(mapping.severity).toBe(Severity.Err);
expect(mapping.category).toBe(Category.DeviceState);
});

it('has valid structure for all mappings', () => {
Object.entries(errorMappings).forEach(([_, mapping]) => {
expect(mapping).toHaveProperty('code');
expect(mapping).toHaveProperty('message');
expect(mapping).toHaveProperty('severity');
expect(mapping).toHaveProperty('category');

const numericErrorCodes = Object.values(ErrorCode).filter(
(value): value is number => typeof value === 'number',
);
expect(numericErrorCodes).toContain(mapping.code);
expect(Object.values(Severity)).toContain(mapping.severity);
expect(Object.values(Category)).toContain(mapping.category);
expect(typeof mapping.message).toBe('string');
});
});
});
});
60 changes: 60 additions & 0 deletions packages/hw-wallet-sdk/src/hardware-error-mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,63 @@ export const LEDGER_ERROR_MAPPINGS = {
userMessage: 'Ethereum app is out of date. Please update it to continue.',
},
};

export const BLE_ERROR_MAPPINGS = {
BLUETOOTH_PERMISSION_DENIED: {
code: ErrorCode.PermissionBluetoothDenied,
message: 'Bluetooth permission denied',
severity: Severity.Err,
category: Category.Configuration,
userMessage:
'Bluetooth permission is required to connect to your hardware wallet. Please enable it in your device settings.',
},
LOCATION_PERMISSION_DENIED: {
code: ErrorCode.PermissionLocationDenied,
message: 'Location permission denied',
severity: Severity.Err,
category: Category.Configuration,
userMessage:
'Location permission is required for Bluetooth scanning on Android. Please enable it in your device settings.',
},
NEARBY_DEVICES_PERMISSION_DENIED: {
code: ErrorCode.PermissionNearbyDevicesDenied,
message: 'Nearby devices permission denied',
severity: Severity.Err,
category: Category.Configuration,
userMessage:
'Nearby devices permission is required to scan for your hardware wallet. Please enable it in your device settings.',
},
BLUETOOTH_DISABLED: {
code: ErrorCode.BluetoothDisabled,
message: 'Bluetooth is turned off',
severity: Severity.Warning,
category: Category.Connection,
userMessage:
'Bluetooth is turned off. Please enable Bluetooth to connect to your hardware wallet.',
},
BLUETOOTH_SCAN_FAILED: {
code: ErrorCode.BluetoothScanFailed,
message: 'Bluetooth scanning failed',
severity: Severity.Err,
category: Category.Connection,
userMessage: 'Unable to scan for Bluetooth devices. Please try again.',
},
BLUETOOTH_CONNECTION_FAILED: {
code: ErrorCode.BluetoothConnectionFailed,
message: 'Bluetooth connection failed',
severity: Severity.Err,
category: Category.Connection,
userMessage:
'Failed to connect via Bluetooth. Please make sure your device is nearby and try again.',
},
};

export const MOBILE_ERROR_MAPPINGS = {
NOT_SUPPORTED: {
code: ErrorCode.MobileNotSupported,
message: 'Operation not supported on mobile',
severity: Severity.Err,
category: Category.DeviceState,
userMessage: 'This operation is not supported on mobile devices.',
},
};
Comment thread
mathieuartu marked this conversation as resolved.
10 changes: 10 additions & 0 deletions packages/hw-wallet-sdk/src/hardware-errors-enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ export enum ErrorCode {
DeviceStateEthAppClosed = 6003,
DeviceStateEthAppOutOfDate = 6004,

// Mobile/BLE Permission Errors
PermissionBluetoothDenied = 7000,
PermissionLocationDenied = 7001,
PermissionNearbyDevicesDenied = 7002,
BluetoothDisabled = 7100,
BluetoothScanFailed = 7101,
BluetoothConnectionFailed = 7102,
MobileNotSupported = 7300,

// Transaction
TxInsufficientFunds = 10000,

Expand All @@ -72,4 +81,5 @@ export enum Category {
UserAction = 'UserAction',
DeviceState = 'DeviceState',
Unknown = 'Unknown',
Configuration = 'Configuration',
}
Loading