@ledgerhq/live-common 25.0.0-next.3 → 25.0.0-next.6

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 (114) hide show
  1. package/lib/apps/support.d.ts.map +1 -1
  2. package/lib/apps/support.js +2 -1
  3. package/lib/apps/support.js.map +1 -1
  4. package/lib/families/cardano/bridge.integration.test.d.ts.map +1 -1
  5. package/lib/families/cardano/bridge.integration.test.js +36 -2
  6. package/lib/families/cardano/bridge.integration.test.js.map +1 -1
  7. package/lib/families/cardano/buildSubAccounts.d.ts.map +1 -1
  8. package/lib/families/cardano/buildSubAccounts.js +5 -3
  9. package/lib/families/cardano/buildSubAccounts.js.map +1 -1
  10. package/lib/families/cardano/deviceTransactionConfig.d.ts +11 -0
  11. package/lib/families/cardano/deviceTransactionConfig.d.ts.map +1 -0
  12. package/lib/families/cardano/deviceTransactionConfig.js +91 -0
  13. package/lib/families/cardano/deviceTransactionConfig.js.map +1 -0
  14. package/lib/families/cardano/errors.d.ts +4 -0
  15. package/lib/families/cardano/errors.d.ts.map +1 -1
  16. package/lib/families/cardano/errors.js +5 -1
  17. package/lib/families/cardano/errors.js.map +1 -1
  18. package/lib/families/cardano/js-estimateMaxSpendable.d.ts.map +1 -1
  19. package/lib/families/cardano/js-estimateMaxSpendable.js +3 -2
  20. package/lib/families/cardano/js-estimateMaxSpendable.js.map +1 -1
  21. package/lib/families/cardano/js-getTransactionStatus.d.ts.map +1 -1
  22. package/lib/families/cardano/js-getTransactionStatus.js +6 -5
  23. package/lib/families/cardano/js-getTransactionStatus.js.map +1 -1
  24. package/lib/families/cardano/js-signOperation.d.ts.map +1 -1
  25. package/lib/families/cardano/js-signOperation.js +10 -5
  26. package/lib/families/cardano/js-signOperation.js.map +1 -1
  27. package/lib/families/cardano/js-synchronisation.d.ts.map +1 -1
  28. package/lib/families/cardano/js-synchronisation.js +126 -98
  29. package/lib/families/cardano/js-synchronisation.js.map +1 -1
  30. package/lib/families/cardano/js-transaction.js +1 -1
  31. package/lib/families/cardano/js-transaction.js.map +1 -1
  32. package/lib/families/cardano/logic.d.ts +1 -0
  33. package/lib/families/cardano/logic.d.ts.map +1 -1
  34. package/lib/families/cardano/logic.js +11 -1
  35. package/lib/families/cardano/logic.js.map +1 -1
  36. package/lib/families/cardano/specs.d.ts.map +1 -1
  37. package/lib/families/cardano/specs.js +39 -1
  38. package/lib/families/cardano/specs.js.map +1 -1
  39. package/lib/families/cardano/speculos-deviceActions.d.ts.map +1 -1
  40. package/lib/families/cardano/speculos-deviceActions.js +15 -0
  41. package/lib/families/cardano/speculos-deviceActions.js.map +1 -1
  42. package/lib/families/cosmos/react.d.ts +1 -1
  43. package/lib/families/cosmos/react.d.ts.map +1 -1
  44. package/lib/families/cosmos/react.js +9 -4
  45. package/lib/families/cosmos/react.js.map +1 -1
  46. package/lib/families/hedera/api/mirror.js +1 -1
  47. package/lib/families/hedera/api/mirror.js.map +1 -1
  48. package/lib/families/hedera/api/network.d.ts.map +1 -1
  49. package/lib/families/hedera/api/network.js +3 -1
  50. package/lib/families/hedera/api/network.js.map +1 -1
  51. package/lib/families/hedera/deviceTransactionConfig.d.ts.map +1 -1
  52. package/lib/families/hedera/deviceTransactionConfig.js +7 -0
  53. package/lib/families/hedera/deviceTransactionConfig.js.map +1 -1
  54. package/lib/families/hedera/js-synchronisation.d.ts.map +1 -1
  55. package/lib/families/hedera/js-synchronisation.js +10 -7
  56. package/lib/families/hedera/js-synchronisation.js.map +1 -1
  57. package/lib/families/hedera/specs.d.ts.map +1 -1
  58. package/lib/families/hedera/specs.js +23 -0
  59. package/lib/families/hedera/specs.js.map +1 -1
  60. package/lib/families/hedera/speculos-deviceActions.d.ts.map +1 -1
  61. package/lib/families/hedera/speculos-deviceActions.js +5 -1
  62. package/lib/families/hedera/speculos-deviceActions.js.map +1 -1
  63. package/lib/families/hedera/transaction.d.ts.map +1 -1
  64. package/lib/families/hedera/transaction.js +2 -2
  65. package/lib/families/hedera/transaction.js.map +1 -1
  66. package/lib/families/hedera/types.d.ts +2 -5
  67. package/lib/families/hedera/types.d.ts.map +1 -1
  68. package/lib/families/hedera/types.js +0 -4
  69. package/lib/families/hedera/types.js.map +1 -1
  70. package/lib/families/hedera/utils.d.ts.map +1 -1
  71. package/lib/families/solana/react.d.ts +2 -1
  72. package/lib/families/solana/react.d.ts.map +1 -1
  73. package/lib/families/solana/react.integration.test.d.ts +2 -0
  74. package/lib/families/solana/react.integration.test.d.ts.map +1 -0
  75. package/lib/families/solana/react.integration.test.js +142 -0
  76. package/lib/families/solana/react.integration.test.js.map +1 -0
  77. package/lib/families/solana/react.js +21 -3
  78. package/lib/families/solana/react.js.map +1 -1
  79. package/lib/featureFlags/types.d.ts +1 -1
  80. package/lib/featureFlags/types.d.ts.map +1 -1
  81. package/lib/generated/deviceTransactionConfig.d.ts +2 -0
  82. package/lib/generated/deviceTransactionConfig.d.ts.map +1 -1
  83. package/lib/generated/deviceTransactionConfig.js +28 -26
  84. package/lib/generated/deviceTransactionConfig.js.map +1 -1
  85. package/package.json +3 -3
  86. package/src/apps/support.ts +1 -0
  87. package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +101 -0
  88. package/src/families/cardano/__snapshots__/bridge.integration.test.ts.snap +41 -1
  89. package/src/families/cardano/bridge.integration.test.ts +38 -0
  90. package/src/families/cardano/buildSubAccounts.ts +5 -3
  91. package/src/families/cardano/deviceTransactionConfig.ts +109 -0
  92. package/src/families/cardano/errors.ts +7 -0
  93. package/src/families/cardano/js-estimateMaxSpendable.ts +4 -1
  94. package/src/families/cardano/js-getTransactionStatus.ts +7 -7
  95. package/src/families/cardano/js-signOperation.ts +10 -5
  96. package/src/families/cardano/js-synchronisation.ts +52 -21
  97. package/src/families/cardano/js-transaction.ts +1 -1
  98. package/src/families/cardano/logic.ts +10 -0
  99. package/src/families/cardano/specs.ts +53 -1
  100. package/src/families/cardano/speculos-deviceActions.ts +15 -0
  101. package/src/families/cosmos/react.ts +12 -4
  102. package/src/families/hedera/api/mirror.ts +1 -1
  103. package/src/families/hedera/api/network.ts +1 -0
  104. package/src/families/hedera/deviceTransactionConfig.ts +8 -0
  105. package/src/families/hedera/js-synchronisation.ts +5 -3
  106. package/src/families/hedera/specs.ts +56 -5
  107. package/src/families/hedera/speculos-deviceActions.ts +1 -0
  108. package/src/families/hedera/transaction.ts +2 -0
  109. package/src/families/hedera/types.ts +2 -8
  110. package/src/families/hedera/utils.ts +1 -1
  111. package/src/families/solana/react.integration.test.ts +76 -0
  112. package/src/families/solana/react.ts +28 -3
  113. package/src/featureFlags/types.ts +1 -0
  114. package/src/generated/deviceTransactionConfig.ts +3 -0
@@ -21,6 +21,7 @@ import {
21
21
  getMemoFromTx,
22
22
  getOperationType,
23
23
  isHexString,
24
+ mergeTokens,
24
25
  } from "./logic";
25
26
  import { encodeOperationId } from "../../operation";
26
27
  import { getNetworkParameters } from "./networks";
@@ -28,6 +29,9 @@ import { getNetworkInfo } from "./api/getNetworkInfo";
28
29
  import uniqBy from "lodash/uniqBy";
29
30
  import postSyncPatch from "./postSyncPatch";
30
31
  import { getTransactions } from "./api/getTransactions";
32
+ import { buildSubAccounts } from "./buildSubAccounts";
33
+ import { calculateMinUtxoAmount } from "@stricahq/typhonjs/dist/utils/utils";
34
+ import { listTokensForCryptoCurrency } from "../../currencies";
31
35
 
32
36
  function mapTxToAccountOperation(
33
37
  tx: APITransaction,
@@ -43,6 +47,10 @@ function mapTxToAccountOperation(
43
47
 
44
48
  const subOperations = inferSubOperations(tx.hash, subAccounts);
45
49
  const memo = getMemoFromTx(tx);
50
+ const extra = {};
51
+ if (memo) {
52
+ extra["memo"] = memo;
53
+ }
46
54
 
47
55
  return {
48
56
  accountId,
@@ -64,14 +72,12 @@ function mapTxToAccountOperation(
64
72
  subOperations,
65
73
  blockHeight: tx.blockHeight,
66
74
  date: new Date(tx.timestamp),
67
- extra: {
68
- memo,
69
- },
75
+ extra: extra,
70
76
  blockHash: undefined,
71
77
  };
72
78
  }
73
79
 
74
- function syncUtxos(
80
+ function prepareUtxos(
75
81
  newTransactions: Array<APITransaction>,
76
82
  stableUtxos: Array<CardanoOutput>,
77
83
  accountCredentialsMap: Record<string, PaymentCredential>
@@ -116,7 +122,10 @@ function syncUtxos(
116
122
  return utxos;
117
123
  }
118
124
 
119
- export const getAccountShape: GetAccountShape = async (info) => {
125
+ export const getAccountShape: GetAccountShape = async (
126
+ info,
127
+ { blacklistedTokenIds }
128
+ ) => {
120
129
  const {
121
130
  transport,
122
131
  currency,
@@ -152,16 +161,27 @@ export const getAccountShape: GetAccountShape = async (info) => {
152
161
  derivationMode,
153
162
  });
154
163
 
164
+ // when new tokens are added / blacklist changes, we need to sync again because we need to go through all operations again
165
+ const syncHash =
166
+ JSON.stringify(blacklistedTokenIds || []) +
167
+ "_" +
168
+ listTokensForCryptoCurrency(currency, {
169
+ withDelisted: true,
170
+ }).length;
171
+ const outdatedSyncHash = initialAccount?.syncHash !== syncHash;
172
+
155
173
  const requiredConfirmations = 90;
156
174
 
157
175
  const oldOperations = initialAccount?.operations || [];
158
176
  const lastBlockHeight = oldOperations.length
159
177
  ? (oldOperations[0].blockHeight || 0) + 1
160
178
  : 0;
161
- const syncFromBlockHeight =
162
- lastBlockHeight > requiredConfirmations
163
- ? lastBlockHeight - requiredConfirmations
164
- : 0;
179
+
180
+ const syncFromBlockHeight = outdatedSyncHash
181
+ ? 0
182
+ : lastBlockHeight > requiredConfirmations
183
+ ? lastBlockHeight - requiredConfirmations
184
+ : 0;
165
185
 
166
186
  const {
167
187
  transactions: newTransactions,
@@ -195,21 +215,24 @@ export const getAccountShape: GetAccountShape = async (info) => {
195
215
  (u) => stableOperationsByIds[u.hash]
196
216
  );
197
217
 
198
- const utxos = syncUtxos(newTransactions, stableUtxos, accountCredentialsMap);
218
+ const utxos = prepareUtxos(
219
+ newTransactions,
220
+ stableUtxos,
221
+ accountCredentialsMap
222
+ );
199
223
  const accountBalance = utxos.reduce(
200
224
  (total, u) => total.plus(u.amount),
201
225
  new BigNumber(0)
202
226
  );
203
- // const tokenBalance = mergeTokens(utxos.map((u) => u.tokens).flat());
204
- // const subAccounts = buildSubAccounts({
205
- // initialAccount,
206
- // parentAccountId: accountId,
207
- // parentCurrency: currency,
208
- // newTransactions,
209
- // tokens: tokenBalance,
210
- // accountCredentialsMap,
211
- // });
212
- const subAccounts = [];
227
+ const tokenBalance = mergeTokens(utxos.map((u) => u.tokens).flat());
228
+ const subAccounts = buildSubAccounts({
229
+ initialAccount,
230
+ parentAccountId: accountId,
231
+ parentCurrency: currency,
232
+ newTransactions,
233
+ tokens: tokenBalance,
234
+ accountCredentialsMap,
235
+ });
213
236
 
214
237
  const newOperations = newTransactions.map((t) =>
215
238
  mapTxToAccountOperation(t, accountId, accountCredentialsMap, subAccounts)
@@ -233,13 +256,21 @@ export const getAccountShape: GetAccountShape = async (info) => {
233
256
  }).getBech32(),
234
257
  }));
235
258
  const cardanoNetworkInfo = await getNetworkInfo(initialAccount, currency);
259
+ const minAdaBalanceForTokens = tokenBalance.length
260
+ ? calculateMinUtxoAmount(
261
+ tokenBalance,
262
+ new BigNumber(cardanoNetworkInfo.protocolParams.lovelacePerUtxoWord),
263
+ false
264
+ )
265
+ : new BigNumber(0);
236
266
 
237
267
  return {
238
268
  id: accountId,
239
269
  xpub,
240
270
  balance: accountBalance,
241
- spendableBalance: accountBalance,
271
+ spendableBalance: accountBalance.minus(minAdaBalanceForTokens),
242
272
  operations: operations,
273
+ syncHash,
243
274
  subAccounts,
244
275
  freshAddresses,
245
276
  freshAddress: freshAddresses[0].address,
@@ -47,7 +47,7 @@ export const prepareTransaction = async (
47
47
  try {
48
48
  transaction = await buildTransaction(a, t);
49
49
  } catch (error) {
50
- return { ...t, fees: new BigNumber(0), amount: new BigNumber(0) };
50
+ return { ...t, fees: new BigNumber(0), amount: t.amount };
51
51
  }
52
52
 
53
53
  const transactionFees = transaction.getFee();
@@ -448,3 +448,13 @@ export function isHexString(value: string): boolean {
448
448
  const regExp = /^[0-9a-fA-F]+$/;
449
449
  return regExp.test(value);
450
450
  }
451
+
452
+ export function decodeTokenName(assetName: string): string {
453
+ if (assetName.length > 0) {
454
+ const bytes = [...Buffer.from(assetName, "hex")];
455
+ if (bytes.filter((byte) => byte <= 32 || byte >= 127).length === 0) {
456
+ return String.fromCharCode(...bytes);
457
+ }
458
+ }
459
+ return assetName;
460
+ }
@@ -9,10 +9,15 @@ import invariant from "invariant";
9
9
  import { utils as TyphonUtils } from "@stricahq/typhonjs";
10
10
  import { mergeTokens } from "./logic";
11
11
  import { parseCurrencyUnit } from "../../currencies";
12
+ import { SubAccount } from "../../types";
12
13
 
13
14
  const currency = getCryptoCurrencyById("cardano");
14
15
  const minBalanceRequired = parseCurrencyUnit(currency.units[0], "2.2");
15
16
  const minBalanceRequiredForMaxSend = parseCurrencyUnit(currency.units[0], "1");
17
+ const minSpendableRequiredForTokenTx = parseCurrencyUnit(
18
+ currency.units[0],
19
+ "3"
20
+ );
16
21
 
17
22
  const cardano: AppSpec<Transaction> = {
18
23
  name: "cardano",
@@ -34,7 +39,12 @@ const cardano: AppSpec<Transaction> = {
34
39
 
35
40
  const updates = [
36
41
  { recipient },
37
- { amount: new BigNumber(account.balance.dividedBy(2)).dp(0, 1) },
42
+ {
43
+ amount: new BigNumber(account.balance.dividedBy(2)).dp(
44
+ 0,
45
+ BigNumber.ROUND_CEIL
46
+ ),
47
+ },
38
48
  { memo: "LedgerLiveBot" },
39
49
  ];
40
50
 
@@ -90,6 +100,48 @@ const cardano: AppSpec<Transaction> = {
90
100
  );
91
101
  },
92
102
  },
103
+ {
104
+ name: "move ~10% token",
105
+ maxRun: 1,
106
+ transaction: ({ account, siblings, bridge, maxSpendable }) => {
107
+ invariant(
108
+ maxSpendable.gte(minSpendableRequiredForTokenTx),
109
+ "balance is too low"
110
+ );
111
+ const sibling = pickSiblings(siblings, 3);
112
+ const recipient = sibling.freshAddress;
113
+ const transaction = bridge.createTransaction(account);
114
+
115
+ const subAccount = account.subAccounts?.find((subAccount) =>
116
+ subAccount.balance.gt(1)
117
+ ) as SubAccount;
118
+ invariant(subAccount, "No token account with balance");
119
+
120
+ const updates = [
121
+ { subAccountId: subAccount.id },
122
+ { recipient },
123
+ {
124
+ amount: new BigNumber(subAccount.balance.dividedBy(10)).dp(
125
+ 0,
126
+ BigNumber.ROUND_CEIL
127
+ ),
128
+ },
129
+ ];
130
+
131
+ return {
132
+ transaction,
133
+ updates,
134
+ };
135
+ },
136
+ test: ({ operation, transaction }): void => {
137
+ expect(operation.subOperations).toBeTruthy();
138
+ expect(operation.subOperations?.length).toEqual(1);
139
+
140
+ const subOperation =
141
+ operation.subOperations && operation.subOperations[0];
142
+ expect(subOperation?.value).toEqual(transaction.amount);
143
+ },
144
+ },
93
145
  ],
94
146
  };
95
147
 
@@ -39,6 +39,21 @@ const acceptTransaction: DeviceAction<Transaction, any> = deviceActionFlow({
39
39
  expectedValue: ({ account, status }) =>
40
40
  expectedValue(account.unit, status.amount),
41
41
  },
42
+ {
43
+ title: "Asset fingerprint",
44
+ button: "LRlr",
45
+ },
46
+ {
47
+ title: "Token amount",
48
+ button: "LRlr",
49
+ },
50
+ {
51
+ title: "Confirm",
52
+ },
53
+ {
54
+ title: "output?",
55
+ button: "Rr",
56
+ },
42
57
  {
43
58
  title: "Transaction fee",
44
59
  button: "LRlr",
@@ -165,19 +165,27 @@ export function useMappedExtraOperationDetails({
165
165
  };
166
166
  }
167
167
 
168
- export function useLedgerFirstShuffledValidatorsCosmos() {
168
+ export function useLedgerFirstShuffledValidatorsCosmos(
169
+ searchInput?: string
170
+ ): CosmosValidatorItem[] {
169
171
  const data = useCosmosPreloadData();
170
172
 
171
173
  return useMemo(() => {
172
- return reorderValidators(data?.validators ?? []);
173
- }, [data]);
174
+ return reorderValidators(data?.validators ?? [], searchInput);
175
+ }, [data, searchInput]);
174
176
  }
175
177
 
176
178
  function reorderValidators(
177
- validators: CosmosValidatorItem[]
179
+ validators: CosmosValidatorItem[],
180
+ searchInput?: string
178
181
  ): CosmosValidatorItem[] {
179
182
  const sortedValidators = validators
180
183
  .filter((validator) => validator.commission !== 1.0)
184
+ .filter((validator) =>
185
+ searchInput
186
+ ? validator.name.toLowerCase().includes(searchInput.toLowerCase())
187
+ : true
188
+ )
181
189
  .sort((a, b) => b.votingPower - a.votingPower);
182
190
 
183
191
  // move Ledger validator to the first position
@@ -136,7 +136,7 @@ export async function getOperationsForAccount(
136
136
  // Set a value just so that it's considered confirmed according to isConfirmedOperation
137
137
  blockHeight: 5,
138
138
  blockHash: null,
139
- extra: { consensus_timestamp },
139
+ extra: {},
140
140
  fee,
141
141
  hash,
142
142
  recipients,
@@ -23,6 +23,7 @@ export async function buildUnsignedTransaction({
23
23
  return new hedera.TransferTransaction()
24
24
  .setNodeAccountIds([new AccountId(3)])
25
25
  .setTransactionId(hedera.TransactionId.generate(accountId))
26
+ .setTransactionMemo(transaction.memo ?? "")
26
27
  .addHbarTransfer(accountId, hbarAmount.negated())
27
28
  .addHbarTransfer(transaction.recipient, hbarAmount)
28
29
  .freeze();
@@ -39,6 +39,14 @@ function getDeviceTransactionConfig({
39
39
  });
40
40
  }
41
41
 
42
+ if (transaction.memo) {
43
+ fields.push({
44
+ type: "text",
45
+ label: "Memo",
46
+ value: transaction.memo,
47
+ });
48
+ }
49
+
42
50
  return fields;
43
51
  }
44
52
 
@@ -1,4 +1,5 @@
1
1
  import invariant from "invariant";
2
+ import { BigNumber } from "bignumber.js";
2
3
  import { Account, getDerivationScheme, runDerivationScheme } from "../../types";
3
4
  import type {
4
5
  GetAccountShape,
@@ -31,14 +32,15 @@ const getAccountShape: GetAccountShape = async (
31
32
 
32
33
  // grab latest operation's consensus timestamp for incremental sync
33
34
  const oldOperations = initialAccount?.operations ?? [];
34
- const latestOperationTimestamp =
35
- oldOperations[0]?.extra?.consensus_timestamp ?? 0;
35
+ const latestOperationTimestamp = oldOperations[0]
36
+ ? Math.floor(oldOperations[0].date.getTime() / 1000)
37
+ : 0;
36
38
 
37
39
  // merge new operations w/ previously synced ones
38
40
  const newOperations = await getOperationsForAccount(
39
41
  liveAccountId,
40
42
  address,
41
- latestOperationTimestamp
43
+ new BigNumber(latestOperationTimestamp).toString()
42
44
  );
43
45
  const operations = mergeOps(oldOperations, newOperations);
44
46
 
@@ -2,12 +2,18 @@ import expect from "expect";
2
2
  import invariant from "invariant";
3
3
  import { getCryptoCurrencyById, parseCurrencyUnit } from "../../currencies";
4
4
  import { DeviceModelId } from "@ledgerhq/devices";
5
- import type { AppSpec } from "../../bot/types";
5
+ import type {
6
+ AppSpec,
7
+ TransactionTestInput,
8
+ TransactionArg,
9
+ TransactionRes,
10
+ } from "../../bot/types";
6
11
  import type { Transaction } from "./types";
7
12
  import { pickSiblings } from "../../bot/specs";
8
13
  import { isAccountEmpty } from "../../account";
9
14
 
10
15
  const currency = getCryptoCurrencyById("hedera");
16
+ const memoTestMessage = "This is a test memo.";
11
17
 
12
18
  // Ensure that, when the recipient corresponds to an empty account,
13
19
  // the amount to send is greater or equal to the required minimum
@@ -41,7 +47,11 @@ const hedera: AppSpec<Transaction> = {
41
47
  {
42
48
  name: "Send ~50%",
43
49
  maxRun: 2,
44
- transaction: ({ account, siblings, bridge }) => {
50
+ transaction: ({
51
+ account,
52
+ siblings,
53
+ bridge,
54
+ }: TransactionArg<Transaction>): TransactionRes<Transaction> => {
45
55
  const sibling = pickSiblings(siblings, 4);
46
56
  const recipient = sibling.freshAddress;
47
57
 
@@ -58,7 +68,11 @@ const hedera: AppSpec<Transaction> = {
58
68
  updates: [{ amount }, { recipient }],
59
69
  };
60
70
  },
61
- test: ({ account, accountBeforeTransaction, operation }) => {
71
+ test: ({
72
+ account,
73
+ accountBeforeTransaction,
74
+ operation,
75
+ }: TransactionTestInput<Transaction>): void => {
62
76
  expect(account.balance.toString()).toBe(
63
77
  accountBeforeTransaction.balance.minus(operation.value).toString()
64
78
  );
@@ -67,7 +81,11 @@ const hedera: AppSpec<Transaction> = {
67
81
  {
68
82
  name: "Send max",
69
83
  maxRun: 2,
70
- transaction: ({ account, siblings, bridge }) => {
84
+ transaction: ({
85
+ account,
86
+ siblings,
87
+ bridge,
88
+ }: TransactionArg<Transaction>): TransactionRes<Transaction> => {
71
89
  const sibling = pickSiblings(siblings, 4);
72
90
  const recipient = sibling.freshAddress;
73
91
 
@@ -78,7 +96,12 @@ const hedera: AppSpec<Transaction> = {
78
96
  updates: [{ recipient }, { useAllAmount: true }],
79
97
  };
80
98
  },
81
- test: ({ accountBeforeTransaction, account, operation, transaction }) => {
99
+ test: ({
100
+ accountBeforeTransaction,
101
+ account,
102
+ operation,
103
+ transaction,
104
+ }: TransactionTestInput<Transaction>): void => {
82
105
  const accountBalanceAfterTx = account.balance.toNumber();
83
106
 
84
107
  // NOTE: operation.fee is the ACTUAL (not estimated) fee cost of the transaction
@@ -89,6 +112,34 @@ const hedera: AppSpec<Transaction> = {
89
112
  expect(accountBalanceAfterTx).toBe(amount);
90
113
  },
91
114
  },
115
+ {
116
+ name: "Memo",
117
+ maxRun: 2,
118
+ transaction: ({
119
+ account,
120
+ siblings,
121
+ bridge,
122
+ }: TransactionArg<Transaction>): TransactionRes<Transaction> => {
123
+ const sibling = pickSiblings(siblings, 4);
124
+ const recipient = sibling.freshAddress;
125
+
126
+ const transaction = bridge.createTransaction(account);
127
+
128
+ const amount = account.balance
129
+ .div(1.9 + 0.2 * Math.random())
130
+ .integerValue();
131
+
132
+ checkSendableToEmptyAccount(amount, sibling);
133
+
134
+ return {
135
+ transaction,
136
+ updates: [{ recipient }, { amount }, { memo: memoTestMessage }],
137
+ };
138
+ },
139
+ test: ({ transaction }: TransactionTestInput<Transaction>): void => {
140
+ expect(transaction.memo).toBe(memoTestMessage);
141
+ },
142
+ },
92
143
  ],
93
144
  };
94
145
 
@@ -53,6 +53,7 @@ const acceptTransaction: DeviceAction<Transaction, any> = deviceActionFlow({
53
53
  {
54
54
  title: "Memo",
55
55
  button: "Rr",
56
+ expectedValue: ({ transaction }) => transaction.memo as string,
56
57
  },
57
58
  {
58
59
  title: "Confirm",
@@ -29,6 +29,7 @@ export function fromTransactionRaw(tr: TransactionRaw): Transaction {
29
29
  return {
30
30
  ...common,
31
31
  family: tr.family,
32
+ memo: tr.memo,
32
33
  };
33
34
  }
34
35
 
@@ -38,6 +39,7 @@ export function toTransactionRaw(t: Transaction): TransactionRaw {
38
39
  return {
39
40
  ...common,
40
41
  family: t.family,
42
+ memo: t.memo,
41
43
  };
42
44
  }
43
45
 
@@ -3,11 +3,6 @@ import type {
3
3
  TransactionCommonRaw,
4
4
  } from "../../types/transaction";
5
5
 
6
- export type CoreStatics = Record<string, never>;
7
- export type CoreAccountSpecifics = Record<string, never>;
8
- export type CoreOperationSpecifics = Record<string, never>;
9
- export type CoreCurrencySpecifics = Record<string, never>;
10
-
11
6
  export type NetworkInfo = {
12
7
  family: "hedera";
13
8
  };
@@ -18,11 +13,10 @@ export type NetworkInfoRaw = {
18
13
 
19
14
  export type Transaction = TransactionCommon & {
20
15
  family: "hedera";
16
+ memo?: string;
21
17
  };
22
18
 
23
19
  export type TransactionRaw = TransactionCommonRaw & {
24
20
  family: "hedera";
21
+ memo?: string;
25
22
  };
26
-
27
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
28
- export const reflect = (_declare: any) => {};
@@ -31,7 +31,7 @@ export async function calculateAmount({
31
31
  }
32
32
 
33
33
  // NOTE: convert from the non-url-safe version of base64 to the url-safe version (that the explorer uses)
34
- export function base64ToUrlSafeBase64(data: string) {
34
+ export function base64ToUrlSafeBase64(data: string): string {
35
35
  // Might be nice to use this alternative if .nvmrc changes to >= Node v14.18.0
36
36
  // base64url encoding option isn't supported until then
37
37
  // Buffer.from(data, "base64").toString("base64url");
@@ -0,0 +1,76 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+ import { getAccountBridge, getCurrencyBridge } from "../../bridge";
3
+ import { getCryptoCurrencyById } from "../../currencies";
4
+ import { setEnv } from "../../env";
5
+ import { makeBridgeCacheSystem } from "../../bridge/cache";
6
+ import { genAccount, genAddingOperationsInAccount } from "../../mock/account";
7
+ import type { Account, CurrencyBridge } from "../../types";
8
+ import type { Transaction } from "./types";
9
+ import { getCurrentSolanaPreloadData } from "./js-preload-data";
10
+ import { LEDGER_VALIDATOR_ADDRESS } from "./utils";
11
+ import * as hooks from "./react";
12
+
13
+ const localCache = {};
14
+ const cache = makeBridgeCacheSystem({
15
+ saveData(c, d) {
16
+ localCache[c.id] = d;
17
+ return Promise.resolve();
18
+ },
19
+
20
+ getData(c) {
21
+ return Promise.resolve(localCache[c.id]);
22
+ },
23
+ });
24
+ describe("solana/react", () => {
25
+ describe("useValidators", () => {
26
+ it("should return validators", async () => {
27
+ const { prepare, account } = setup();
28
+ await prepare();
29
+ const { result } = renderHook(() =>
30
+ hooks.useValidators(account.currency)
31
+ );
32
+ const data = getCurrentSolanaPreloadData(account.currency);
33
+
34
+ expect(result.current).toStrictEqual(data.validators);
35
+ });
36
+
37
+ it("should return validators", async () => {
38
+ const { prepare, account } = setup();
39
+ await prepare();
40
+ const { result } = renderHook(() =>
41
+ hooks.useValidators(account.currency, "Ledger")
42
+ );
43
+
44
+ expect(
45
+ result.current.some(
46
+ (validator) => validator.voteAccount === LEDGER_VALIDATOR_ADDRESS
47
+ )
48
+ ).toBe(true);
49
+ });
50
+ });
51
+ });
52
+
53
+ function setup(): {
54
+ account: Account;
55
+ currencyBridge: CurrencyBridge;
56
+ transaction: Transaction;
57
+ prepare: () => Promise<any>;
58
+ } {
59
+ setEnv("MOCK", 1);
60
+ setEnv("EXPERIMENTAL_CURRENCIES", "solana");
61
+ const seed = "solana-2";
62
+ const currency = getCryptoCurrencyById("solana");
63
+ const a = genAccount(seed, {
64
+ currency,
65
+ });
66
+ const account = genAddingOperationsInAccount(a, 3, seed);
67
+ const currencyBridge = getCurrencyBridge(currency);
68
+ const bridge = getAccountBridge(account);
69
+ const transaction = bridge.createTransaction(account);
70
+ return {
71
+ account,
72
+ currencyBridge,
73
+ transaction,
74
+ prepare: async () => cache.prepareCurrency(currency),
75
+ };
76
+ }
@@ -6,6 +6,7 @@ import {
6
6
  getSolanaPreloadData,
7
7
  } from "./js-preload-data";
8
8
  import { SolanaPreloadDataV1, SolanaStake, SolanaStakeWithMeta } from "./types";
9
+ import { ValidatorsAppValidator } from "./validator-app";
9
10
 
10
11
  export function useSolanaPreloadData(
11
12
  currency: CryptoCurrency
@@ -16,12 +17,36 @@ export function useSolanaPreloadData(
16
17
  );
17
18
  }
18
19
 
19
- export function useValidators(currency: CryptoCurrency) {
20
+ export function useValidators(
21
+ currency: CryptoCurrency,
22
+ search?: string
23
+ ): ValidatorsAppValidator[] {
20
24
  const data = useSolanaPreloadData(currency);
21
25
 
22
26
  return useMemo(() => {
23
- return data?.validators ?? [];
24
- }, [data]);
27
+ const validators = data?.validators ?? [];
28
+
29
+ if (validators.length === 0 || !search || search === "") {
30
+ return validators;
31
+ }
32
+
33
+ const lowercaseSearch = search.toLowerCase();
34
+
35
+ const filtered = validators.filter(
36
+ (validator) =>
37
+ validator.name?.toLowerCase().includes(lowercaseSearch) ||
38
+ validator.voteAccount.toLowerCase().includes(lowercaseSearch)
39
+ );
40
+
41
+ const flags = [];
42
+ const output: ValidatorsAppValidator[] = [];
43
+ for (let i = 0; i < filtered.length; i++) {
44
+ if (flags[filtered[i].voteAccount]) continue;
45
+ flags[filtered[i].voteAccount] = true;
46
+ output.push(filtered[i]);
47
+ }
48
+ return output;
49
+ }, [data, search]);
25
50
  }
26
51
 
27
52
  export function useSolanaStakesWithMeta(
@@ -6,6 +6,7 @@ export type FeatureId =
6
6
  | "ratings"
7
7
  | "counterValue"
8
8
  | "buyDeviceFromLive"
9
+ | "ptxSmartRouting"
9
10
  | string;
10
11
 
11
12
  // We use objects instead of direct booleans for potential future improvements
@@ -2,6 +2,8 @@ import algorand from "../families/algorand/deviceTransactionConfig";
2
2
 
3
3
  import bitcoin from "../families/bitcoin/deviceTransactionConfig";
4
4
 
5
+ import cardano from "../families/cardano/deviceTransactionConfig";
6
+
5
7
  import celo from "../families/celo/deviceTransactionConfig";
6
8
 
7
9
  import cosmos from "../families/cosmos/deviceTransactionConfig";
@@ -32,6 +34,7 @@ import tron from "../families/tron/deviceTransactionConfig";
32
34
  export default {
33
35
  algorand,
34
36
  bitcoin,
37
+ cardano,
35
38
  celo,
36
39
  cosmos,
37
40
  crypto_org,