@ledgerhq/live-common 34.55.0-nightly.20251215100948 → 34.55.0-nightly.20251216024112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/hw/getOnboardingStatePolling.d.ts.map +1 -1
- package/lib/hw/getOnboardingStatePolling.js +11 -1
- package/lib/hw/getOnboardingStatePolling.js.map +1 -1
- package/lib/hw/installApp.d.ts.map +1 -1
- package/lib/hw/installApp.js +4 -2
- package/lib/hw/installApp.js.map +1 -1
- package/lib/wallet-api/Exchange/server.d.ts.map +1 -1
- package/lib/wallet-api/Exchange/server.js +18 -0
- package/lib/wallet-api/Exchange/server.js.map +1 -1
- package/lib/wallet-api/Exchange/tracking.d.ts +18 -0
- package/lib/wallet-api/Exchange/tracking.d.ts.map +1 -1
- package/lib/wallet-api/Exchange/tracking.js +22 -0
- package/lib/wallet-api/Exchange/tracking.js.map +1 -1
- package/lib-es/hw/getOnboardingStatePolling.d.ts.map +1 -1
- package/lib-es/hw/getOnboardingStatePolling.js +12 -2
- package/lib-es/hw/getOnboardingStatePolling.js.map +1 -1
- package/lib-es/hw/installApp.d.ts.map +1 -1
- package/lib-es/hw/installApp.js +5 -3
- package/lib-es/hw/installApp.js.map +1 -1
- package/lib-es/wallet-api/Exchange/server.d.ts.map +1 -1
- package/lib-es/wallet-api/Exchange/server.js +18 -0
- package/lib-es/wallet-api/Exchange/server.js.map +1 -1
- package/lib-es/wallet-api/Exchange/tracking.d.ts +18 -0
- package/lib-es/wallet-api/Exchange/tracking.d.ts.map +1 -1
- package/lib-es/wallet-api/Exchange/tracking.js +22 -0
- package/lib-es/wallet-api/Exchange/tracking.js.map +1 -1
- package/package.json +73 -73
- package/src/families/cosmos/datasets/__snapshots__/babylon.integration.test.ts.snap +2 -2
- package/src/families/cosmos/datasets/__snapshots__/cosmos.integration.test.ts.snap +667 -10
- package/src/families/cosmos/datasets/__snapshots__/cryptoOrg.integration.test.ts.snap +2 -2
- package/src/families/cosmos/datasets/__snapshots__/mantra.integration.test.ts.snap +2 -2
- package/src/families/cosmos/datasets/__snapshots__/osmosis.integration.test.ts.snap +2 -2
- package/src/families/cosmos/datasets/__snapshots__/persistence.integration.test.ts.snap +4 -4
- package/src/hw/getOnboardingStatePolling.test.ts +64 -1
- package/src/hw/getOnboardingStatePolling.ts +15 -3
- package/src/hw/installApp.test.ts +82 -1
- package/src/hw/installApp.ts +58 -52
- package/src/wallet-api/Exchange/server.test.ts +2 -0
- package/src/wallet-api/Exchange/server.ts +20 -0
- package/src/wallet-api/Exchange/tracking.ts +56 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
exports[`crypto_org currency bridge scanAccounts crypto_org seed 1 1`] = `
|
|
4
4
|
[
|
|
5
5
|
{
|
|
6
|
-
"balance": "
|
|
6
|
+
"balance": "10000010",
|
|
7
7
|
"cosmosResources": {
|
|
8
8
|
"delegatedBalance": "0",
|
|
9
9
|
"delegations": [],
|
|
@@ -23,7 +23,7 @@ exports[`crypto_org currency bridge scanAccounts crypto_org seed 1 1`] = `
|
|
|
23
23
|
"operationsCount": 0,
|
|
24
24
|
"pendingOperations": [],
|
|
25
25
|
"seedIdentifier": "0346289788c2f518a158232e7028e8e86b30cff0670326d3a2507183cb0d81cecb",
|
|
26
|
-
"spendableBalance": "
|
|
26
|
+
"spendableBalance": "10000010",
|
|
27
27
|
"swapHistory": [],
|
|
28
28
|
"syncHash": undefined,
|
|
29
29
|
"used": true,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
exports[`mantra currency bridge scanAccounts mantra seed 1 1`] = `
|
|
4
4
|
[
|
|
5
5
|
{
|
|
6
|
-
"balance": "
|
|
6
|
+
"balance": "714930",
|
|
7
7
|
"currencyId": "mantra",
|
|
8
8
|
"derivationMode": "",
|
|
9
9
|
"freshAddress": "mantra1gyauvl44q2apn3u3aujm36q8zrj74vry7n5nvn",
|
|
@@ -12,7 +12,7 @@ exports[`mantra currency bridge scanAccounts mantra seed 1 1`] = `
|
|
|
12
12
|
"index": 0,
|
|
13
13
|
"pendingOperations": [],
|
|
14
14
|
"seedIdentifier": "03d5e0ebb3f1ae2afe87e5d5a24b5029a59cc12f8fd1056840091b2f0b97e54e83",
|
|
15
|
-
"spendableBalance": "
|
|
15
|
+
"spendableBalance": "204930",
|
|
16
16
|
"swapHistory": [],
|
|
17
17
|
"syncHash": undefined,
|
|
18
18
|
"used": true,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
exports[`osmo currency bridge scanAccounts osmo seed 1 1`] = `
|
|
4
4
|
[
|
|
5
5
|
{
|
|
6
|
-
"balance": "
|
|
6
|
+
"balance": "124348",
|
|
7
7
|
"currencyId": "osmo",
|
|
8
8
|
"derivationMode": "",
|
|
9
9
|
"freshAddress": "osmo17gmcxyc5ccd5kwqqatpgfdgh380w2hc77zm0zw",
|
|
@@ -12,7 +12,7 @@ exports[`osmo currency bridge scanAccounts osmo seed 1 1`] = `
|
|
|
12
12
|
"index": 0,
|
|
13
13
|
"pendingOperations": [],
|
|
14
14
|
"seedIdentifier": "02dc75cfe8137450ae2259dc7f29fd8767951956cc943adb4387e91c37a058a9f0",
|
|
15
|
-
"spendableBalance": "
|
|
15
|
+
"spendableBalance": "124348",
|
|
16
16
|
"swapHistory": [],
|
|
17
17
|
"syncHash": undefined,
|
|
18
18
|
"used": true,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
exports[`persistence currency bridge scanAccounts persistence seed 1 1`] = `
|
|
4
4
|
[
|
|
5
5
|
{
|
|
6
|
-
"balance": "
|
|
6
|
+
"balance": "2636577",
|
|
7
7
|
"currencyId": "persistence",
|
|
8
8
|
"derivationMode": "",
|
|
9
9
|
"freshAddress": "persistence1gyauvl44q2apn3u3aujm36q8zrj74vrym5cypd",
|
|
@@ -12,14 +12,14 @@ exports[`persistence currency bridge scanAccounts persistence seed 1 1`] = `
|
|
|
12
12
|
"index": 0,
|
|
13
13
|
"pendingOperations": [],
|
|
14
14
|
"seedIdentifier": "03d5e0ebb3f1ae2afe87e5d5a24b5029a59cc12f8fd1056840091b2f0b97e54e83",
|
|
15
|
-
"spendableBalance": "
|
|
15
|
+
"spendableBalance": "1202573",
|
|
16
16
|
"swapHistory": [],
|
|
17
17
|
"syncHash": undefined,
|
|
18
18
|
"used": true,
|
|
19
19
|
"xpub": "persistence1gyauvl44q2apn3u3aujm36q8zrj74vrym5cypd",
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
"balance": "
|
|
22
|
+
"balance": "230008",
|
|
23
23
|
"currencyId": "persistence",
|
|
24
24
|
"derivationMode": "",
|
|
25
25
|
"freshAddress": "persistence1v2mp0m7k96dm9qv60fkspcqlzpkzrwnevf47z2",
|
|
@@ -28,7 +28,7 @@ exports[`persistence currency bridge scanAccounts persistence seed 1 1`] = `
|
|
|
28
28
|
"index": 1,
|
|
29
29
|
"pendingOperations": [],
|
|
30
30
|
"seedIdentifier": "03d5e0ebb3f1ae2afe87e5d5a24b5029a59cc12f8fd1056840091b2f0b97e54e83",
|
|
31
|
-
"spendableBalance": "
|
|
31
|
+
"spendableBalance": "230008",
|
|
32
32
|
"swapHistory": [],
|
|
33
33
|
"syncHash": undefined,
|
|
34
34
|
"used": true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getOnboardingStatePolling } from "./getOnboardingStatePolling";
|
|
2
|
-
import { from, Subscription, TimeoutError } from "rxjs";
|
|
2
|
+
import { from, of, Subscription, TimeoutError } from "rxjs";
|
|
3
3
|
import * as rxjsOperators from "rxjs/operators";
|
|
4
4
|
import { DeviceModelId } from "@ledgerhq/devices";
|
|
5
5
|
import Transport from "@ledgerhq/hw-transport";
|
|
@@ -14,7 +14,13 @@ import { getVersion } from "../device/use-cases/getVersionUseCase";
|
|
|
14
14
|
import { extractOnboardingState, OnboardingState, OnboardingStep } from "./extractOnboardingState";
|
|
15
15
|
import { SeedPhraseType } from "@ledgerhq/types-live";
|
|
16
16
|
import { DeviceDisconnectedWhileSendingError } from "@ledgerhq/device-management-kit";
|
|
17
|
+
import { quitApp } from "../deviceSDK/commands/quitApp";
|
|
17
18
|
|
|
19
|
+
jest.mock("../deviceSDK/commands/quitApp", () => {
|
|
20
|
+
return {
|
|
21
|
+
quitApp: jest.fn(() => of(undefined)), // immediately-completing observable
|
|
22
|
+
};
|
|
23
|
+
});
|
|
18
24
|
jest.mock("./deviceAccess");
|
|
19
25
|
jest.mock("../device/use-cases/getVersionUseCase");
|
|
20
26
|
jest.mock("./extractOnboardingState");
|
|
@@ -42,6 +48,7 @@ const pollingPeriodMs = 1000;
|
|
|
42
48
|
|
|
43
49
|
const mockedGetVersion = jest.mocked(getVersion);
|
|
44
50
|
const mockedWithDevice = jest.mocked(withDevice);
|
|
51
|
+
const mockedQuitApp = jest.mocked(quitApp);
|
|
45
52
|
mockedWithDevice.mockReturnValue(job => from(job(new Transport())));
|
|
46
53
|
|
|
47
54
|
const mockedExtractOnboardingState = jest.mocked(extractOnboardingState);
|
|
@@ -65,6 +72,7 @@ describe("getOnboardingStatePolling", () => {
|
|
|
65
72
|
afterEach(() => {
|
|
66
73
|
mockedGetVersion.mockClear();
|
|
67
74
|
mockedExtractOnboardingState.mockClear();
|
|
75
|
+
mockedQuitApp.mockClear();
|
|
68
76
|
jest.clearAllTimers();
|
|
69
77
|
onboardingStatePollingSubscription?.unsubscribe();
|
|
70
78
|
});
|
|
@@ -308,6 +316,61 @@ describe("getOnboardingStatePolling", () => {
|
|
|
308
316
|
},
|
|
309
317
|
});
|
|
310
318
|
});
|
|
319
|
+
it("should call quitApp before fetching the device version", done => {
|
|
320
|
+
mockedGetVersion.mockResolvedValue(aFirmwareInfo);
|
|
321
|
+
mockedExtractOnboardingState.mockReturnValue(anOnboardingState);
|
|
322
|
+
|
|
323
|
+
const device = aDevice;
|
|
324
|
+
|
|
325
|
+
getOnboardingStatePolling({
|
|
326
|
+
deviceId: device.deviceId,
|
|
327
|
+
deviceName: null,
|
|
328
|
+
pollingPeriodMs,
|
|
329
|
+
}).subscribe({
|
|
330
|
+
next: value => {
|
|
331
|
+
try {
|
|
332
|
+
expect(value.onboardingState).toEqual(anOnboardingState);
|
|
333
|
+
|
|
334
|
+
expect(mockedQuitApp).toHaveBeenCalledTimes(1);
|
|
335
|
+
|
|
336
|
+
const firstCallArgs = (mockedQuitApp as jest.Mock).mock.calls[0];
|
|
337
|
+
expect(firstCallArgs[0]).toBeInstanceOf(Transport);
|
|
338
|
+
|
|
339
|
+
done();
|
|
340
|
+
} catch (err) {
|
|
341
|
+
done(err);
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
error: err => done(err),
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
jest.advanceTimersByTime(pollingPeriodMs - 1);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("should call quitApp only once when polling", () => {
|
|
351
|
+
mockedGetVersion.mockResolvedValue(aFirmwareInfo);
|
|
352
|
+
mockedExtractOnboardingState.mockReturnValue(anOnboardingState);
|
|
353
|
+
|
|
354
|
+
const device = aDevice;
|
|
355
|
+
|
|
356
|
+
onboardingStatePollingSubscription = getOnboardingStatePolling({
|
|
357
|
+
deviceId: device.deviceId,
|
|
358
|
+
deviceName: null,
|
|
359
|
+
pollingPeriodMs,
|
|
360
|
+
}).subscribe();
|
|
361
|
+
|
|
362
|
+
jest.advanceTimersByTime(pollingPeriodMs - 1);
|
|
363
|
+
|
|
364
|
+
expect(mockedQuitApp).toHaveBeenCalledTimes(1);
|
|
365
|
+
const firstCallArgs = (mockedQuitApp as jest.Mock).mock.calls[0];
|
|
366
|
+
expect(firstCallArgs[0]).toBeInstanceOf(Transport);
|
|
367
|
+
|
|
368
|
+
jest.advanceTimersByTime(pollingPeriodMs * 5);
|
|
369
|
+
|
|
370
|
+
expect(mockedQuitApp).toHaveBeenCalledTimes(1);
|
|
371
|
+
|
|
372
|
+
expect(mockedGetVersion.mock.calls.length).toBeGreaterThanOrEqual(1);
|
|
373
|
+
});
|
|
311
374
|
});
|
|
312
375
|
|
|
313
376
|
describe("When the device is in bootloader mode", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { from, of, throwError, Observable, TimeoutError, timer } from "rxjs";
|
|
2
|
-
import { map, catchError, first, timeout, repeat } from "rxjs/operators";
|
|
2
|
+
import { map, catchError, first, timeout, repeat, switchMap } from "rxjs/operators";
|
|
3
3
|
import { getVersion } from "../device/use-cases/getVersionUseCase";
|
|
4
4
|
import { withDevice } from "./deviceAccess";
|
|
5
5
|
import {
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { FirmwareInfo } from "@ledgerhq/types-live";
|
|
18
18
|
import { extractOnboardingState, OnboardingState } from "./extractOnboardingState";
|
|
19
19
|
import { DeviceDisconnectedWhileSendingError } from "@ledgerhq/device-management-kit";
|
|
20
|
+
import { quitApp } from "../deviceSDK/commands/quitApp";
|
|
20
21
|
|
|
21
22
|
export type OnboardingStatePollingResult = {
|
|
22
23
|
onboardingState: OnboardingState | null;
|
|
@@ -58,11 +59,23 @@ export const getOnboardingStatePolling = ({
|
|
|
58
59
|
safeGuardTimeoutMs = pollingPeriodMs * 10, // Nb Empirical value
|
|
59
60
|
allowedErrorChecks = [],
|
|
60
61
|
}: GetOnboardingStatePollingArgs): GetOnboardingStatePollingResult => {
|
|
62
|
+
let hasQuitAppAlreadyRun = false;
|
|
63
|
+
|
|
61
64
|
const getOnboardingStateOnce = (): Observable<OnboardingStatePollingResult> => {
|
|
62
65
|
return withDevice(deviceId, {
|
|
63
66
|
openTimeoutMs: transportAbortTimeoutMs,
|
|
64
67
|
matchDeviceByName: deviceName ?? undefined,
|
|
65
|
-
})(t =>
|
|
68
|
+
})(t => {
|
|
69
|
+
const getVersionObs = from(getVersion(t, { abortTimeoutMs: transportAbortTimeoutMs }));
|
|
70
|
+
|
|
71
|
+
// only run quitApp the first time
|
|
72
|
+
if (hasQuitAppAlreadyRun) {
|
|
73
|
+
return getVersionObs;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
hasQuitAppAlreadyRun = true;
|
|
77
|
+
return quitApp(t).pipe(switchMap(() => getVersionObs));
|
|
78
|
+
}).pipe(
|
|
66
79
|
timeout(safeGuardTimeoutMs), // Throws a TimeoutError
|
|
67
80
|
first(),
|
|
68
81
|
catchError((error: unknown) => {
|
|
@@ -73,7 +86,6 @@ export const getOnboardingStatePolling = ({
|
|
|
73
86
|
// Pushes the error to the next step to be processed (no retry from the beginning)
|
|
74
87
|
return of(error as Error);
|
|
75
88
|
}
|
|
76
|
-
|
|
77
89
|
return throwError(() => error);
|
|
78
90
|
}),
|
|
79
91
|
map((event: FirmwareInfo | Error) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TestScheduler } from "rxjs/testing";
|
|
2
2
|
import installApp from "./installApp";
|
|
3
3
|
import ManagerAPI from "../manager/api";
|
|
4
|
-
import { defer } from "rxjs";
|
|
4
|
+
import { defer, of, throwError } from "rxjs";
|
|
5
5
|
import { anAppBuilder } from "../mock/fixtures/anApp";
|
|
6
6
|
import { aTransportBuilder } from "@ledgerhq/hw-transport-mocker";
|
|
7
7
|
import {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ManagerDeviceLockedError,
|
|
10
10
|
UnresponsiveDeviceError,
|
|
11
11
|
} from "@ledgerhq/errors";
|
|
12
|
+
import { quitApp } from "../deviceSDK/commands/quitApp";
|
|
12
13
|
|
|
13
14
|
// Mocking ManagerAPI
|
|
14
15
|
jest.mock("../manager/api", () => {
|
|
@@ -19,7 +20,13 @@ jest.mock("../manager/api", () => {
|
|
|
19
20
|
install: jest.fn(),
|
|
20
21
|
};
|
|
21
22
|
});
|
|
23
|
+
|
|
24
|
+
jest.mock("../deviceSDK/commands/quitApp", () => ({
|
|
25
|
+
quitApp: jest.fn(),
|
|
26
|
+
}));
|
|
27
|
+
|
|
22
28
|
const mockManagerApiInstall = jest.mocked(ManagerAPI.install);
|
|
29
|
+
const mockQuitApp = jest.mocked(quitApp);
|
|
23
30
|
|
|
24
31
|
describe("installApp", () => {
|
|
25
32
|
let testScheduler: TestScheduler;
|
|
@@ -29,6 +36,11 @@ describe("installApp", () => {
|
|
|
29
36
|
// Makes TestScheduler assertions works with our test framework Jest
|
|
30
37
|
expect(actual).toEqual(expected);
|
|
31
38
|
});
|
|
39
|
+
|
|
40
|
+
mockManagerApiInstall.mockReset();
|
|
41
|
+
mockQuitApp.mockReset();
|
|
42
|
+
|
|
43
|
+
mockQuitApp.mockReturnValue(of<void>(undefined));
|
|
32
44
|
});
|
|
33
45
|
|
|
34
46
|
test("On one error followed by a successfully emitted event, it should retry after the given delay", () => {
|
|
@@ -149,4 +161,73 @@ describe("installApp", () => {
|
|
|
149
161
|
});
|
|
150
162
|
}
|
|
151
163
|
});
|
|
164
|
+
|
|
165
|
+
test("it should run quit app at the beginning then keep going with manager", () => {
|
|
166
|
+
const transport = aTransportBuilder();
|
|
167
|
+
const app = anAppBuilder();
|
|
168
|
+
|
|
169
|
+
const fakeEvent = {
|
|
170
|
+
type: "bulk-progress",
|
|
171
|
+
progress: 0.5,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
mockManagerApiInstall.mockReturnValue(of(fakeEvent));
|
|
175
|
+
|
|
176
|
+
const results: Array<{ progress: number }> = [];
|
|
177
|
+
|
|
178
|
+
installApp(transport, "target-test", app).subscribe({
|
|
179
|
+
next: v => results.push(v),
|
|
180
|
+
error: e => {
|
|
181
|
+
throw e;
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// quitApp called once, with the same transport
|
|
186
|
+
expect(mockQuitApp).toHaveBeenCalledTimes(1);
|
|
187
|
+
expect(mockQuitApp).toHaveBeenCalledWith(transport);
|
|
188
|
+
|
|
189
|
+
// ManagerAPI.install called once, with correct params
|
|
190
|
+
expect(mockManagerApiInstall).toHaveBeenCalledTimes(1);
|
|
191
|
+
expect(mockManagerApiInstall).toHaveBeenCalledWith(
|
|
192
|
+
transport,
|
|
193
|
+
"install-app",
|
|
194
|
+
expect.objectContaining({
|
|
195
|
+
targetId: "target-test",
|
|
196
|
+
perso: app.perso,
|
|
197
|
+
deleteKey: app.delete_key,
|
|
198
|
+
firmware: app.firmware,
|
|
199
|
+
firmwareKey: app.firmware_key,
|
|
200
|
+
hash: app.hash,
|
|
201
|
+
}),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// quitApp must be invoked before ManagerAPI.install
|
|
205
|
+
expect(mockQuitApp.mock.invocationCallOrder[0]).toBeLessThan(
|
|
206
|
+
mockManagerApiInstall.mock.invocationCallOrder[0],
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
expect(results).toEqual([{ progress: 0.5 }]);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("it should return error if quit app fail", done => {
|
|
213
|
+
const transport = aTransportBuilder();
|
|
214
|
+
const app = anAppBuilder();
|
|
215
|
+
const error = new Error("quit failed");
|
|
216
|
+
|
|
217
|
+
mockQuitApp.mockReturnValueOnce(throwError(() => error));
|
|
218
|
+
|
|
219
|
+
const observable = installApp(transport, "target-test", app);
|
|
220
|
+
|
|
221
|
+
observable.subscribe({
|
|
222
|
+
next: () => {
|
|
223
|
+
done(new Error("Expected an error, but got a next notification"));
|
|
224
|
+
},
|
|
225
|
+
error: err => {
|
|
226
|
+
expect(err).toBe(error);
|
|
227
|
+
// ManagerAPI.install should never be called if quitApp fails
|
|
228
|
+
expect(mockManagerApiInstall).not.toHaveBeenCalled();
|
|
229
|
+
done();
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
});
|
|
152
233
|
});
|
package/src/hw/installApp.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Observable, throwError, timer } from "rxjs";
|
|
2
|
-
import { throttleTime, filter, map, catchError, retry } from "rxjs/operators";
|
|
2
|
+
import { throttleTime, filter, map, catchError, retry, switchMap } from "rxjs/operators";
|
|
3
3
|
import {
|
|
4
4
|
LockedDeviceError,
|
|
5
5
|
ManagerAppDepInstallRequired,
|
|
@@ -12,6 +12,7 @@ import ManagerAPI from "../manager/api";
|
|
|
12
12
|
import { getDependencies } from "../apps/polyfill";
|
|
13
13
|
import { LocalTracer } from "@ledgerhq/logs";
|
|
14
14
|
import { LOG_TYPE } from ".";
|
|
15
|
+
import { quitApp } from "../deviceSDK/commands/quitApp";
|
|
15
16
|
|
|
16
17
|
const APP_INSTALL_RETRY_DELAY = 500;
|
|
17
18
|
const APP_INSTALL_RETRY_LIMIT = 5;
|
|
@@ -53,60 +54,65 @@ export default function installApp(
|
|
|
53
54
|
retryDelayMs,
|
|
54
55
|
});
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
error
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
57
|
+
// Run quitApp just before ManagerAPI.install
|
|
58
|
+
return quitApp(transport).pipe(
|
|
59
|
+
switchMap(() =>
|
|
60
|
+
ManagerAPI.install(transport, "install-app", {
|
|
61
|
+
targetId,
|
|
62
|
+
perso: app.perso,
|
|
63
|
+
deleteKey: app.delete_key,
|
|
64
|
+
firmware: app.firmware,
|
|
65
|
+
firmwareKey: app.firmware_key,
|
|
66
|
+
hash: app.hash,
|
|
67
|
+
}).pipe(
|
|
68
|
+
retry({
|
|
69
|
+
count: retryLimit,
|
|
70
|
+
delay: (error: unknown, retryCount: number) => {
|
|
71
|
+
// Not retrying on locked device errors
|
|
72
|
+
if (
|
|
73
|
+
error instanceof LockedDeviceError ||
|
|
74
|
+
error instanceof ManagerDeviceLockedError ||
|
|
75
|
+
error instanceof UnresponsiveDeviceError
|
|
76
|
+
) {
|
|
77
|
+
tracer.trace(`Not retrying on error: ${error}`, {
|
|
78
|
+
error,
|
|
79
|
+
});
|
|
80
|
+
return throwError(() => error);
|
|
81
|
+
}
|
|
78
82
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
83
|
+
tracer.trace(`Retrying (${retryCount}/${retryLimit}) on error: ${error}`, {
|
|
84
|
+
error,
|
|
85
|
+
retryLimit,
|
|
86
|
+
retryDelayMs,
|
|
87
|
+
});
|
|
88
|
+
return timer(retryDelayMs);
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
filter((e: any) => e.type === "bulk-progress"), // only bulk progress interests the UI
|
|
92
|
+
throttleTime(100), // throttle to only emit 10 event/s max, to not spam the UI
|
|
93
|
+
map((e: any) => ({
|
|
94
|
+
progress: e.progress,
|
|
95
|
+
})), // extract a stream of progress percentage
|
|
96
|
+
catchError((e: Error) => {
|
|
97
|
+
tracer.trace(`Error: ${e}`, { error: e });
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
if (!e || !e.message) return throwError(() => e);
|
|
96
100
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
const status = e.message.slice(e.message.length - 4);
|
|
102
|
+
if (status === "6a83" || status === "6811") {
|
|
103
|
+
const dependencies = getDependencies(app.name);
|
|
104
|
+
return throwError(
|
|
105
|
+
() =>
|
|
106
|
+
new ManagerAppDepInstallRequired("", {
|
|
107
|
+
appName: app.name,
|
|
108
|
+
dependency: dependencies.join(", "),
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
return throwError(() => e);
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
|
+
),
|
|
111
117
|
);
|
|
112
118
|
}
|
|
@@ -467,6 +467,17 @@ export const handlers = ({
|
|
|
467
467
|
throw wrappedError;
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
tracking.swapPayloadRequested({
|
|
471
|
+
provider,
|
|
472
|
+
transactionId,
|
|
473
|
+
fromAccountAddress,
|
|
474
|
+
toAccountAddress,
|
|
475
|
+
fromCurrencyId: fromCurrency!.id,
|
|
476
|
+
toCurrencyId: toCurrency?.id,
|
|
477
|
+
fromAmount,
|
|
478
|
+
quoteId,
|
|
479
|
+
});
|
|
480
|
+
|
|
470
481
|
const {
|
|
471
482
|
binaryPayload,
|
|
472
483
|
signature,
|
|
@@ -494,6 +505,15 @@ export const handlers = ({
|
|
|
494
505
|
throw wrappedError;
|
|
495
506
|
});
|
|
496
507
|
|
|
508
|
+
tracking.swapResponseRetrieved({
|
|
509
|
+
binaryPayload,
|
|
510
|
+
signature,
|
|
511
|
+
payinAddress,
|
|
512
|
+
swapId,
|
|
513
|
+
payinExtraId,
|
|
514
|
+
extraTransactionParameters,
|
|
515
|
+
});
|
|
516
|
+
|
|
497
517
|
// Complete Swap
|
|
498
518
|
const trackingCompleteParams = {
|
|
499
519
|
provider: params.provider,
|
|
@@ -119,6 +119,62 @@ export default function trackingWrapper(trackCall: TrackExchange) {
|
|
|
119
119
|
completeExchangeNoParams: (manifest: AppManifest) => {
|
|
120
120
|
track("Completes Exchange no params", getEventData(manifest));
|
|
121
121
|
},
|
|
122
|
+
|
|
123
|
+
swapPayloadRequested: ({
|
|
124
|
+
provider,
|
|
125
|
+
transactionId,
|
|
126
|
+
fromAccountAddress,
|
|
127
|
+
toAccountAddress,
|
|
128
|
+
fromCurrencyId,
|
|
129
|
+
toCurrencyId,
|
|
130
|
+
fromAmount,
|
|
131
|
+
quoteId,
|
|
132
|
+
}: {
|
|
133
|
+
provider: string;
|
|
134
|
+
transactionId: string;
|
|
135
|
+
fromAccountAddress: string;
|
|
136
|
+
toAccountAddress: string;
|
|
137
|
+
fromCurrencyId: string;
|
|
138
|
+
toCurrencyId?: string;
|
|
139
|
+
fromAmount: string | number;
|
|
140
|
+
quoteId?: string;
|
|
141
|
+
}) => {
|
|
142
|
+
track("Swap payload requested", {
|
|
143
|
+
provider,
|
|
144
|
+
transactionId,
|
|
145
|
+
fromAccountAddress,
|
|
146
|
+
toAccountAddress,
|
|
147
|
+
fromCurrencyId,
|
|
148
|
+
toCurrencyId,
|
|
149
|
+
fromAmount: String(fromAmount),
|
|
150
|
+
quoteId,
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
swapResponseRetrieved: ({
|
|
155
|
+
binaryPayload,
|
|
156
|
+
signature,
|
|
157
|
+
payinAddress,
|
|
158
|
+
swapId,
|
|
159
|
+
payinExtraId,
|
|
160
|
+
extraTransactionParameters,
|
|
161
|
+
}: {
|
|
162
|
+
binaryPayload: string;
|
|
163
|
+
signature: string;
|
|
164
|
+
payinAddress: string;
|
|
165
|
+
swapId: string;
|
|
166
|
+
payinExtraId?: string;
|
|
167
|
+
extraTransactionParameters?: string;
|
|
168
|
+
}) => {
|
|
169
|
+
track("Swap response retrieved", {
|
|
170
|
+
binaryPayload,
|
|
171
|
+
signature,
|
|
172
|
+
payinAddress,
|
|
173
|
+
swapId,
|
|
174
|
+
payinExtraId,
|
|
175
|
+
extraTransactionParameters,
|
|
176
|
+
});
|
|
177
|
+
},
|
|
122
178
|
} as const;
|
|
123
179
|
}
|
|
124
180
|
|