@ledgerhq/live-common 34.54.0-nightly.20251127130943 → 34.54.0-nightly.20251128145758
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/bridge/generic-alpaca/accountBridge.d.ts +1 -1
- package/lib/bridge/generic-alpaca/accountBridge.d.ts.map +1 -1
- package/lib/bridge/generic-alpaca/accountBridge.js +2 -1
- package/lib/bridge/generic-alpaca/accountBridge.js.map +1 -1
- package/lib/bridge/generic-alpaca/currencyBridge.d.ts.map +1 -1
- package/lib/bridge/generic-alpaca/currencyBridge.js +2 -0
- package/lib/bridge/generic-alpaca/currencyBridge.js.map +1 -1
- package/lib/bridge/generic-alpaca/postSync.d.ts +16 -0
- package/lib/bridge/generic-alpaca/postSync.d.ts.map +1 -0
- package/lib/bridge/generic-alpaca/postSync.js +44 -0
- package/lib/bridge/generic-alpaca/postSync.js.map +1 -0
- package/lib/bridge/generic-alpaca/utils.d.ts.map +1 -1
- package/lib/bridge/generic-alpaca/utils.js +1 -0
- package/lib/bridge/generic-alpaca/utils.js.map +1 -1
- package/lib/e2e/data/deviceLabelsData.d.ts.map +1 -1
- package/lib/e2e/data/deviceLabelsData.js +1 -0
- package/lib/e2e/data/deviceLabelsData.js.map +1 -1
- package/lib/e2e/data/regexes.d.ts +2 -0
- package/lib/e2e/data/regexes.d.ts.map +1 -0
- package/lib/e2e/data/regexes.js +5 -0
- package/lib/e2e/data/regexes.js.map +1 -0
- package/lib/e2e/enum/Device.d.ts +1 -0
- package/lib/e2e/enum/Device.d.ts.map +1 -1
- package/lib/e2e/enum/Device.js +1 -0
- package/lib/e2e/enum/Device.js.map +1 -1
- package/lib/e2e/families/cardano.d.ts.map +1 -1
- package/lib/e2e/families/cardano.js +29 -14
- package/lib/e2e/families/cardano.js.map +1 -1
- package/lib/e2e/index.d.ts +4 -0
- package/lib/e2e/index.d.ts.map +1 -1
- package/lib/e2e/index.js +21 -1
- package/lib/e2e/index.js.map +1 -1
- package/lib/e2e/speculos.d.ts.map +1 -1
- package/lib/e2e/speculos.js +31 -4
- package/lib/e2e/speculos.js.map +1 -1
- package/lib/e2e/speculosAppVersion.d.ts.map +1 -1
- package/lib/e2e/speculosAppVersion.js +6 -2
- package/lib/e2e/speculosAppVersion.js.map +1 -1
- package/lib/e2e/swap.d.ts.map +1 -1
- package/lib/e2e/swap.js +7 -6
- package/lib/e2e/swap.js.map +1 -1
- package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
- package/lib/featureFlags/defaultFeatures.js +13 -0
- package/lib/featureFlags/defaultFeatures.js.map +1 -1
- package/lib/featureFlags/stakePrograms/index.js +4 -4
- package/lib/featureFlags/stakePrograms/index.js.map +1 -1
- package/lib/featureFlags/useFeature.d.ts +1 -1
- package/lib/featureFlags/useFeature.d.ts.map +1 -1
- package/lib/load/speculos.d.ts.map +1 -1
- package/lib/load/speculos.js +1 -0
- package/lib/load/speculos.js.map +1 -1
- package/lib-es/bridge/generic-alpaca/accountBridge.d.ts +1 -1
- package/lib-es/bridge/generic-alpaca/accountBridge.d.ts.map +1 -1
- package/lib-es/bridge/generic-alpaca/accountBridge.js +2 -1
- package/lib-es/bridge/generic-alpaca/accountBridge.js.map +1 -1
- package/lib-es/bridge/generic-alpaca/currencyBridge.d.ts.map +1 -1
- package/lib-es/bridge/generic-alpaca/currencyBridge.js +2 -0
- package/lib-es/bridge/generic-alpaca/currencyBridge.js.map +1 -1
- package/lib-es/bridge/generic-alpaca/postSync.d.ts +16 -0
- package/lib-es/bridge/generic-alpaca/postSync.d.ts.map +1 -0
- package/lib-es/bridge/generic-alpaca/postSync.js +37 -0
- package/lib-es/bridge/generic-alpaca/postSync.js.map +1 -0
- package/lib-es/bridge/generic-alpaca/utils.d.ts.map +1 -1
- package/lib-es/bridge/generic-alpaca/utils.js +1 -0
- package/lib-es/bridge/generic-alpaca/utils.js.map +1 -1
- package/lib-es/e2e/data/deviceLabelsData.d.ts.map +1 -1
- package/lib-es/e2e/data/deviceLabelsData.js +1 -0
- package/lib-es/e2e/data/deviceLabelsData.js.map +1 -1
- package/lib-es/e2e/data/regexes.d.ts +2 -0
- package/lib-es/e2e/data/regexes.d.ts.map +1 -0
- package/lib-es/e2e/data/regexes.js +2 -0
- package/lib-es/e2e/data/regexes.js.map +1 -0
- package/lib-es/e2e/enum/Device.d.ts +1 -0
- package/lib-es/e2e/enum/Device.d.ts.map +1 -1
- package/lib-es/e2e/enum/Device.js +1 -0
- package/lib-es/e2e/enum/Device.js.map +1 -1
- package/lib-es/e2e/families/cardano.d.ts.map +1 -1
- package/lib-es/e2e/families/cardano.js +29 -14
- package/lib-es/e2e/families/cardano.js.map +1 -1
- package/lib-es/e2e/index.d.ts +4 -0
- package/lib-es/e2e/index.d.ts.map +1 -1
- package/lib-es/e2e/index.js +16 -0
- package/lib-es/e2e/index.js.map +1 -1
- package/lib-es/e2e/speculos.d.ts.map +1 -1
- package/lib-es/e2e/speculos.js +31 -4
- package/lib-es/e2e/speculos.js.map +1 -1
- package/lib-es/e2e/speculosAppVersion.d.ts.map +1 -1
- package/lib-es/e2e/speculosAppVersion.js +6 -2
- package/lib-es/e2e/speculosAppVersion.js.map +1 -1
- package/lib-es/e2e/swap.d.ts.map +1 -1
- package/lib-es/e2e/swap.js +7 -6
- package/lib-es/e2e/swap.js.map +1 -1
- package/lib-es/featureFlags/defaultFeatures.d.ts.map +1 -1
- package/lib-es/featureFlags/defaultFeatures.js +13 -0
- package/lib-es/featureFlags/defaultFeatures.js.map +1 -1
- package/lib-es/featureFlags/stakePrograms/index.js +4 -4
- package/lib-es/featureFlags/stakePrograms/index.js.map +1 -1
- package/lib-es/featureFlags/useFeature.d.ts +1 -1
- package/lib-es/featureFlags/useFeature.d.ts.map +1 -1
- package/lib-es/load/speculos.d.ts.map +1 -1
- package/lib-es/load/speculos.js +1 -0
- package/lib-es/load/speculos.js.map +1 -1
- package/package.json +58 -58
- package/src/bridge/generic-alpaca/accountBridge.ts +3 -2
- package/src/bridge/generic-alpaca/currencyBridge.ts +2 -0
- package/src/bridge/generic-alpaca/postSync.test.ts +97 -0
- package/src/bridge/generic-alpaca/postSync.ts +42 -0
- package/src/bridge/generic-alpaca/utils.test.ts +3 -0
- package/src/bridge/generic-alpaca/utils.ts +1 -0
- package/src/e2e/data/deviceLabelsData.ts +1 -0
- package/src/e2e/data/regexes.ts +1 -0
- package/src/e2e/enum/Device.ts +1 -0
- package/src/e2e/families/cardano.ts +32 -14
- package/src/e2e/index.ts +20 -0
- package/src/e2e/speculos.ts +35 -4
- package/src/e2e/speculosAppVersion.ts +8 -2
- package/src/e2e/swap.ts +8 -7
- package/src/featureFlags/defaultFeatures.ts +13 -0
- package/src/featureFlags/stakePrograms/index.ts +4 -4
- package/src/load/speculos.ts +1 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Account } from "@ledgerhq/types-live";
|
|
2
|
+
import { postSync } from "./postSync";
|
|
3
|
+
import BigNumber from "bignumber.js";
|
|
4
|
+
|
|
5
|
+
describe("postSync", () => {
|
|
6
|
+
it("removes confirmed and outdated native operations from the pending pool", () => {
|
|
7
|
+
const initialAccount = {
|
|
8
|
+
operations: [{ hash: "hash0", transactionSequenceNumber: new BigNumber(4), type: "OUT" }],
|
|
9
|
+
pendingOperations: [
|
|
10
|
+
{ hash: "outdated", transactionSequenceNumber: new BigNumber(3), type: "IN" },
|
|
11
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "OUT" },
|
|
12
|
+
{ hash: "hash2", transactionSequenceNumber: new BigNumber(6), type: "IN" },
|
|
13
|
+
],
|
|
14
|
+
} as Account;
|
|
15
|
+
const synced = {
|
|
16
|
+
operations: [
|
|
17
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "OUT" },
|
|
18
|
+
{ hash: "hash0", transactionSequenceNumber: new BigNumber(4), type: "OUT" },
|
|
19
|
+
],
|
|
20
|
+
pendingOperations: [
|
|
21
|
+
{ hash: "outdated", transactionSequenceNumber: new BigNumber(3), type: "IN" },
|
|
22
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "OUT" },
|
|
23
|
+
{ hash: "hash2", transactionSequenceNumber: new BigNumber(6), type: "IN" },
|
|
24
|
+
],
|
|
25
|
+
} as Account;
|
|
26
|
+
|
|
27
|
+
expect(postSync(initialAccount, synced)).toMatchObject({
|
|
28
|
+
operations: [
|
|
29
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "OUT" },
|
|
30
|
+
{ hash: "hash0", transactionSequenceNumber: new BigNumber(4), type: "OUT" },
|
|
31
|
+
],
|
|
32
|
+
pendingOperations: [
|
|
33
|
+
{ hash: "hash2", transactionSequenceNumber: new BigNumber(6), type: "IN" },
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("removes confirmed and outdated token operations from the pending pool", () => {
|
|
39
|
+
const initialAccount = {
|
|
40
|
+
operations: [{ hash: "hash0", transactionSequenceNumber: new BigNumber(4), type: "OUT" }],
|
|
41
|
+
pendingOperations: [
|
|
42
|
+
{ hash: "outdated", transactionSequenceNumber: new BigNumber(3), type: "NONE" },
|
|
43
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "FEES" },
|
|
44
|
+
{ hash: "hash2", transactionSequenceNumber: new BigNumber(6), type: "IN" },
|
|
45
|
+
],
|
|
46
|
+
subAccounts: [
|
|
47
|
+
{
|
|
48
|
+
pendingOperations: [
|
|
49
|
+
{ hash: "outdated", transactionSequenceNumber: new BigNumber(3), type: "IN" },
|
|
50
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "FEES" },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
} as Account;
|
|
55
|
+
const synced = {
|
|
56
|
+
operations: [
|
|
57
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "FEES" },
|
|
58
|
+
{ hash: "hash0", transactionSequenceNumber: new BigNumber(4), type: "OUT" },
|
|
59
|
+
],
|
|
60
|
+
pendingOperations: [
|
|
61
|
+
{ hash: "outdated", transactionSequenceNumber: new BigNumber(3), type: "NONE" },
|
|
62
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "FEES" },
|
|
63
|
+
{ hash: "hash2", transactionSequenceNumber: new BigNumber(6), type: "IN" },
|
|
64
|
+
],
|
|
65
|
+
subAccounts: [
|
|
66
|
+
{
|
|
67
|
+
operations: [{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "OUT" }],
|
|
68
|
+
pendingOperations: [
|
|
69
|
+
{ hash: "outdated", transactionSequenceNumber: new BigNumber(3), type: "IN" },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
} as Account;
|
|
74
|
+
|
|
75
|
+
expect(postSync(initialAccount, synced)).toMatchObject({
|
|
76
|
+
operations: [
|
|
77
|
+
{ hash: "hash1", transactionSequenceNumber: new BigNumber(5), type: "FEES" },
|
|
78
|
+
{ hash: "hash0", transactionSequenceNumber: new BigNumber(4), type: "OUT" },
|
|
79
|
+
],
|
|
80
|
+
pendingOperations: [
|
|
81
|
+
{ hash: "hash2", transactionSequenceNumber: new BigNumber(6), type: "IN" },
|
|
82
|
+
],
|
|
83
|
+
subAccounts: [
|
|
84
|
+
{
|
|
85
|
+
operations: [
|
|
86
|
+
{
|
|
87
|
+
hash: "hash1",
|
|
88
|
+
transactionSequenceNumber: new BigNumber(5),
|
|
89
|
+
type: "OUT",
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
pendingOperations: [],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Account, AccountLike, Operation } from "@ledgerhq/types-live";
|
|
2
|
+
import BigNumber from "bignumber.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* After each sync or scan, remove operations from the pending pools if necessary
|
|
6
|
+
* Operations stay pending if and only if
|
|
7
|
+
* - they are confirmed, i.e. their hash appear in the operation list
|
|
8
|
+
* - they are not outdated, i.e. their sequence number is at least greater than the
|
|
9
|
+
* sequence number of the latest transaction
|
|
10
|
+
* NOTE Compared to the default behaviour
|
|
11
|
+
* - pending operations of token accounts are cleaned up, so we don't see both pending and completed
|
|
12
|
+
* sub operations in the operation details drawer
|
|
13
|
+
* - pending operations are cleaned if their hash already belong to the completed operations, preventing
|
|
14
|
+
* undesired replacement (ex: optimistic operation for self token sending on EVM is incomplete, since
|
|
15
|
+
* it only contains the OUT sub operation)
|
|
16
|
+
*/
|
|
17
|
+
export function postSync(initial: Account, synced: Account): Account {
|
|
18
|
+
const lastOperation = synced.operations.find(op => ["OUT", "FEES"].includes(op.type));
|
|
19
|
+
const latestSequence = lastOperation?.transactionSequenceNumber || new BigNumber(-1);
|
|
20
|
+
|
|
21
|
+
function isPending(account: AccountLike, op: Operation): boolean {
|
|
22
|
+
return (
|
|
23
|
+
// Operation is not confirmed
|
|
24
|
+
!account.operations.some(o => o.hash === op.hash) &&
|
|
25
|
+
// Operation is not outdated
|
|
26
|
+
op.transactionSequenceNumber !== undefined &&
|
|
27
|
+
op.transactionSequenceNumber.gt(latestSequence)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const pendingOperations = initial.pendingOperations.length
|
|
32
|
+
? initial.pendingOperations.filter(op => isPending(synced, op))
|
|
33
|
+
: [];
|
|
34
|
+
const subAccounts = synced.subAccounts?.length
|
|
35
|
+
? synced.subAccounts.map(subAccount => ({
|
|
36
|
+
...subAccount,
|
|
37
|
+
pendingOperations: subAccount.pendingOperations.filter(op => isPending(subAccount, op)),
|
|
38
|
+
}))
|
|
39
|
+
: [];
|
|
40
|
+
|
|
41
|
+
return { ...synced, pendingOperations, subAccounts };
|
|
42
|
+
}
|
|
@@ -166,10 +166,12 @@ describe("Alpaca utils", () => {
|
|
|
166
166
|
},
|
|
167
167
|
...params,
|
|
168
168
|
} as GenericTransaction,
|
|
169
|
+
3n,
|
|
169
170
|
);
|
|
170
171
|
|
|
171
172
|
expect(operation).toMatchObject({
|
|
172
173
|
id: `parent-account-id--${expected.parentType}`,
|
|
174
|
+
transactionSequenceNumber: new BigNumber(3),
|
|
173
175
|
type: expected.parentType,
|
|
174
176
|
value: expected.parentValue,
|
|
175
177
|
accountId: "parent-account-id",
|
|
@@ -194,6 +196,7 @@ describe("Alpaca utils", () => {
|
|
|
194
196
|
subOperations: [
|
|
195
197
|
{
|
|
196
198
|
id: `sub-account-id--${expected.subType}`,
|
|
199
|
+
transactionSequenceNumber: new BigNumber(3),
|
|
197
200
|
accountId: "sub-account-id",
|
|
198
201
|
type: expected.subType,
|
|
199
202
|
senders: ["account-address"],
|
|
@@ -412,6 +412,7 @@ export const buildOptimisticOperation = (
|
|
|
412
412
|
blockHeight: null,
|
|
413
413
|
senders: [account.freshAddress],
|
|
414
414
|
recipients: [transaction.recipient],
|
|
415
|
+
transactionSequenceNumber: new BigNumber(sequenceNumber?.toString() ?? 0),
|
|
415
416
|
accountId: subAccountId,
|
|
416
417
|
date: new Date(),
|
|
417
418
|
transactionRaw: toGenericTransactionRaw({
|
|
@@ -112,6 +112,7 @@ export const DEVICE_LABELS_CONFIG: DeviceLabelsConfig = {
|
|
|
112
112
|
},
|
|
113
113
|
[DeviceModelId.stax]: TOUCHSCREEN_DEVICE_CONFIG,
|
|
114
114
|
[DeviceModelId.europa]: TOUCHSCREEN_DEVICE_CONFIG,
|
|
115
|
+
[DeviceModelId.apex]: TOUCHSCREEN_DEVICE_CONFIG,
|
|
115
116
|
default: {
|
|
116
117
|
receiveVerify: {
|
|
117
118
|
[AppInfos.BNB_CHAIN.name]: DeviceLabels.VERIFY_BSC,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const floatNumberRegex = /^\d+\.?\d+$/;
|
package/src/e2e/enum/Device.ts
CHANGED
|
@@ -22,7 +22,7 @@ function validateTransactionData(tx: Transaction, events: string[]) {
|
|
|
22
22
|
|
|
23
23
|
async function sendCardanoTouchDevices(tx: Transaction) {
|
|
24
24
|
await waitFor(DeviceLabels.REVIEW_TRANSACTION);
|
|
25
|
-
const events = await pressUntilTextFound(DeviceLabels.
|
|
25
|
+
const events = await pressUntilTextFound(DeviceLabels.AMOUNT);
|
|
26
26
|
validateTransactionData(tx, events);
|
|
27
27
|
await pressAndRelease(DeviceLabels.TAP_TO_CONTINUE);
|
|
28
28
|
await waitFor(DeviceLabels.FEES);
|
|
@@ -77,18 +77,22 @@ export async function sendCardano(tx: Transaction) {
|
|
|
77
77
|
return sendCardanoButtonDevice(tx);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
const TOUCH_DELEGATE_STEPS = [
|
|
81
|
+
[DeviceLabels.REVIEW_TRANSACTION, "swipe"],
|
|
82
|
+
[DeviceLabels.TAP_TO_CONTINUE, "tap"],
|
|
83
|
+
[DeviceLabels.REGISTER, "swipe"],
|
|
84
|
+
[DeviceLabels.TAP_TO_CONTINUE, "tap"],
|
|
85
|
+
[DeviceLabels.CONFIRM, "confirm"],
|
|
86
|
+
[DeviceLabels.DELEGATE_STAKE, "swipe"],
|
|
87
|
+
[DeviceLabels.TAP_TO_CONTINUE, "tap"],
|
|
88
|
+
[DeviceLabels.CONFIRM, "confirm"],
|
|
89
|
+
[DeviceLabels.HOLD_TO_SIGN, "hold"],
|
|
90
|
+
] as const;
|
|
91
|
+
|
|
80
92
|
const DELEGATE_STEPS_CONFIG = {
|
|
81
|
-
[DeviceModelId.stax
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
[DeviceLabels.REGISTER, "swipe"],
|
|
85
|
-
[DeviceLabels.TAP_TO_CONTINUE, "tap"],
|
|
86
|
-
[DeviceLabels.CONFIRM, "confirm"],
|
|
87
|
-
[DeviceLabels.DELEGATE_STAKE, "swipe"],
|
|
88
|
-
[DeviceLabels.TAP_TO_CONTINUE, "tap"],
|
|
89
|
-
[DeviceLabels.CONFIRM, "confirm"],
|
|
90
|
-
[DeviceLabels.HOLD_TO_SIGN, "hold"],
|
|
91
|
-
] as const,
|
|
93
|
+
[DeviceModelId.stax]: TOUCH_DELEGATE_STEPS,
|
|
94
|
+
[DeviceModelId.europa]: TOUCH_DELEGATE_STEPS,
|
|
95
|
+
[DeviceModelId.apex]: TOUCH_DELEGATE_STEPS,
|
|
92
96
|
[DeviceModelId.nanoS]: [
|
|
93
97
|
[DeviceLabels.NEW_ORDINARY, "right"],
|
|
94
98
|
[DeviceLabels.TRANSACTION_FEE, "both"],
|
|
@@ -115,8 +119,22 @@ const DELEGATE_STEPS_CONFIG = {
|
|
|
115
119
|
] as const,
|
|
116
120
|
};
|
|
117
121
|
|
|
122
|
+
function getConfirmButtonCoords(): { x: number; y: number } {
|
|
123
|
+
const speculosModel = getSpeculosModel();
|
|
124
|
+
|
|
125
|
+
switch (speculosModel) {
|
|
126
|
+
case DeviceModelId.stax:
|
|
127
|
+
return { x: 152, y: 532 };
|
|
128
|
+
case DeviceModelId.apex:
|
|
129
|
+
return { x: 114, y: 305 };
|
|
130
|
+
case DeviceModelId.europa:
|
|
131
|
+
default:
|
|
132
|
+
return { x: 186, y: 446 };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
118
136
|
async function delegateTouchDevicesAction(label: DeviceLabels) {
|
|
119
|
-
const CONFIRM_BUTTON_COORDS =
|
|
137
|
+
const CONFIRM_BUTTON_COORDS = getConfirmButtonCoords();
|
|
120
138
|
await waitFor(label);
|
|
121
139
|
switch (label) {
|
|
122
140
|
case DeviceLabels.TAP_TO_CONTINUE:
|
|
@@ -164,7 +182,7 @@ async function executeDelegateStep(label: DeviceLabels, action: ActionType) {
|
|
|
164
182
|
export async function delegateCardano() {
|
|
165
183
|
const speculosModel = getSpeculosModel();
|
|
166
184
|
const steps =
|
|
167
|
-
|
|
185
|
+
isTouchDevice() && DELEGATE_STEPS_CONFIG[speculosModel]
|
|
168
186
|
? DELEGATE_STEPS_CONFIG[speculosModel]
|
|
169
187
|
: speculosModel === DeviceModelId.nanoS
|
|
170
188
|
? DELEGATE_STEPS_CONFIG[DeviceModelId.nanoS]
|
package/src/e2e/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EnvName } from "@ledgerhq/live-env";
|
|
2
2
|
import { Feature, FeatureId } from "@ledgerhq/types-live";
|
|
3
3
|
import { getFeature, DEFAULT_FEATURES } from "../featureFlags";
|
|
4
|
+
import axios, { AxiosError } from "axios";
|
|
4
5
|
|
|
5
6
|
export const getAllFeatureFlags = (
|
|
6
7
|
appLanguage?: string,
|
|
@@ -53,3 +54,22 @@ export const formatEnvData = (data: { [key in EnvName]: unknown }) => {
|
|
|
53
54
|
}
|
|
54
55
|
return allureData;
|
|
55
56
|
};
|
|
57
|
+
|
|
58
|
+
export const sanitizeError = (error: unknown): Error => {
|
|
59
|
+
if (!axios.isAxiosError(error)) {
|
|
60
|
+
return error instanceof Error ? error : new Error(String(error ?? "Unknown error"));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const err = error as AxiosError;
|
|
64
|
+
const sanitized = new Error(err.message || "Axios request failed");
|
|
65
|
+
|
|
66
|
+
Object.assign(sanitized, {
|
|
67
|
+
name: err.name,
|
|
68
|
+
code: err.code,
|
|
69
|
+
url: err.config?.url,
|
|
70
|
+
method: err.config?.method,
|
|
71
|
+
status: err.response?.status,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return sanitized;
|
|
75
|
+
};
|
package/src/e2e/speculos.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
swipeRight,
|
|
50
50
|
} from "./deviceInteraction/TouchDeviceSimulator";
|
|
51
51
|
import { withDeviceController } from "./deviceInteraction/DeviceController";
|
|
52
|
+
import { sanitizeError } from ".";
|
|
52
53
|
|
|
53
54
|
const isSpeculosRemote = process.env.REMOTE_SPECULOS === "true";
|
|
54
55
|
|
|
@@ -428,7 +429,7 @@ export async function startSpeculos(
|
|
|
428
429
|
};
|
|
429
430
|
});
|
|
430
431
|
} catch (e: unknown) {
|
|
431
|
-
console.error(e);
|
|
432
|
+
console.error(sanitizeError(e));
|
|
432
433
|
log("engine", `test ${testName} failed with ${String(e)}`);
|
|
433
434
|
}
|
|
434
435
|
}
|
|
@@ -610,7 +611,7 @@ export async function takeScreenshot(port?: number): Promise<Buffer | undefined>
|
|
|
610
611
|
);
|
|
611
612
|
return response.data;
|
|
612
613
|
} catch (error) {
|
|
613
|
-
console.error("Error downloading speculos screenshot:", error);
|
|
614
|
+
console.error("Error downloading speculos screenshot:", sanitizeError(error));
|
|
614
615
|
}
|
|
615
616
|
}
|
|
616
617
|
|
|
@@ -668,12 +669,42 @@ export const activateLedgerSync = withDeviceController(({ getButtonsController }
|
|
|
668
669
|
}
|
|
669
670
|
});
|
|
670
671
|
|
|
672
|
+
const getSettingsToggle1Coordinates = () => {
|
|
673
|
+
const deviceModel = getSpeculosModel();
|
|
674
|
+
|
|
675
|
+
switch (deviceModel) {
|
|
676
|
+
case DeviceModelId.stax:
|
|
677
|
+
return { x: 345, y: 136 };
|
|
678
|
+
case DeviceModelId.europa:
|
|
679
|
+
return { x: 420, y: 140 };
|
|
680
|
+
case DeviceModelId.apex:
|
|
681
|
+
return { x: 263, y: 100 };
|
|
682
|
+
default:
|
|
683
|
+
return { x: 420, y: 140 };
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
const getSettingsCogwheelCoordinates = () => {
|
|
688
|
+
const deviceModel = getSpeculosModel();
|
|
689
|
+
|
|
690
|
+
switch (deviceModel) {
|
|
691
|
+
case DeviceModelId.stax:
|
|
692
|
+
return { x: 362, y: 43 };
|
|
693
|
+
case DeviceModelId.europa:
|
|
694
|
+
return { x: 400, y: 80 };
|
|
695
|
+
case DeviceModelId.apex:
|
|
696
|
+
return { x: 253, y: 58 };
|
|
697
|
+
default:
|
|
698
|
+
return { x: 400, y: 80 };
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
|
|
671
702
|
export const activateExpertMode = withDeviceController(({ getButtonsController }) => async () => {
|
|
672
703
|
const buttons = getButtonsController();
|
|
673
704
|
|
|
674
705
|
if (isTouchDevice()) {
|
|
675
706
|
await goToSettings();
|
|
676
|
-
const SettingsToggle1Coordinates =
|
|
707
|
+
const SettingsToggle1Coordinates = getSettingsToggle1Coordinates();
|
|
677
708
|
await pressAndRelease(
|
|
678
709
|
DeviceLabels.SETTINGS_TOGGLE_1,
|
|
679
710
|
SettingsToggle1Coordinates.x,
|
|
@@ -698,7 +729,7 @@ export const goToSettings = withDeviceController(({ getButtonsController }) => a
|
|
|
698
729
|
const buttons = getButtonsController();
|
|
699
730
|
|
|
700
731
|
if (isTouchDevice()) {
|
|
701
|
-
const SettingsCogwheelCoordinates =
|
|
732
|
+
const SettingsCogwheelCoordinates = getSettingsCogwheelCoordinates();
|
|
702
733
|
await pressAndRelease(
|
|
703
734
|
DeviceLabels.SETTINGS,
|
|
704
735
|
SettingsCogwheelCoordinates.x,
|
|
@@ -3,6 +3,7 @@ import { version } from "../../package.json";
|
|
|
3
3
|
import { getEnv } from "@ledgerhq/live-env";
|
|
4
4
|
import { DeviceModelId } from "@ledgerhq/devices";
|
|
5
5
|
import { Device as CryptoWallet } from "./enum/Device";
|
|
6
|
+
import { sanitizeError } from "./index";
|
|
6
7
|
import * as fs from "fs";
|
|
7
8
|
import * as path from "path";
|
|
8
9
|
|
|
@@ -18,6 +19,8 @@ export function getSpeculosModel(): DeviceModelId {
|
|
|
18
19
|
case CryptoWallet.FLEX.name:
|
|
19
20
|
case DeviceModelId.europa:
|
|
20
21
|
return DeviceModelId.europa;
|
|
22
|
+
case CryptoWallet.NANO_GEN_5.name:
|
|
23
|
+
return DeviceModelId.apex;
|
|
21
24
|
case CryptoWallet.LNSP.name:
|
|
22
25
|
default:
|
|
23
26
|
return DeviceModelId.nanoSP;
|
|
@@ -26,7 +29,9 @@ export function getSpeculosModel(): DeviceModelId {
|
|
|
26
29
|
|
|
27
30
|
export function isTouchDevice(): boolean {
|
|
28
31
|
const model = getSpeculosModel();
|
|
29
|
-
return
|
|
32
|
+
return (
|
|
33
|
+
model === DeviceModelId.stax || model === DeviceModelId.europa || model === DeviceModelId.apex
|
|
34
|
+
);
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
function getDeviceTargetId(device: DeviceModelId): number {
|
|
@@ -36,6 +41,7 @@ function getDeviceTargetId(device: DeviceModelId): number {
|
|
|
36
41
|
[DeviceModelId.nanoSP]: CryptoWallet.LNSP.targetId,
|
|
37
42
|
[DeviceModelId.stax]: CryptoWallet.STAX.targetId,
|
|
38
43
|
[DeviceModelId.europa]: CryptoWallet.FLEX.targetId,
|
|
44
|
+
[DeviceModelId.apex]: CryptoWallet.NANO_GEN_5.targetId,
|
|
39
45
|
};
|
|
40
46
|
return modelToTargetIdMap[device];
|
|
41
47
|
}
|
|
@@ -73,7 +79,7 @@ export async function createNanoAppJsonFile(nanoAppFilePath: string): Promise<vo
|
|
|
73
79
|
}
|
|
74
80
|
fs.writeFileSync(jsonFilePath, JSON.stringify(appCatalog, null, 2), "utf8");
|
|
75
81
|
} catch (error) {
|
|
76
|
-
console.error("Unable to create app version file:", error);
|
|
82
|
+
console.error("Unable to create app version file:", sanitizeError(error));
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
85
|
|
package/src/e2e/swap.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Account } from "./enum/Account";
|
|
2
|
+
import { sanitizeError } from "./index";
|
|
2
3
|
import axios from "axios";
|
|
3
4
|
|
|
4
5
|
export async function getMinimumSwapAmount(AccountFrom: Account, AccountTo: Account) {
|
|
5
6
|
try {
|
|
6
7
|
const requestConfig = {
|
|
7
8
|
method: "GET",
|
|
8
|
-
url:
|
|
9
|
+
url: "https://swap-stg.ledger-test.com/v5/quote",
|
|
9
10
|
params: {
|
|
10
11
|
from: AccountFrom.currency.id,
|
|
11
12
|
to: AccountTo.currency.id,
|
|
@@ -21,9 +22,7 @@ export async function getMinimumSwapAmount(AccountFrom: Account, AccountTo: Acco
|
|
|
21
22
|
tradeType: "INPUT",
|
|
22
23
|
uniswapOrderType: "uniswapxv1",
|
|
23
24
|
},
|
|
24
|
-
headers: {
|
|
25
|
-
accept: "application/json",
|
|
26
|
-
},
|
|
25
|
+
headers: { accept: "application/json" },
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
const { data } = await axios(requestConfig);
|
|
@@ -38,8 +37,10 @@ export async function getMinimumSwapAmount(AccountFrom: Account, AccountTo: Acco
|
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
return Math.max(...minimumAmounts);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
} catch (error: any) {
|
|
41
|
+
const sanitizedError = sanitizeError(error);
|
|
42
|
+
console.error("Error fetching swap minimum amount:", sanitizedError);
|
|
43
|
+
// throw the sanitized error, not the original circular Axios error
|
|
44
|
+
throw sanitizedError;
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import reduce from "lodash/reduce";
|
|
9
9
|
import { BUY_SELL_UI_APP_ID } from "../wallet-api/constants";
|
|
10
10
|
import { formatToFirebaseFeatureId } from "./firebaseFeatureFlags";
|
|
11
|
+
import { DeviceModelId } from "@ledgerhq/types-devices";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Default disabled feature.
|
|
@@ -215,6 +216,18 @@ export const DEFAULT_FEATURES: Features = {
|
|
|
215
216
|
protectId: "protect-simu",
|
|
216
217
|
},
|
|
217
218
|
},
|
|
219
|
+
recoverUpsellPostOnboarding: {
|
|
220
|
+
...DEFAULT_FEATURE,
|
|
221
|
+
params: {
|
|
222
|
+
deviceIds: [
|
|
223
|
+
DeviceModelId.nanoSP,
|
|
224
|
+
DeviceModelId.nanoX,
|
|
225
|
+
DeviceModelId.stax,
|
|
226
|
+
DeviceModelId.europa,
|
|
227
|
+
DeviceModelId.apex,
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
},
|
|
218
231
|
|
|
219
232
|
storyly: {
|
|
220
233
|
enabled: false,
|
|
@@ -44,7 +44,7 @@ export const getBitcoinYieldSetting = (stakePrograms: Feature_StakePrograms | nu
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
export const getEthDepositScreenSetting = (stakePrograms: Feature_StakePrograms | null): string => {
|
|
47
|
-
/** Check if Ethereum has "earn" provider configured in redirects with
|
|
47
|
+
/** Check if Ethereum has "earn" provider configured in redirects with ethDepositCohort. */
|
|
48
48
|
const ethereumRedirect = stakePrograms?.enabled
|
|
49
49
|
? stakePrograms?.params?.redirects?.["ethereum"]
|
|
50
50
|
: undefined;
|
|
@@ -55,8 +55,8 @@ export const getEthDepositScreenSetting = (stakePrograms: Feature_StakePrograms
|
|
|
55
55
|
return "standard";
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
// Extract
|
|
59
|
-
const
|
|
58
|
+
// Extract ethDepositCohort from queryParams
|
|
59
|
+
const ethDepositCohort = ethereumRedirect.queryParams?.ethDepositCohort;
|
|
60
60
|
|
|
61
|
-
return
|
|
61
|
+
return ethDepositCohort ?? "missing_cohort_value";
|
|
62
62
|
};
|
package/src/load/speculos.ts
CHANGED