@ledgerhq/live-common 26.0.0 → 26.1.0-next.0
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/README.md +1 -1
- package/lib/ble/hooks/useBleDevicePairing.d.ts +18 -0
- package/lib/ble/hooks/useBleDevicePairing.d.ts.map +1 -0
- package/lib/ble/hooks/useBleDevicePairing.js +57 -0
- package/lib/ble/hooks/useBleDevicePairing.js.map +1 -0
- package/lib/ble/hooks/useBleDevicePairing.test.d.ts +2 -0
- package/lib/ble/hooks/useBleDevicePairing.test.d.ts.map +1 -0
- package/lib/ble/hooks/useBleDevicePairing.test.js +159 -0
- package/lib/ble/hooks/useBleDevicePairing.test.js.map +1 -0
- package/lib/ble/hooks/useBleDevicesScanning.d.ts +25 -0
- package/lib/ble/hooks/useBleDevicesScanning.d.ts.map +1 -0
- package/lib/ble/hooks/useBleDevicesScanning.js +117 -0
- package/lib/ble/hooks/useBleDevicesScanning.js.map +1 -0
- package/lib/ble/hooks/useBleDevicesScanning.test.d.ts +2 -0
- package/lib/ble/hooks/useBleDevicesScanning.test.d.ts.map +1 -0
- package/lib/ble/hooks/useBleDevicesScanning.test.js +281 -0
- package/lib/ble/hooks/useBleDevicesScanning.test.js.map +1 -0
- package/lib/ble/types.d.ts +21 -0
- package/lib/ble/types.d.ts.map +1 -0
- package/lib/ble/types.js +3 -0
- package/lib/ble/types.js.map +1 -0
- package/lib/families/cardano/js-estimateMaxSpendable.d.ts.map +1 -1
- package/lib/families/cardano/js-estimateMaxSpendable.js +2 -0
- package/lib/families/cardano/js-estimateMaxSpendable.js.map +1 -1
- package/lib/families/cosmos/specs.js +2 -2
- package/lib/families/cosmos/specs.js.map +1 -1
- package/lib/families/elrond/specs.js +1 -1
- package/lib/families/elrond/specs.js.map +1 -1
- package/lib/families/polkadot/bridge.integration.test.js +3 -3
- package/lib/families/polkadot/bridge.integration.test.js.map +1 -1
- package/lib/families/polkadot/specs.d.ts.map +1 -1
- package/lib/families/polkadot/specs.js +4 -3
- package/lib/families/polkadot/specs.js.map +1 -1
- package/lib/featureFlags/provider.d.ts +5 -1
- package/lib/featureFlags/provider.d.ts.map +1 -1
- package/lib/featureFlags/provider.js +6 -4
- package/lib/featureFlags/provider.js.map +1 -1
- package/lib/featureFlags/useFeature.d.ts.map +1 -1
- package/lib/featureFlags/useFeature.js +3 -1
- package/lib/featureFlags/useFeature.js.map +1 -1
- package/lib/generated/account.d.ts.map +1 -1
- package/lib/generated/account.js.map +1 -1
- package/lib/generated/bridge/js.d.ts.map +1 -1
- package/lib/generated/bridge/js.js.map +1 -1
- package/lib/generated/bridge/mock.d.ts.map +1 -1
- package/lib/generated/bridge/mock.js.map +1 -1
- package/lib/generated/cli-transaction.d.ts.map +1 -1
- package/lib/generated/cli-transaction.js.map +1 -1
- package/lib/generated/deviceTransactionConfig.d.ts.map +1 -1
- package/lib/generated/deviceTransactionConfig.js.map +1 -1
- package/lib/generated/exchange.d.ts.map +1 -1
- package/lib/generated/exchange.js.map +1 -1
- package/lib/generated/hw-getAddress.d.ts.map +1 -1
- package/lib/generated/hw-getAddress.js.map +1 -1
- package/lib/generated/hw-signMessage.d.ts.map +1 -1
- package/lib/generated/hw-signMessage.js.map +1 -1
- package/lib/generated/mock.d.ts.map +1 -1
- package/lib/generated/mock.js.map +1 -1
- package/lib/generated/platformAdapter.d.ts.map +1 -1
- package/lib/generated/platformAdapter.js.map +1 -1
- package/lib/generated/presync.d.ts.map +1 -1
- package/lib/generated/presync.js.map +1 -1
- package/lib/generated/specs.d.ts.map +1 -1
- package/lib/generated/specs.js.map +1 -1
- package/lib/generated/speculos-deviceActions.d.ts.map +1 -1
- package/lib/generated/speculos-deviceActions.js.map +1 -1
- package/lib/generated/transaction.d.ts.map +1 -1
- package/lib/generated/transaction.js.map +1 -1
- package/lib/generated/types.d.ts.map +1 -1
- package/lib/platform/react.d.ts.map +1 -1
- package/lib/platform/react.js +6 -3
- package/lib/platform/react.js.map +1 -1
- package/package.json +18 -18
- package/src/ble/hooks/useBleDevicePairing.test.ts +91 -0
- package/src/ble/hooks/useBleDevicePairing.ts +51 -0
- package/src/ble/hooks/useBleDevicesScanning.test.ts +252 -0
- package/src/ble/hooks/useBleDevicesScanning.ts +147 -0
- package/src/ble/types.ts +42 -0
- package/src/families/cardano/js-estimateMaxSpendable.ts +2 -0
- package/src/families/cosmos/specs.ts +2 -2
- package/src/families/elrond/specs.ts +1 -1
- package/src/families/polkadot/bridge.integration.test.ts +3 -3
- package/src/families/polkadot/specs.ts +4 -3
- package/src/featureFlags/provider.tsx +11 -1
- package/src/featureFlags/useFeature.ts +6 -2
- package/src/generated/account.ts +0 -8
- package/src/generated/bridge/js.ts +0 -18
- package/src/generated/bridge/mock.ts +0 -8
- package/src/generated/cli-transaction.ts +0 -17
- package/src/generated/deviceTransactionConfig.ts +15 -31
- package/src/generated/exchange.ts +0 -5
- package/src/generated/hw-getAddress.ts +0 -18
- package/src/generated/hw-signMessage.ts +0 -3
- package/src/generated/mock.ts +0 -3
- package/src/generated/platformAdapter.ts +0 -4
- package/src/generated/presync.ts +0 -1
- package/src/generated/specs.ts +0 -17
- package/src/generated/speculos-deviceActions.ts +0 -17
- package/src/generated/transaction.ts +0 -18
- package/src/generated/types.ts +7 -4
- package/src/platform/react.ts +7 -3
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { renderHook, act } from "@testing-library/react-hooks";
|
|
2
|
+
import type {
|
|
3
|
+
Observer as TransportObserver,
|
|
4
|
+
Subscription as TransportSubscription,
|
|
5
|
+
DescriptorEvent,
|
|
6
|
+
} from "@ledgerhq/hw-transport";
|
|
7
|
+
import { DeviceModelId, getDeviceModel } from "@ledgerhq/devices";
|
|
8
|
+
import { useBleDevicesScanning } from "./useBleDevicesScanning";
|
|
9
|
+
import type { TransportBleDevice } from "../types";
|
|
10
|
+
|
|
11
|
+
jest.useFakeTimers();
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
14
|
+
const nanoXServiceUuid = getDeviceModel(DeviceModelId.nanoX).bluetoothSpec![0]
|
|
15
|
+
.serviceUuid;
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17
|
+
const nanoFTSServiceUuid = getDeviceModel(DeviceModelId.nanoFTS)
|
|
18
|
+
.bluetoothSpec![0].serviceUuid;
|
|
19
|
+
|
|
20
|
+
// Fake devices info we would get from the bluetooth transport listen
|
|
21
|
+
const aTransportBleDevice = (overrideProps?: Partial<TransportBleDevice>) => {
|
|
22
|
+
return {
|
|
23
|
+
id: "aBleNanoId",
|
|
24
|
+
name: "aBleNano",
|
|
25
|
+
localName: null,
|
|
26
|
+
rssi: 50,
|
|
27
|
+
mtu: 50,
|
|
28
|
+
serviceUUIDs: [nanoXServiceUuid],
|
|
29
|
+
...overrideProps,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type bleTransportListenObserver = TransportObserver<
|
|
34
|
+
DescriptorEvent<TransportBleDevice | null>
|
|
35
|
+
>;
|
|
36
|
+
|
|
37
|
+
// HOF creating a mocked Transport listen method to be given to useBleDevicesScanning
|
|
38
|
+
const setupMockBleTransportListen =
|
|
39
|
+
(mockEmitValuesByObserver: (observer: bleTransportListenObserver) => void) =>
|
|
40
|
+
(observer: bleTransportListenObserver): TransportSubscription => {
|
|
41
|
+
mockEmitValuesByObserver(observer);
|
|
42
|
+
|
|
43
|
+
const unsubscribe = () => {};
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
unsubscribe,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
describe("useBleDevicesScanning", () => {
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
jest.clearAllTimers();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("When several unique devices are found by the scanner", () => {
|
|
56
|
+
const deviceIdA = "ID_A";
|
|
57
|
+
const deviceIdB = "ID_B";
|
|
58
|
+
const deviceIdC = "ID_C";
|
|
59
|
+
|
|
60
|
+
const mockEmitValuesByObserver = (observer: bleTransportListenObserver) => {
|
|
61
|
+
observer.next({
|
|
62
|
+
type: "add",
|
|
63
|
+
descriptor: aTransportBleDevice({ id: deviceIdA }),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
observer.next({
|
|
68
|
+
type: "add",
|
|
69
|
+
descriptor: aTransportBleDevice({
|
|
70
|
+
id: deviceIdB,
|
|
71
|
+
serviceUUIDs: [nanoFTSServiceUuid],
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
}, 1000);
|
|
75
|
+
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
observer.next({
|
|
78
|
+
type: "add",
|
|
79
|
+
descriptor: aTransportBleDevice({ id: deviceIdC }),
|
|
80
|
+
});
|
|
81
|
+
}, 2000);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
describe("and when no filters are applied to the scanning", () => {
|
|
85
|
+
it("should update the list of scanned devices for each new scanned device", async () => {
|
|
86
|
+
const { result } = renderHook(() =>
|
|
87
|
+
useBleDevicesScanning({
|
|
88
|
+
bleTransportListen: setupMockBleTransportListen(
|
|
89
|
+
mockEmitValuesByObserver
|
|
90
|
+
),
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
expect(result.current.scannedDevices).toHaveLength(1);
|
|
95
|
+
expect(result.current.scannedDevices[0].deviceId).toBe(deviceIdA);
|
|
96
|
+
// The model was correctly deduced from the ble spec
|
|
97
|
+
expect(result.current.scannedDevices[0].deviceModel.id).toBe(
|
|
98
|
+
DeviceModelId.nanoX
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
await act(async () => {
|
|
102
|
+
jest.advanceTimersByTime(1000);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(result.current.scannedDevices).toHaveLength(2);
|
|
106
|
+
expect(result.current.scannedDevices[1].deviceId).toBe(deviceIdB);
|
|
107
|
+
expect(result.current.scannedDevices[1].deviceModel.id).toBe(
|
|
108
|
+
DeviceModelId.nanoFTS
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("and when filterModelIds is not null", () => {
|
|
114
|
+
it("should filter the scanning result by the given model ids", async () => {
|
|
115
|
+
const { result } = renderHook(() =>
|
|
116
|
+
useBleDevicesScanning({
|
|
117
|
+
bleTransportListen: setupMockBleTransportListen(
|
|
118
|
+
mockEmitValuesByObserver
|
|
119
|
+
),
|
|
120
|
+
filterByModelIds: [DeviceModelId.nanoX],
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// The first scanned device was a nanoX
|
|
125
|
+
expect(result.current.scannedDevices).toHaveLength(1);
|
|
126
|
+
expect(result.current.scannedDevices[0].deviceId).toBe(deviceIdA);
|
|
127
|
+
// The model was correctly deduced from the ble spec
|
|
128
|
+
expect(result.current.scannedDevices[0].deviceModel.id).toBe(
|
|
129
|
+
DeviceModelId.nanoX
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
await act(async () => {
|
|
133
|
+
jest.advanceTimersByTime(1000);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// The second scanned device was a nanoFTS, and was filtered out
|
|
137
|
+
expect(result.current.scannedDevices).toHaveLength(1);
|
|
138
|
+
|
|
139
|
+
await act(async () => {
|
|
140
|
+
jest.advanceTimersByTime(1000);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// The third scanned device was a nanoX
|
|
144
|
+
expect(result.current.scannedDevices).toHaveLength(2);
|
|
145
|
+
expect(result.current.scannedDevices[1].deviceId).toBe(deviceIdC);
|
|
146
|
+
expect(result.current.scannedDevices[1].deviceModel.id).toBe(
|
|
147
|
+
DeviceModelId.nanoX
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("and when the hook consumer stops the scanning", () => {
|
|
153
|
+
it("should stop the scanning", async () => {
|
|
154
|
+
let stopBleScanning = false;
|
|
155
|
+
|
|
156
|
+
const { result, rerender } = renderHook(() =>
|
|
157
|
+
useBleDevicesScanning({
|
|
158
|
+
bleTransportListen: setupMockBleTransportListen(
|
|
159
|
+
mockEmitValuesByObserver
|
|
160
|
+
),
|
|
161
|
+
stopBleScanning,
|
|
162
|
+
})
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// At first the scanning finds device(s)
|
|
166
|
+
expect(result.current.scannedDevices).toHaveLength(1);
|
|
167
|
+
expect(result.current.scannedDevices[0].deviceId).toBe(deviceIdA);
|
|
168
|
+
expect(result.current.scannedDevices[0].deviceModel.id).toBe(
|
|
169
|
+
DeviceModelId.nanoX
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Then the consumer stops the scanning
|
|
173
|
+
stopBleScanning = true;
|
|
174
|
+
rerender({
|
|
175
|
+
bleTransportListen: setupMockBleTransportListen(
|
|
176
|
+
mockEmitValuesByObserver
|
|
177
|
+
),
|
|
178
|
+
stopBleScanning,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await act(async () => {
|
|
182
|
+
jest.advanceTimersByTime(2000);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// It should not find any new devices
|
|
186
|
+
expect(result.current.scannedDevices).toHaveLength(1);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
describe("When the same device is being scanned several times", () => {
|
|
192
|
+
const deviceIdA = "ID_A";
|
|
193
|
+
const deviceIdB = "ID_B";
|
|
194
|
+
|
|
195
|
+
const mockEmitValuesByObserver = (observer: bleTransportListenObserver) => {
|
|
196
|
+
observer.next({
|
|
197
|
+
type: "add",
|
|
198
|
+
descriptor: aTransportBleDevice({ id: deviceIdA }),
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
observer.next({
|
|
203
|
+
type: "add",
|
|
204
|
+
descriptor: aTransportBleDevice({ id: deviceIdA }),
|
|
205
|
+
});
|
|
206
|
+
}, 1000);
|
|
207
|
+
|
|
208
|
+
setTimeout(() => {
|
|
209
|
+
observer.next({
|
|
210
|
+
type: "add",
|
|
211
|
+
descriptor: aTransportBleDevice({ id: deviceIdB }),
|
|
212
|
+
});
|
|
213
|
+
}, 2000);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
it("should update the list of scanned devices without any duplicate", async () => {
|
|
217
|
+
const { result } = renderHook(() =>
|
|
218
|
+
useBleDevicesScanning({
|
|
219
|
+
bleTransportListen: setupMockBleTransportListen(
|
|
220
|
+
mockEmitValuesByObserver
|
|
221
|
+
),
|
|
222
|
+
})
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// The first time it gets the device from the scanning
|
|
226
|
+
expect(result.current.scannedDevices).toHaveLength(1);
|
|
227
|
+
expect(result.current.scannedDevices[0].deviceId).toBe(deviceIdA);
|
|
228
|
+
expect(result.current.scannedDevices[0].deviceModel.id).toBe(
|
|
229
|
+
DeviceModelId.nanoX
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
await act(async () => {
|
|
233
|
+
jest.advanceTimersByTime(1000);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// The second time it gets the same device from the scanning
|
|
237
|
+
// It should not have been added to the list
|
|
238
|
+
expect(result.current.scannedDevices).toHaveLength(1);
|
|
239
|
+
|
|
240
|
+
await act(async () => {
|
|
241
|
+
jest.advanceTimersByTime(1000);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// The third time it gets a new device
|
|
245
|
+
expect(result.current.scannedDevices).toHaveLength(2);
|
|
246
|
+
expect(result.current.scannedDevices[1].deviceId).toBe(deviceIdB);
|
|
247
|
+
expect(result.current.scannedDevices[1].deviceModel.id).toBe(
|
|
248
|
+
DeviceModelId.nanoX
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from "react";
|
|
2
|
+
import { Observable, of } from "rxjs";
|
|
3
|
+
import { concatAll } from "rxjs/operators";
|
|
4
|
+
import { getInfosForServiceUuid } from "@ledgerhq/devices";
|
|
5
|
+
import type { DeviceModelId } from "@ledgerhq/types-devices";
|
|
6
|
+
import type {
|
|
7
|
+
Observer as TransportObserver,
|
|
8
|
+
Subscription as TransportSubscription,
|
|
9
|
+
DescriptorEvent,
|
|
10
|
+
DescriptorEventType,
|
|
11
|
+
} from "@ledgerhq/hw-transport";
|
|
12
|
+
import { TransportBleDevice, ScannedDevice, BleError } from "../types";
|
|
13
|
+
|
|
14
|
+
export type ScanningBleError = BleError | null;
|
|
15
|
+
|
|
16
|
+
export type UseBleDevicesScanningResult = {
|
|
17
|
+
scannedDevices: ScannedDevice[];
|
|
18
|
+
scanningBleError: ScanningBleError;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type UseBleDevicesScanningDependencies = {
|
|
22
|
+
bleTransportListen: (
|
|
23
|
+
observer: TransportObserver<DescriptorEvent<TransportBleDevice | null>>
|
|
24
|
+
) => TransportSubscription;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type UseBleDevicesScanningOptions = {
|
|
28
|
+
stopBleScanning?: boolean;
|
|
29
|
+
filterByModelIds?: DeviceModelId[];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const DEFAULT_DEVICE_NAME = "Device";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Scans the BLE devices around the user
|
|
36
|
+
* @param filterByModelIds An array of model ids to filter on
|
|
37
|
+
* @param stopBleScanning Flag to stop or continue the scanning
|
|
38
|
+
* @returns An object containing:
|
|
39
|
+
* - scannedDevices: list of ScannedDevice found by the scanning
|
|
40
|
+
* - scanningBleError: if an error occurred, a BleError, otherwise null
|
|
41
|
+
*/
|
|
42
|
+
export const useBleDevicesScanning = ({
|
|
43
|
+
bleTransportListen,
|
|
44
|
+
stopBleScanning,
|
|
45
|
+
filterByModelIds,
|
|
46
|
+
}: UseBleDevicesScanningDependencies &
|
|
47
|
+
UseBleDevicesScanningOptions): UseBleDevicesScanningResult => {
|
|
48
|
+
const [scanningBleError, setScanningBleError] =
|
|
49
|
+
useState<ScanningBleError>(null);
|
|
50
|
+
const [scannedDevices, setScannedDevices] = useState<ScannedDevice[]>([]);
|
|
51
|
+
// To check for duplicates. The ref will persist for the full lifetime of the component
|
|
52
|
+
// in which the hook is called.
|
|
53
|
+
// We could use the current value of scannedDevices inside setScannedDevices
|
|
54
|
+
// but the check would only be done at the end of the process when calling setScannedDevices.
|
|
55
|
+
const scannedDevicesRef = useRef<ScannedDevice[]>([]);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (stopBleScanning) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const bleScanningSource = new Observable(bleTransportListen);
|
|
63
|
+
|
|
64
|
+
// Concat to flatten the events emitted by the scanning, that could arrive too fast
|
|
65
|
+
const sub = of(bleScanningSource)
|
|
66
|
+
.pipe(concatAll())
|
|
67
|
+
.subscribe({
|
|
68
|
+
next: (event: {
|
|
69
|
+
type: DescriptorEventType;
|
|
70
|
+
descriptor: TransportBleDevice | null;
|
|
71
|
+
}) => {
|
|
72
|
+
setScanningBleError(null);
|
|
73
|
+
const { type, descriptor } = event;
|
|
74
|
+
|
|
75
|
+
if (type === "add" && descriptor) {
|
|
76
|
+
const transportDevice = descriptor;
|
|
77
|
+
|
|
78
|
+
const isScannedDeviceDuplicate = scannedDevicesRef.current.some(
|
|
79
|
+
(d) => d.deviceId === transportDevice.id
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Avoiding duplicates
|
|
83
|
+
if (isScannedDeviceDuplicate) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
transportDevice.serviceUUIDs &&
|
|
89
|
+
transportDevice.serviceUUIDs.length > 0
|
|
90
|
+
) {
|
|
91
|
+
const bleInfo = getInfosForServiceUuid(
|
|
92
|
+
transportDevice.serviceUUIDs[0]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (!bleInfo) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Filters on the model ids, if asked
|
|
100
|
+
if (
|
|
101
|
+
filterByModelIds &&
|
|
102
|
+
!filterByModelIds.includes(bleInfo.deviceModel.id)
|
|
103
|
+
) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const newScannedDevice = {
|
|
108
|
+
deviceModel: bleInfo.deviceModel,
|
|
109
|
+
deviceName:
|
|
110
|
+
transportDevice.localName ??
|
|
111
|
+
transportDevice.name ??
|
|
112
|
+
DEFAULT_DEVICE_NAME,
|
|
113
|
+
deviceId: transportDevice.id,
|
|
114
|
+
bleRssi: transportDevice.rssi,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
setScannedDevices((scannedDevices) => [
|
|
118
|
+
...scannedDevices,
|
|
119
|
+
newScannedDevice,
|
|
120
|
+
]);
|
|
121
|
+
scannedDevicesRef.current.push(newScannedDevice);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
error: (error: BleError) => {
|
|
126
|
+
// TODO: we should be sure to have a BleError
|
|
127
|
+
if (error.errorCode) {
|
|
128
|
+
setScanningBleError(error);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return () => {
|
|
134
|
+
sub.unsubscribe();
|
|
135
|
+
};
|
|
136
|
+
}, [
|
|
137
|
+
bleTransportListen,
|
|
138
|
+
stopBleScanning,
|
|
139
|
+
filterByModelIds,
|
|
140
|
+
setScannedDevices,
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
scannedDevices,
|
|
145
|
+
scanningBleError,
|
|
146
|
+
};
|
|
147
|
+
};
|
package/src/ble/types.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { DeviceModel } from "@ledgerhq/types-devices";
|
|
2
|
+
|
|
3
|
+
// Inspired by Device class from react-native-ble-plx
|
|
4
|
+
// Should be exported from somewhere else
|
|
5
|
+
// TODO: Update after BIM transport is finished
|
|
6
|
+
export type TransportBleDevice = {
|
|
7
|
+
// Device identifier: MAC address on Android and UUID on iOS.
|
|
8
|
+
id: string;
|
|
9
|
+
// Device name
|
|
10
|
+
name: string | null;
|
|
11
|
+
// User friendly name of device.
|
|
12
|
+
localName: string | null;
|
|
13
|
+
// Current Received Signal Strength Indication of device
|
|
14
|
+
rssi: number | null;
|
|
15
|
+
// Current Maximum Transmission Unit for this device.
|
|
16
|
+
// When device is not connected default value of 23 is used.
|
|
17
|
+
mtu: number;
|
|
18
|
+
// List of available services visible during scanning.
|
|
19
|
+
serviceUUIDs: string[] | null;
|
|
20
|
+
// Allows any other properties
|
|
21
|
+
[otherOptions: string]: unknown;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Inspired by BleError class from react-native-ble-plx
|
|
25
|
+
// BleError should be an error class which is guaranteed to be thrown by all functions
|
|
26
|
+
// by our different implementations of Transport or at least of ble implementation of Transport
|
|
27
|
+
// TODO: BleError should probably be a class and update after BIM transport is finished
|
|
28
|
+
export type BleError = Error & {
|
|
29
|
+
// Platform independent error code.
|
|
30
|
+
// It is defined as an enum named BleErrorCode in react-native-ble-plx
|
|
31
|
+
// We should have our own mapping
|
|
32
|
+
errorCode: number;
|
|
33
|
+
// Allows any other properties
|
|
34
|
+
[otherOptions: string]: unknown;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type ScannedDevice = {
|
|
38
|
+
deviceId: string;
|
|
39
|
+
deviceName: string;
|
|
40
|
+
bleRssi: number | null;
|
|
41
|
+
deviceModel: DeviceModel;
|
|
42
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BigNumber } from "bignumber.js";
|
|
2
|
+
import { log } from "@ledgerhq/logs";
|
|
2
3
|
import type { AccountLike, Account } from "@ledgerhq/types-live";
|
|
3
4
|
import { getMainAccount } from "../../account";
|
|
4
5
|
import type { CardanoAccount, Transaction } from "./types";
|
|
@@ -42,6 +43,7 @@ const estimateMaxSpendable = async ({
|
|
|
42
43
|
try {
|
|
43
44
|
typhonTransaction = await buildTransaction(a as CardanoAccount, t);
|
|
44
45
|
} catch (error) {
|
|
46
|
+
log("cardano-error", "Failed to estimate max spendable: " + String(error));
|
|
45
47
|
return new BigNumber(0);
|
|
46
48
|
}
|
|
47
49
|
const transactionAmount = typhonTransaction
|
|
@@ -154,7 +154,7 @@ const cosmos: AppSpec<Transaction> = {
|
|
|
154
154
|
count
|
|
155
155
|
)
|
|
156
156
|
.minus(minAmount.times(2))
|
|
157
|
-
.times(0.
|
|
157
|
+
.times(0.25 * Math.random());
|
|
158
158
|
invariant(remaining.gt(0), "not enough funds in account for delegate");
|
|
159
159
|
const all = data.validators.filter(
|
|
160
160
|
(v) =>
|
|
@@ -298,7 +298,7 @@ const cosmos: AppSpec<Transaction> = {
|
|
|
298
298
|
const amount = (sourceDelegation as CosmosDelegation).amount
|
|
299
299
|
.times(
|
|
300
300
|
// most of the time redelegate all
|
|
301
|
-
Math.random() > 0.
|
|
301
|
+
Math.random() > 0.2 ? 1 : Math.random()
|
|
302
302
|
)
|
|
303
303
|
.integerValue();
|
|
304
304
|
invariant(amount.gt(0), "random amount to be positive");
|
|
@@ -715,7 +715,7 @@ describe("canUnbond", () => {
|
|
|
715
715
|
nominations: [],
|
|
716
716
|
unlockings: [
|
|
717
717
|
...Array(MAX_UNLOCKINGS - 1).map(() => ({
|
|
718
|
-
amount: new BigNumber(
|
|
718
|
+
amount: new BigNumber(100000),
|
|
719
719
|
completionDate: new Date(),
|
|
720
720
|
})),
|
|
721
721
|
],
|
|
@@ -736,7 +736,7 @@ describe("canUnbond", () => {
|
|
|
736
736
|
nominations: [],
|
|
737
737
|
unlockings: [
|
|
738
738
|
...Array(MAX_UNLOCKINGS).map(() => ({
|
|
739
|
-
amount: new BigNumber(
|
|
739
|
+
amount: new BigNumber(100000),
|
|
740
740
|
completionDate: new Date(),
|
|
741
741
|
})),
|
|
742
742
|
],
|
|
@@ -757,7 +757,7 @@ describe("canUnbond", () => {
|
|
|
757
757
|
nominations: [],
|
|
758
758
|
unlockings: [
|
|
759
759
|
...Array(MAX_UNLOCKINGS).map(() => ({
|
|
760
|
-
amount: new BigNumber(
|
|
760
|
+
amount: new BigNumber(100000),
|
|
761
761
|
completionDate: new Date(),
|
|
762
762
|
})),
|
|
763
763
|
],
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../../families/polkadot/logic";
|
|
22
22
|
import { DeviceModelId } from "@ledgerhq/devices";
|
|
23
23
|
|
|
24
|
+
const maxAccounts = 32;
|
|
24
25
|
const currency = getCryptoCurrencyById("polkadot");
|
|
25
26
|
// FIXME Should be replaced with EXISTENTIAL_DEPOSIT_RECOMMENDED_MARGIN in logic.ts
|
|
26
27
|
const POLKADOT_MIN_SAFE = parseCurrencyUnit(currency.units[0], "0.1");
|
|
@@ -52,10 +53,10 @@ const polkadot: AppSpec<Transaction> = {
|
|
|
52
53
|
mutations: [
|
|
53
54
|
{
|
|
54
55
|
name: "send 50%~",
|
|
55
|
-
maxRun:
|
|
56
|
+
maxRun: 4,
|
|
56
57
|
transaction: ({ account, siblings, bridge }) => {
|
|
57
58
|
invariant((account as PolkadotAccount).polkadotResources, "polkadot");
|
|
58
|
-
const sibling = pickSiblings(siblings,
|
|
59
|
+
const sibling = pickSiblings(siblings, maxAccounts);
|
|
59
60
|
let amount = account.spendableBalance
|
|
60
61
|
.div(1.9 + 0.2 * Math.random())
|
|
61
62
|
.integerValue();
|
|
@@ -74,7 +75,7 @@ const polkadot: AppSpec<Transaction> = {
|
|
|
74
75
|
transaction: bridge.createTransaction(account),
|
|
75
76
|
updates: [
|
|
76
77
|
{
|
|
77
|
-
recipient:
|
|
78
|
+
recipient: sibling.freshAddress,
|
|
78
79
|
},
|
|
79
80
|
{
|
|
80
81
|
amount,
|
|
@@ -10,10 +10,14 @@ import type { FeatureId, Feature } from "@ledgerhq/types-live";
|
|
|
10
10
|
|
|
11
11
|
type State = {
|
|
12
12
|
getFeature: (_: FeatureId) => Feature | null;
|
|
13
|
+
overrideFeature: (_: FeatureId, value: Feature) => void;
|
|
14
|
+
resetFeature: (_: FeatureId) => void;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
const initialState: State = {
|
|
16
18
|
getFeature: (_) => ({ enabled: false }),
|
|
19
|
+
overrideFeature: (_) => {},
|
|
20
|
+
resetFeature: (_) => {},
|
|
17
21
|
};
|
|
18
22
|
|
|
19
23
|
const FeatureFlagsContext = createContext<State>(initialState);
|
|
@@ -24,11 +28,15 @@ export function useFeatureFlags(): State {
|
|
|
24
28
|
|
|
25
29
|
type Props = {
|
|
26
30
|
getFeature: (_: FeatureId) => Feature | null;
|
|
31
|
+
overrideFeature: (_: FeatureId, value: Feature) => void;
|
|
32
|
+
resetFeature: (_: FeatureId) => void;
|
|
27
33
|
children?: ReactNode;
|
|
28
34
|
};
|
|
29
35
|
|
|
30
36
|
export function FeatureFlagsProvider({
|
|
31
37
|
getFeature,
|
|
38
|
+
overrideFeature,
|
|
39
|
+
resetFeature,
|
|
32
40
|
children,
|
|
33
41
|
}: Props): JSX.Element {
|
|
34
42
|
const [state, setState] = useState<State>(initialState);
|
|
@@ -37,8 +45,10 @@ export function FeatureFlagsProvider({
|
|
|
37
45
|
setState((prev) => ({
|
|
38
46
|
...prev,
|
|
39
47
|
getFeature,
|
|
48
|
+
overrideFeature,
|
|
49
|
+
resetFeature,
|
|
40
50
|
}));
|
|
41
|
-
}, [getFeature]);
|
|
51
|
+
}, [getFeature, overrideFeature, resetFeature]);
|
|
42
52
|
|
|
43
53
|
return (
|
|
44
54
|
<FeatureFlagsContext.Provider value={state}>
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
1
2
|
import { useFeatureFlags } from "./provider";
|
|
2
3
|
import { FeatureId, Feature } from "@ledgerhq/types-live";
|
|
3
4
|
|
|
4
5
|
const useFeature = (key: FeatureId): Feature | null => {
|
|
5
6
|
const featureFlags = useFeatureFlags();
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
const value = useMemo(
|
|
8
|
+
() => featureFlags.getFeature(key),
|
|
9
|
+
[featureFlags, key]
|
|
10
|
+
);
|
|
11
|
+
return value;
|
|
8
12
|
};
|
|
9
13
|
|
|
10
14
|
export default useFeature;
|
package/src/generated/account.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import algorand from "../families/algorand/account";
|
|
2
|
-
|
|
3
2
|
import bitcoin from "../families/bitcoin/account";
|
|
4
|
-
|
|
5
3
|
import cosmos from "../families/cosmos/account";
|
|
6
|
-
|
|
7
4
|
import crypto_org from "../families/crypto_org/account";
|
|
8
|
-
|
|
9
5
|
import elrond from "../families/elrond/account";
|
|
10
|
-
|
|
11
6
|
import filecoin from "../families/filecoin/account";
|
|
12
|
-
|
|
13
7
|
import osmosis from "../families/osmosis/account";
|
|
14
|
-
|
|
15
8
|
import polkadot from "../families/polkadot/account";
|
|
16
9
|
|
|
17
|
-
|
|
18
10
|
export default {
|
|
19
11
|
algorand,
|
|
20
12
|
bitcoin,
|
|
@@ -1,40 +1,22 @@
|
|
|
1
1
|
import algorand from "../../families/algorand/bridge/js";
|
|
2
|
-
|
|
3
2
|
import bitcoin from "../../families/bitcoin/bridge/js";
|
|
4
|
-
|
|
5
3
|
import cardano from "../../families/cardano/bridge/js";
|
|
6
|
-
|
|
7
4
|
import celo from "../../families/celo/bridge/js";
|
|
8
|
-
|
|
9
5
|
import cosmos from "../../families/cosmos/bridge/js";
|
|
10
|
-
|
|
11
6
|
import crypto_org from "../../families/crypto_org/bridge/js";
|
|
12
|
-
|
|
13
7
|
import elrond from "../../families/elrond/bridge/js";
|
|
14
|
-
|
|
15
8
|
import ethereum from "../../families/ethereum/bridge/js";
|
|
16
|
-
|
|
17
9
|
import filecoin from "../../families/filecoin/bridge/js";
|
|
18
|
-
|
|
19
10
|
import hedera from "../../families/hedera/bridge/js";
|
|
20
|
-
|
|
21
11
|
import neo from "../../families/neo/bridge/js";
|
|
22
|
-
|
|
23
12
|
import osmosis from "../../families/osmosis/bridge/js";
|
|
24
|
-
|
|
25
13
|
import polkadot from "../../families/polkadot/bridge/js";
|
|
26
|
-
|
|
27
14
|
import ripple from "../../families/ripple/bridge/js";
|
|
28
|
-
|
|
29
15
|
import solana from "../../families/solana/bridge/js";
|
|
30
|
-
|
|
31
16
|
import stellar from "../../families/stellar/bridge/js";
|
|
32
|
-
|
|
33
17
|
import tezos from "../../families/tezos/bridge/js";
|
|
34
|
-
|
|
35
18
|
import tron from "../../families/tron/bridge/js";
|
|
36
19
|
|
|
37
|
-
|
|
38
20
|
export default {
|
|
39
21
|
algorand,
|
|
40
22
|
bitcoin,
|
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import algorand from "../../families/algorand/bridge/mock";
|
|
2
|
-
|
|
3
2
|
import bitcoin from "../../families/bitcoin/bridge/mock";
|
|
4
|
-
|
|
5
3
|
import cosmos from "../../families/cosmos/bridge/mock";
|
|
6
|
-
|
|
7
4
|
import ethereum from "../../families/ethereum/bridge/mock";
|
|
8
|
-
|
|
9
5
|
import ripple from "../../families/ripple/bridge/mock";
|
|
10
|
-
|
|
11
6
|
import solana from "../../families/solana/bridge/mock";
|
|
12
|
-
|
|
13
7
|
import stellar from "../../families/stellar/bridge/mock";
|
|
14
|
-
|
|
15
8
|
import tezos from "../../families/tezos/bridge/mock";
|
|
16
9
|
|
|
17
|
-
|
|
18
10
|
export default {
|
|
19
11
|
algorand,
|
|
20
12
|
bitcoin,
|