@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.
Files changed (101) hide show
  1. package/README.md +1 -1
  2. package/lib/ble/hooks/useBleDevicePairing.d.ts +18 -0
  3. package/lib/ble/hooks/useBleDevicePairing.d.ts.map +1 -0
  4. package/lib/ble/hooks/useBleDevicePairing.js +57 -0
  5. package/lib/ble/hooks/useBleDevicePairing.js.map +1 -0
  6. package/lib/ble/hooks/useBleDevicePairing.test.d.ts +2 -0
  7. package/lib/ble/hooks/useBleDevicePairing.test.d.ts.map +1 -0
  8. package/lib/ble/hooks/useBleDevicePairing.test.js +159 -0
  9. package/lib/ble/hooks/useBleDevicePairing.test.js.map +1 -0
  10. package/lib/ble/hooks/useBleDevicesScanning.d.ts +25 -0
  11. package/lib/ble/hooks/useBleDevicesScanning.d.ts.map +1 -0
  12. package/lib/ble/hooks/useBleDevicesScanning.js +117 -0
  13. package/lib/ble/hooks/useBleDevicesScanning.js.map +1 -0
  14. package/lib/ble/hooks/useBleDevicesScanning.test.d.ts +2 -0
  15. package/lib/ble/hooks/useBleDevicesScanning.test.d.ts.map +1 -0
  16. package/lib/ble/hooks/useBleDevicesScanning.test.js +281 -0
  17. package/lib/ble/hooks/useBleDevicesScanning.test.js.map +1 -0
  18. package/lib/ble/types.d.ts +21 -0
  19. package/lib/ble/types.d.ts.map +1 -0
  20. package/lib/ble/types.js +3 -0
  21. package/lib/ble/types.js.map +1 -0
  22. package/lib/families/cardano/js-estimateMaxSpendable.d.ts.map +1 -1
  23. package/lib/families/cardano/js-estimateMaxSpendable.js +2 -0
  24. package/lib/families/cardano/js-estimateMaxSpendable.js.map +1 -1
  25. package/lib/families/cosmos/specs.js +2 -2
  26. package/lib/families/cosmos/specs.js.map +1 -1
  27. package/lib/families/elrond/specs.js +1 -1
  28. package/lib/families/elrond/specs.js.map +1 -1
  29. package/lib/families/polkadot/bridge.integration.test.js +3 -3
  30. package/lib/families/polkadot/bridge.integration.test.js.map +1 -1
  31. package/lib/families/polkadot/specs.d.ts.map +1 -1
  32. package/lib/families/polkadot/specs.js +4 -3
  33. package/lib/families/polkadot/specs.js.map +1 -1
  34. package/lib/featureFlags/provider.d.ts +5 -1
  35. package/lib/featureFlags/provider.d.ts.map +1 -1
  36. package/lib/featureFlags/provider.js +6 -4
  37. package/lib/featureFlags/provider.js.map +1 -1
  38. package/lib/featureFlags/useFeature.d.ts.map +1 -1
  39. package/lib/featureFlags/useFeature.js +3 -1
  40. package/lib/featureFlags/useFeature.js.map +1 -1
  41. package/lib/generated/account.d.ts.map +1 -1
  42. package/lib/generated/account.js.map +1 -1
  43. package/lib/generated/bridge/js.d.ts.map +1 -1
  44. package/lib/generated/bridge/js.js.map +1 -1
  45. package/lib/generated/bridge/mock.d.ts.map +1 -1
  46. package/lib/generated/bridge/mock.js.map +1 -1
  47. package/lib/generated/cli-transaction.d.ts.map +1 -1
  48. package/lib/generated/cli-transaction.js.map +1 -1
  49. package/lib/generated/deviceTransactionConfig.d.ts.map +1 -1
  50. package/lib/generated/deviceTransactionConfig.js.map +1 -1
  51. package/lib/generated/exchange.d.ts.map +1 -1
  52. package/lib/generated/exchange.js.map +1 -1
  53. package/lib/generated/hw-getAddress.d.ts.map +1 -1
  54. package/lib/generated/hw-getAddress.js.map +1 -1
  55. package/lib/generated/hw-signMessage.d.ts.map +1 -1
  56. package/lib/generated/hw-signMessage.js.map +1 -1
  57. package/lib/generated/mock.d.ts.map +1 -1
  58. package/lib/generated/mock.js.map +1 -1
  59. package/lib/generated/platformAdapter.d.ts.map +1 -1
  60. package/lib/generated/platformAdapter.js.map +1 -1
  61. package/lib/generated/presync.d.ts.map +1 -1
  62. package/lib/generated/presync.js.map +1 -1
  63. package/lib/generated/specs.d.ts.map +1 -1
  64. package/lib/generated/specs.js.map +1 -1
  65. package/lib/generated/speculos-deviceActions.d.ts.map +1 -1
  66. package/lib/generated/speculos-deviceActions.js.map +1 -1
  67. package/lib/generated/transaction.d.ts.map +1 -1
  68. package/lib/generated/transaction.js.map +1 -1
  69. package/lib/generated/types.d.ts.map +1 -1
  70. package/lib/platform/react.d.ts.map +1 -1
  71. package/lib/platform/react.js +6 -3
  72. package/lib/platform/react.js.map +1 -1
  73. package/package.json +18 -18
  74. package/src/ble/hooks/useBleDevicePairing.test.ts +91 -0
  75. package/src/ble/hooks/useBleDevicePairing.ts +51 -0
  76. package/src/ble/hooks/useBleDevicesScanning.test.ts +252 -0
  77. package/src/ble/hooks/useBleDevicesScanning.ts +147 -0
  78. package/src/ble/types.ts +42 -0
  79. package/src/families/cardano/js-estimateMaxSpendable.ts +2 -0
  80. package/src/families/cosmos/specs.ts +2 -2
  81. package/src/families/elrond/specs.ts +1 -1
  82. package/src/families/polkadot/bridge.integration.test.ts +3 -3
  83. package/src/families/polkadot/specs.ts +4 -3
  84. package/src/featureFlags/provider.tsx +11 -1
  85. package/src/featureFlags/useFeature.ts +6 -2
  86. package/src/generated/account.ts +0 -8
  87. package/src/generated/bridge/js.ts +0 -18
  88. package/src/generated/bridge/mock.ts +0 -8
  89. package/src/generated/cli-transaction.ts +0 -17
  90. package/src/generated/deviceTransactionConfig.ts +15 -31
  91. package/src/generated/exchange.ts +0 -5
  92. package/src/generated/hw-getAddress.ts +0 -18
  93. package/src/generated/hw-signMessage.ts +0 -3
  94. package/src/generated/mock.ts +0 -3
  95. package/src/generated/platformAdapter.ts +0 -4
  96. package/src/generated/presync.ts +0 -1
  97. package/src/generated/specs.ts +0 -17
  98. package/src/generated/speculos-deviceActions.ts +0 -17
  99. package/src/generated/transaction.ts +0 -18
  100. package/src/generated/types.ts +7 -4
  101. 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
+ };
@@ -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.5 * Math.random());
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.3 ? 1 : Math.random()
301
+ Math.random() > 0.2 ? 1 : Math.random()
302
302
  )
303
303
  .integerValue();
304
304
  invariant(amount.gt(0), "random amount to be positive");
@@ -54,7 +54,7 @@ const elrondSpec: AppSpec<Transaction> = {
54
54
  transaction: bridge.createTransaction(account),
55
55
  updates: [
56
56
  {
57
- recipient: pickSiblings(siblings, 1).freshAddress,
57
+ recipient: sibling.freshAddress,
58
58
  },
59
59
  {
60
60
  amount,
@@ -715,7 +715,7 @@ describe("canUnbond", () => {
715
715
  nominations: [],
716
716
  unlockings: [
717
717
  ...Array(MAX_UNLOCKINGS - 1).map(() => ({
718
- amount: new BigNumber(Math.random()),
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(Math.random()),
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(Math.random()),
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: 2,
56
+ maxRun: 4,
56
57
  transaction: ({ account, siblings, bridge }) => {
57
58
  invariant((account as PolkadotAccount).polkadotResources, "polkadot");
58
- const sibling = pickSiblings(siblings, 2);
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: pickSiblings(siblings, 1).freshAddress,
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
- return featureFlags.getFeature(key);
7
+ const value = useMemo(
8
+ () => featureFlags.getFeature(key),
9
+ [featureFlags, key]
10
+ );
11
+ return value;
8
12
  };
9
13
 
10
14
  export default useFeature;
@@ -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,