Skip to content
This repository was archived by the owner on Mar 23, 2023. It is now read-only.

Commit 1d24f83

Browse files
authored
feat: basic ledger setup and listener (#2890)
1 parent 4ba2bd8 commit 1d24f83

File tree

18 files changed

+493
-484
lines changed

18 files changed

+493
-484
lines changed

.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ module.exports = {
2929
"plugin:@typescript-eslint/recommended",
3030
"plugin:prettier/recommended",
3131
"plugin:react/recommended",
32+
"plugin:react-hooks/recommended",
3233
"prettier/@typescript-eslint",
3334
"plugin:testing-library/react",
3435
"plugin:testcafe/recommended",

.github/workflows/e2e.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
run: yarn install --ignore-engines --frozen-lockfile
4444

4545
- name: Build
46-
run: yarn build:ci
46+
run: yarn build:e2e
4747

4848
- name: Upload Build Artifacts
4949
uses: actions/upload-artifact@v2

.github/workflows/format.yml

+3
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ jobs:
7171
- name: Install (Yarn)
7272
run: yarn install --frozen-lockfile
7373

74+
- name: Rebuild
75+
run: npm rebuild
76+
7477
- name: Format
7578
run: |
7679
export PATH="$(yarn global bin):$PATH"

.github/workflows/test.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ jobs:
4646
- name: Install (Yarn)
4747
run: yarn install --frozen-lockfile
4848

49+
- name: Rebuild
50+
run: npm rebuild
51+
4952
- name: Test
50-
run: yarn test:coverage --maxWorkers=4 --coverageDirectory .coverage/unit
53+
run: yarn test:coverage --forceExit --maxWorkers=4 --coverageDirectory .coverage/unit
5154

5255
- name: Codecov
5356
uses: codecov/codecov-action@v1

.storybook/main.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
const path = require("path");
2-
const { override, addWebpackAlias, addWebpackModuleRule } = require("customize-cra");
1+
const { override, addWebpackModuleRule } = require("customize-cra");
32
const { injectTailwindCSS } = require("../config-overrides");
43

54
const injectNode = () =>
6-
addWebpackAlias({
7-
fs: path.resolve(__dirname, "mocks/fsMock.js"),
5+
override((config) => {
6+
config.node = {
7+
fs: "empty",
8+
dns: "mock",
9+
};
10+
return config;
811
});
912

1013
module.exports = {

.storybook/mocks/fsMock.js

-5
This file was deleted.

.yarnrc

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
child-concurrency 1
22
--install.ignore-engines true
3+
--install.ignore-optional true

config-overrides.js

+8-55
Original file line numberDiff line numberDiff line change
@@ -5,65 +5,22 @@ const {
55
addWebpackExternals,
66
addWebpackPlugin,
77
setWebpackTarget,
8+
addWebpackModuleRule,
89
} = require("customize-cra");
910
const CopyWebpackPlugin = require("copy-webpack-plugin");
1011
const path = require("path");
1112
const { EnvironmentPlugin } = require("webpack");
1213
const { dependencies } = require("./package.json");
14+
const nodeExternals = require("webpack-node-externals");
1315

14-
const whiteListedModules = [
15-
"@arkecosystem/crypto",
16-
"@arkecosystem/platform-sdk",
17-
"@arkecosystem/platform-sdk-ada",
18-
"@arkecosystem/platform-sdk-ark",
19-
"@arkecosystem/platform-sdk-atom",
20-
"@arkecosystem/platform-sdk-btc",
21-
"@arkecosystem/platform-sdk-crypto",
22-
"@arkecosystem/platform-sdk-eos",
23-
"@arkecosystem/platform-sdk-eth",
24-
"@arkecosystem/platform-sdk-intl",
25-
"@arkecosystem/platform-sdk-ipfs",
26-
"@arkecosystem/platform-sdk-lsk",
27-
"@arkecosystem/platform-sdk-markets",
28-
"@arkecosystem/platform-sdk-neo",
29-
"@arkecosystem/platform-sdk-news",
30-
"@arkecosystem/platform-sdk-profiles",
31-
"@arkecosystem/platform-sdk-support",
32-
"@arkecosystem/platform-sdk-trx",
33-
"@arkecosystem/platform-sdk-xlm",
34-
"@arkecosystem/platform-sdk-xmr",
35-
"@arkecosystem/platform-sdk-xrp",
36-
"@arkecosystem/utils",
37-
"@tippyjs/react",
38-
"downshift",
39-
"framer-motion",
40-
"hash-wasm",
41-
"i18next",
42-
"isomorphic-fetch",
43-
"react-dom",
44-
"react-error-boundary",
45-
"react-hook-form",
46-
"react-i18next",
47-
"react-inlinesvg",
48-
"react-linkify",
49-
"react-loading-skeleton",
50-
"react-range",
51-
"react-router-config",
52-
"react-router-dom",
53-
"react-scripts",
54-
"react-table",
55-
"react-toastify",
56-
"react",
57-
"recharts",
58-
"socks-proxy-agent",
59-
"styled-components",
60-
"swiper",
61-
"yup",
62-
"extract-domain",
63-
];
16+
const whiteListedModules = [];
6417

6518
const addNodeExternals = () =>
66-
addWebpackExternals([...Object.keys(dependencies || {}).filter((d) => !whiteListedModules.includes(d))]);
19+
addWebpackExternals([
20+
nodeExternals({
21+
allowlist: [/tippy/, /swiper/, "isomorphic-fetch"],
22+
}),
23+
]);
6724

6825
const injectTailwindCSS = () =>
6926
addPostcssPlugins([
@@ -91,10 +48,6 @@ module.exports = override(
9148
copyFiles(),
9249
addWebpackAlias({
9350
"@arkecosystem/crypto": "@arkecosystem/crypto/dist/index.esm.js",
94-
"@liskhq/lisk-cryptography": "@liskhq/lisk-cryptography/dist-browser/index.min.js",
95-
bytebuffer: "bytebuffer/dist/bytebuffer-node.js",
96-
memcpy: path.resolve(__dirname, "src/polyfill/memcpy.js"),
97-
history: "history/index.js",
9851
}),
9952
);
10053

package.json

+24-8
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,22 @@
2323
"scripts": {
2424
"build": "react-app-rewired build",
2525
"build:ci": "cross-env GENERATE_SOURCEMAP=false yarn build",
26+
"build:e2e": "cross-env REACT_APP_BUILD_MODE=demo yarn build:ci",
2627
"build:linux": "yarn build:ci && electron-builder --linux --publish never",
2728
"build:mac": "yarn build:ci && electron-builder --mac --publish never",
2829
"build:win": "yarn build:ci && electron-builder --win --x64 --ia32 --publish never",
2930
"dev": "cross-env REACT_APP_BUILD_MODE=demo concurrently \"yarn start\" \"wait-on http://localhost:3000 && electron .\"",
3031
"eject": "react-app-rewired eject",
32+
"electron-rebuild": "electron-builder install-app-deps",
3133
"format": "yarn lint && yarn prettier",
34+
"postinstall": "yarn electron-rebuild",
3235
"lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix",
3336
"prettier": "prettier --write \"./*.{ts,tsx,js,json,md}\" \"./**/*.{ts,tsx,js,json,md}\"",
3437
"start": "cross-env BROWSER=none react-app-rewired start",
3538
"storybook": "start-storybook -p 5000",
3639
"test": "react-app-rewired --expose-gc test --logHeapUsage",
3740
"test:coverage": "react-app-rewired --expose-gc test --logHeapUsage --coverage --watchAll=false",
38-
"test:e2e": "cross-env ELECTRON_IS_E2E=1 ELECTRON_IS_DEV=0 NODE_ENV=production REACT_APP_BUILD_MODE=demo testcafe",
41+
"test:e2e": "cross-env ELECTRON_IS_E2E=1 ELECTRON_IS_DEV=0 testcafe",
3942
"preversion": "cross-env ./scripts/version-artifacts.sh"
4043
},
4144
"husky": {
@@ -134,10 +137,11 @@
134137
"@arkecosystem/platform-sdk-profiles": "^0.9.367",
135138
"@arkecosystem/platform-sdk-support": "^0.9.367",
136139
"@arkecosystem/platform-sdk-trx": "^0.9.367",
137-
"@arkecosystem/platform-sdk-xlm": "^0.9.367",
138140
"@arkecosystem/platform-sdk-xmr": "^0.9.367",
139141
"@arkecosystem/platform-sdk-xrp": "^0.9.367",
140142
"@arkecosystem/utils": "^1.2.0",
143+
"@ledgerhq/devices": "^5.15.0",
144+
"@ledgerhq/hw-transport-node-hid-singleton": "^5.16.0",
141145
"@tippyjs/react": "^4.0.5",
142146
"@types/react-linkify": "^1.0.0",
143147
"about-window": "^1.13.4",
@@ -149,6 +153,7 @@
149153
"hash-wasm": "^3.7.1",
150154
"i18next": "^19.4.5",
151155
"isomorphic-fetch": "^2.2.1",
156+
"node-hid": "^1.3.0",
152157
"react": "^16.13.1",
153158
"react-dom": "^16.13.1",
154159
"react-error-boundary": "^2.2.2",
@@ -160,7 +165,7 @@
160165
"react-range": "^1.6.7",
161166
"react-router-config": "^5.1.1",
162167
"react-router-dom": "^5.2.0",
163-
"react-scripts": "3.4.1",
168+
"react-scripts": "^3.4.3",
164169
"react-table": "^7.3.2",
165170
"react-toastify": "^6.0.8",
166171
"recharts": "^2.0.0-beta.6",
@@ -172,8 +177,10 @@
172177
"yup": "^0.29.3"
173178
},
174179
"devDependencies": {
180+
"@babel/preset-env": "^7.10.4",
175181
"@babel/preset-react": "^7.10.4",
176182
"@babel/preset-typescript": "^7.10.4",
183+
"@ledgerhq/hw-transport-u2f": "^5.15.0",
177184
"@storybook/addon-actions": "^6.0.6",
178185
"@storybook/addon-knobs": "^6.0.6",
179186
"@storybook/addons": "^6.0.6",
@@ -212,14 +219,15 @@
212219
"@typescript-eslint/parser": "^3.1.0",
213220
"autoprefixer": "^9.8.0",
214221
"babel-loader": "^8.1.0",
222+
"bcrypto": "^5.3.0",
215223
"codecov": "^3.7.0",
216224
"concurrently": "^5.2.0",
217225
"copy-webpack-plugin": "^6.0.3",
218226
"cross-env": "^7.0.2",
219227
"customize-cra": "^1.0.0",
220-
"electron": "^9.0.2",
221-
"electron-builder": "^22.7.0",
222-
"electron-devtools-installer": "^3.0.0",
228+
"electron": "^9.3.1",
229+
"electron-builder": "^22.8.1",
230+
"electron-devtools-installer": "^3.1.1",
223231
"electron-notarize": "^1.0.0",
224232
"electron-root-path": "^1.0.16",
225233
"eslint-config-prettier": "^6.11.0",
@@ -258,7 +266,15 @@
258266
"testcafe": "^1.8.8",
259267
"testcafe-browser-provider-electron": "^0.0.15",
260268
"typescript": "^3.9.5",
261-
"wait-on": "^5.1.0"
269+
"wait-on": "^5.1.0",
270+
"webpack-node-externals": "^2.5.2"
271+
},
272+
"optionalDependencies": {
273+
"fsevents": "*"
274+
},
275+
"resolutions": {
276+
"**/fsevents": "^2.1.3",
277+
"**/**/bcrypto": "^5.3.0"
262278
},
263279
"babelMacros": {
264280
"twin": {
@@ -272,7 +288,7 @@
272288
"appId": "io.ark.desktop-wallet",
273289
"artifactName": "${name}-${os}-${arch}-${version}.${ext}",
274290
"afterSign": "scripts/notarize.js",
275-
"npmRebuild": false,
291+
"npmRebuild": true,
276292
"extraMetadata": {
277293
"main": "build/electron/index.js"
278294
},

src/app/App.tsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Environment } from "@arkecosystem/platform-sdk-profiles";
1313
// import { XRP } from "@arkecosystem/platform-sdk-xrp";
1414
import { ApplicationError, Offline } from "domains/error/pages";
1515
import { Splash } from "domains/splash/pages";
16+
import { LedgerListener } from "domains/transaction/components/LedgerListener";
1617
import React, { useEffect, useLayoutEffect, useState } from "react";
1718
import { ErrorBoundary } from "react-error-boundary";
1819
import { I18nextProvider } from "react-i18next";
@@ -51,11 +52,7 @@ const Main = () => {
5152
useLayoutEffect(() => {
5253
const boot = async () => {
5354
/* istanbul ignore next */
54-
const shouldUseFixture: boolean =
55-
process.env.REACT_APP_BUILD_MODE === "demo" ||
56-
// TestCafe doesn't expose environment variables.
57-
(process.env.NODE_ENV === "production" && process.env.PUBLIC_URL === ".");
58-
55+
const shouldUseFixture = process.env.REACT_APP_BUILD_MODE === "demo";
5956
await env.verify(shouldUseFixture ? fixtureData : undefined);
6057
await env.boot();
6158
await runAll();
@@ -117,6 +114,7 @@ export const App = () => {
117114
<ErrorBoundary FallbackComponent={ApplicationError}>
118115
<I18nextProvider i18n={i18n}>
119116
<EnvironmentProvider env={env}>
117+
<LedgerListener />
120118
<Main />
121119
</EnvironmentProvider>
122120
</I18nextProvider>

src/domains/network/data.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { LSK } from "@arkecosystem/platform-sdk-lsk";
88
import { NEO } from "@arkecosystem/platform-sdk-neo";
99
import { NetworkData } from "@arkecosystem/platform-sdk-profiles";
1010
import { TRX } from "@arkecosystem/platform-sdk-trx";
11-
import { XLM } from "@arkecosystem/platform-sdk-xlm";
11+
// import { XLM } from "@arkecosystem/platform-sdk-xlm";
1212
import { XMR } from "@arkecosystem/platform-sdk-xmr";
1313
import { XRP } from "@arkecosystem/platform-sdk-xrp";
1414

@@ -315,8 +315,8 @@ export const availableNetworksMock: NetworkData[] = [
315315
new NetworkData(NEO.manifest.name, NEO.manifest.networks["neo.testnet"]),
316316
new NetworkData(TRX.manifest.name, TRX.manifest.networks["trx.mainnet"]),
317317
new NetworkData(TRX.manifest.name, TRX.manifest.networks["trx.testnet"]),
318-
new NetworkData(XLM.manifest.name, XLM.manifest.networks["xlm.mainnet"]),
319-
new NetworkData(XLM.manifest.name, XLM.manifest.networks["xlm.testnet"]),
318+
// new NetworkData(XLM.manifest.name, XLM.manifest.networks["xlm.mainnet"]),
319+
// new NetworkData(XLM.manifest.name, XLM.manifest.networks["xlm.testnet"]),
320320
new NetworkData(XMR.manifest.name, XMR.manifest.networks["xmr.mainnet"]),
321321
new NetworkData(XMR.manifest.name, XMR.manifest.networks["xmr.testnet"]),
322322
new NetworkData(XRP.manifest.name, XRP.manifest.networks["xrp.mainnet"]),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @ts-ignore - Could not find a declaration file for module '@ledgerhq/devices'.
2+
import { DeviceModelId } from "@ledgerhq/devices";
3+
4+
export interface LedgerDevice {
5+
path: string;
6+
modelId: DeviceModelId;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import LedgerTransport from "@ledgerhq/hw-transport-node-hid-singleton";
2+
import React from "react";
3+
import { act } from "react-dom/test-utils";
4+
import { render } from "utils/testing-library";
5+
6+
import { LedgerListener } from "./LedgerListener";
7+
8+
describe("LedgerListener", () => {
9+
it("should sync devices", async () => {
10+
let onNext: any;
11+
const listenMock = jest.spyOn(LedgerTransport, "listen").mockImplementation(({ next }: any) => (onNext = next));
12+
const consoleMock = jest.spyOn(console, "log").mockImplementation(() => null);
13+
14+
render(<LedgerListener />);
15+
16+
await new Promise((resolve) => setTimeout(resolve, 200));
17+
18+
const addParams = { type: "add", descriptor: "", deviceModel: { id: "1" } };
19+
act(() => {
20+
onNext(addParams);
21+
});
22+
expect(consoleMock).toHaveBeenCalledWith("[Ledger] add", addParams);
23+
24+
const removeParams = { type: "remove", descriptor: "", deviceModel: {} };
25+
act(() => {
26+
onNext(removeParams);
27+
});
28+
expect(consoleMock).toHaveBeenCalledWith("[Ledger] remove", removeParams);
29+
30+
listenMock.mockReset();
31+
});
32+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// @ts-ignore - Could not find a declaration file for module '@ledgerhq/hw-transport-node-hid-singleton'.
2+
import LedgerTransport from "@ledgerhq/hw-transport-node-hid-singleton";
3+
import { useEffect, useState } from "react";
4+
5+
import { LedgerDevice } from "./LedgerListener.models";
6+
7+
export const LedgerListener = () => {
8+
// eslint-disable-next-line
9+
const [_, setDevice] = useState<LedgerDevice | undefined>();
10+
11+
useEffect(() => {
12+
const syncDevices = () => {
13+
LedgerTransport.listen({
14+
next: ({
15+
deviceModel,
16+
descriptor,
17+
type,
18+
}: {
19+
descriptor: string;
20+
type: string;
21+
deviceModel: { id?: string };
22+
}) => {
23+
console.log(`[Ledger] ${type}`, { descriptor, deviceModel, type });
24+
const modelId = deviceModel?.id || "nanoS";
25+
if (type === "add") {
26+
setDevice({ path: descriptor, modelId });
27+
}
28+
setDevice(undefined);
29+
},
30+
});
31+
};
32+
33+
const timeoutRef = setTimeout(syncDevices, 100);
34+
return () => {
35+
clearTimeout(timeoutRef);
36+
};
37+
}, []);
38+
39+
return null;
40+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./LedgerListener";

0 commit comments

Comments
 (0)