Skip to content

Commit 913af51

Browse files
committed
Merge branch 'main' into feat/trezor-keyring-v2
2 parents 972590d + 63bdcf5 commit 913af51

44 files changed

Lines changed: 2610 additions & 182 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Check Changelog
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, labeled, unlabeled]
6+
7+
jobs:
8+
check-changelog:
9+
name: Check changelog
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Check changelog
13+
uses: MetaMask/github-tools/.github/actions/check-changelog@v1
14+
with:
15+
base-branch: ${{ github.event.pull_request.base.ref }}
16+
head-ref: ${{ github.head_ref }}
17+
labels: ${{ toJSON(github.event.pull_request.labels) }}
18+
pr-number: ${{ github.event.pull_request.number }}
19+
repo: ${{ github.repository }}

.github/workflows/validate-pr-title.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ jobs:
3939
style
4040
test
4141
scopes: |
42+
deps
43+
deps-dev
4244
keyring-api
4345
keyring-eth-hd
4446
keyring-eth-ledger-bridge

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This repository contains the following packages [^fn1]:
1919
- [`@metamask/eth-simple-keyring`](packages/keyring-eth-simple)
2020
- [`@metamask/eth-snap-keyring`](packages/keyring-snap-bridge)
2121
- [`@metamask/eth-trezor-keyring`](packages/keyring-eth-trezor)
22+
- [`@metamask/hw-wallet-sdk`](packages/hw-wallet-sdk)
2223
- [`@metamask/keyring-api`](packages/keyring-api)
2324
- [`@metamask/keyring-internal-api`](packages/keyring-internal-api)
2425
- [`@metamask/keyring-internal-snap-client`](packages/keyring-internal-snap-client)
@@ -37,6 +38,7 @@ Or, in graph form [^fn1]:
3738
graph LR;
3839
linkStyle default opacity:0.5
3940
account_api(["@metamask/account-api"]);
41+
hw_wallet_sdk(["@metamask/hw-wallet-sdk"]);
4042
keyring_api(["@metamask/keyring-api"]);
4143
eth_hd_keyring(["@metamask/eth-hd-keyring"]);
4244
eth_ledger_bridge_keyring(["@metamask/eth-ledger-bridge-keyring"]);
@@ -55,7 +57,9 @@ linkStyle default opacity:0.5
5557
eth_hd_keyring --> keyring_api;
5658
eth_hd_keyring --> keyring_utils;
5759
eth_hd_keyring --> account_api;
60+
eth_ledger_bridge_keyring --> keyring_api;
5861
eth_ledger_bridge_keyring --> keyring_utils;
62+
eth_ledger_bridge_keyring --> account_api;
5963
eth_qr_keyring --> keyring_utils;
6064
eth_simple_keyring --> keyring_api;
6165
eth_simple_keyring --> keyring_utils;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Add hardware related error mappings and custom hardware error ([#421](https://github.com/MetaMask/accounts/pull/421))
13+
14+
[Unreleased]: https://github.com/MetaMask/accounts/

packages/hw-wallet-sdk/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# `@metamask/hw-wallet-sdk`
2+
3+
MetaMask Hardware Wallet SDK.
4+
5+
## Installation
6+
7+
`yarn add @metamask/hw-wallet-sdk`
8+
9+
or
10+
11+
`npm install @metamask/hw-wallet-sdk`
12+
13+
## Contributing
14+
15+
This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* For a detailed explanation regarding each configuration property and type check, visit:
3+
* https://jestjs.io/docs/configuration
4+
*/
5+
6+
const merge = require('deepmerge');
7+
const path = require('path');
8+
9+
const baseConfig = require('../../jest.config.packages');
10+
11+
const displayName = path.basename(__dirname);
12+
13+
module.exports = merge(baseConfig, {
14+
// The display name when running multiple projects
15+
displayName,
16+
17+
coveragePathIgnorePatterns: ['./src/tests'],
18+
19+
// An object that configures minimum threshold enforcement for coverage results
20+
coverageThreshold: {
21+
global: {
22+
branches: 100,
23+
functions: 100,
24+
lines: 100,
25+
statements: 100,
26+
},
27+
},
28+
});
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{
2+
"name": "@metamask/hw-wallet-sdk",
3+
"version": "0.0.0",
4+
"description": "MetaMask Hardware Device SDK",
5+
"keywords": [
6+
"metamask",
7+
"hardware-wallet"
8+
],
9+
"homepage": "https://github.com/MetaMask/accounts/tree/main/packages/hw-wallet-sdk#readme",
10+
"bugs": {
11+
"url": "https://github.com/MetaMask/accounts/issues"
12+
},
13+
"repository": {
14+
"type": "git",
15+
"url": "https://github.com/MetaMask/accounts.git"
16+
},
17+
"exports": {
18+
".": {
19+
"import": {
20+
"types": "./dist/index.d.mts",
21+
"default": "./dist/index.mjs"
22+
},
23+
"require": {
24+
"types": "./dist/index.d.cts",
25+
"default": "./dist/index.cjs"
26+
}
27+
}
28+
},
29+
"main": "./dist/index.cjs",
30+
"types": "./dist/index.d.cts",
31+
"files": [
32+
"dist/"
33+
],
34+
"scripts": {
35+
"build": "ts-bridge --project tsconfig.build.json --no-references",
36+
"build:clean": "yarn build --clean",
37+
"build:docs": "typedoc",
38+
"changelog:update": "../../scripts/update-changelog.sh @metamask/hw-wallet-sdk",
39+
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/hw-wallet-sdk",
40+
"publish:preview": "yarn npm publish --tag preview",
41+
"test": "yarn test:source && yarn test:types",
42+
"test:clean": "jest --clearCache",
43+
"test:source": "jest && jest-it-up",
44+
"test:types": "../../scripts/tsd-test.sh ./src",
45+
"test:watch": "jest --watch"
46+
},
47+
"devDependencies": {
48+
"@lavamoat/allow-scripts": "^3.2.1",
49+
"@lavamoat/preinstall-always-fail": "^2.1.0",
50+
"@metamask/auto-changelog": "^3.4.4",
51+
"@ts-bridge/cli": "^0.6.3",
52+
"@types/jest": "^29.5.12",
53+
"@types/node": "^20.12.12",
54+
"deepmerge": "^4.2.2",
55+
"depcheck": "^1.4.7",
56+
"jest": "^29.5.0",
57+
"jest-it-up": "^3.1.0",
58+
"rimraf": "^5.0.7",
59+
"ts-jest": "^29.0.5",
60+
"ts-node": "^10.9.2",
61+
"tsd": "^0.31.0",
62+
"typedoc": "^0.25.13",
63+
"typescript": "~5.6.3"
64+
},
65+
"engines": {
66+
"node": "^18.18 || >=20"
67+
},
68+
"publishConfig": {
69+
"access": "public",
70+
"registry": "https://registry.npmjs.org/"
71+
},
72+
"lavamoat": {
73+
"allowScripts": {
74+
"@lavamoat/preinstall-always-fail": false
75+
}
76+
},
77+
"tsd": {
78+
"directory": "src",
79+
"compilerOptions": {
80+
"composite": "false"
81+
}
82+
}
83+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { LEDGER_ERROR_MAPPINGS } from './hardware-error-mappings';
2+
import { ErrorCode, Severity, Category } from './hardware-errors-enums';
3+
4+
describe('HARDWARE_ERROR_MAPPINGS', () => {
5+
describe('Ledger mappings', () => {
6+
const errorMappings = LEDGER_ERROR_MAPPINGS;
7+
8+
it('has errorMappings object', () => {
9+
expect(errorMappings).toBeDefined();
10+
expect(typeof errorMappings).toBe('object');
11+
});
12+
13+
describe('success codes', () => {
14+
it('map 0x9000 to success', () => {
15+
const mapping = errorMappings['0x9000'];
16+
expect(mapping).toBeDefined();
17+
expect(mapping.code).toBe(ErrorCode.Success);
18+
expect(mapping.severity).toBe(Severity.Info);
19+
expect(mapping.category).toBe(Category.Success);
20+
});
21+
});
22+
23+
describe('authentication errors', () => {
24+
it('map 0x6300 to authentication failed', () => {
25+
const mapping = errorMappings['0x6300'];
26+
expect(mapping.code).toBe(ErrorCode.AuthenticationFailed);
27+
expect(mapping.severity).toBe(Severity.Err);
28+
expect(mapping.category).toBe(Category.Authentication);
29+
expect(mapping.userMessage).toBeDefined();
30+
});
31+
32+
it('map 0x63c0 to PIN attempts remaining', () => {
33+
const mapping = errorMappings['0x63c0'];
34+
expect(mapping.code).toBe(ErrorCode.AuthenticationPinAttemptsRemaining);
35+
expect(mapping.severity).toBe(Severity.Warning);
36+
});
37+
38+
it('map 0x5515 to device locked', () => {
39+
const mapping = errorMappings['0x5515'];
40+
expect(mapping.code).toBe(ErrorCode.AuthenticationDeviceLocked);
41+
expect(mapping.severity).toBe(Severity.Err);
42+
expect(mapping.userMessage).toContain('unlock');
43+
});
44+
45+
it('map 0x9840 to device blocked', () => {
46+
const mapping = errorMappings['0x9840'];
47+
expect(mapping.code).toBe(ErrorCode.AuthenticationDeviceBlocked);
48+
expect(mapping.severity).toBe(Severity.Critical);
49+
});
50+
});
51+
52+
describe('user action errors', () => {
53+
it('map 0x6985 to user rejected', () => {
54+
const mapping = errorMappings['0x6985'];
55+
expect(mapping.code).toBe(ErrorCode.UserRejected);
56+
expect(mapping.severity).toBe(Severity.Warning);
57+
expect(mapping.category).toBe(Category.UserAction);
58+
});
59+
60+
it('map 0x5501 to user refused', () => {
61+
const mapping = errorMappings['0x5501'];
62+
expect(mapping.code).toBe(ErrorCode.UserRejected);
63+
expect(mapping.severity).toBe(Severity.Warning);
64+
});
65+
});
66+
describe('connection errors', () => {
67+
it('map 0x650f to connection issue', () => {
68+
const mapping = errorMappings['0x650f'];
69+
expect(mapping.code).toBe(ErrorCode.ConnectionClosed);
70+
expect(mapping.category).toBe(Category.Connection);
71+
});
72+
});
73+
74+
it('has valid structure for all mappings', () => {
75+
Object.entries(errorMappings).forEach(([_, mapping]) => {
76+
expect(mapping).toHaveProperty('code');
77+
expect(mapping).toHaveProperty('message');
78+
expect(mapping).toHaveProperty('severity');
79+
expect(mapping).toHaveProperty('category');
80+
81+
const numericErrorCodes = Object.values(ErrorCode).filter(
82+
(value): value is number => typeof value === 'number',
83+
);
84+
expect(numericErrorCodes).toContain(mapping.code);
85+
expect(Object.values(Severity)).toContain(mapping.severity);
86+
expect(Object.values(Category)).toContain(mapping.category);
87+
expect(typeof mapping.message).toBe('string');
88+
});
89+
});
90+
91+
it('has valid userMessage when present', () => {
92+
const mappingsWithUserMessage = Object.values(errorMappings).filter(
93+
(mapping): mapping is typeof mapping & { userMessage: string } =>
94+
'userMessage' in mapping &&
95+
typeof mapping.userMessage === 'string' &&
96+
mapping.userMessage.length > 0,
97+
);
98+
expect(mappingsWithUserMessage.length).toBeGreaterThan(0);
99+
mappingsWithUserMessage.forEach((mapping) => {
100+
expect(typeof mapping.userMessage).toBe('string');
101+
expect(mapping.userMessage.length).toBeGreaterThan(0);
102+
});
103+
});
104+
});
105+
});

0 commit comments

Comments
 (0)