@ledgerhq/coin-hedera 1.12.0-nightly.3 → 1.12.0-nightly.4

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 (61) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +67 -15
  3. package/lib/api/network.d.ts +2 -6
  4. package/lib/api/network.d.ts.map +1 -1
  5. package/lib/api/network.js +4 -25
  6. package/lib/api/network.js.map +1 -1
  7. package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
  8. package/lib/bridge/buildOptimisticOperation.js +6 -2
  9. package/lib/bridge/buildOptimisticOperation.js.map +1 -1
  10. package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
  11. package/lib/bridge/getTransactionStatus.js +10 -13
  12. package/lib/bridge/getTransactionStatus.js.map +1 -1
  13. package/lib/bridge/getTransactionStatus.test.js +24 -8
  14. package/lib/bridge/getTransactionStatus.test.js.map +1 -1
  15. package/lib/bridge/index.d.ts.map +1 -1
  16. package/lib/bridge/index.js +3 -0
  17. package/lib/bridge/index.js.map +1 -1
  18. package/lib/bridge/utils.d.ts +4 -0
  19. package/lib/bridge/utils.d.ts.map +1 -1
  20. package/lib/bridge/utils.integration.test.js +40 -0
  21. package/lib/bridge/utils.integration.test.js.map +1 -1
  22. package/lib/bridge/utils.js +41 -3
  23. package/lib/bridge/utils.js.map +1 -1
  24. package/lib/errors.d.ts +12 -0
  25. package/lib/errors.d.ts.map +1 -1
  26. package/lib/errors.js +5 -1
  27. package/lib/errors.js.map +1 -1
  28. package/lib-es/api/network.d.ts +2 -6
  29. package/lib-es/api/network.d.ts.map +1 -1
  30. package/lib-es/api/network.js +3 -24
  31. package/lib-es/api/network.js.map +1 -1
  32. package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
  33. package/lib-es/bridge/buildOptimisticOperation.js +7 -3
  34. package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
  35. package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
  36. package/lib-es/bridge/getTransactionStatus.js +9 -12
  37. package/lib-es/bridge/getTransactionStatus.js.map +1 -1
  38. package/lib-es/bridge/getTransactionStatus.test.js +21 -5
  39. package/lib-es/bridge/getTransactionStatus.test.js.map +1 -1
  40. package/lib-es/bridge/index.d.ts.map +1 -1
  41. package/lib-es/bridge/index.js +3 -0
  42. package/lib-es/bridge/index.js.map +1 -1
  43. package/lib-es/bridge/utils.d.ts +4 -0
  44. package/lib-es/bridge/utils.d.ts.map +1 -1
  45. package/lib-es/bridge/utils.integration.test.js +41 -1
  46. package/lib-es/bridge/utils.integration.test.js.map +1 -1
  47. package/lib-es/bridge/utils.js +40 -3
  48. package/lib-es/bridge/utils.js.map +1 -1
  49. package/lib-es/errors.d.ts +12 -0
  50. package/lib-es/errors.d.ts.map +1 -1
  51. package/lib-es/errors.js +4 -0
  52. package/lib-es/errors.js.map +1 -1
  53. package/package.json +10 -10
  54. package/src/api/network.ts +2 -35
  55. package/src/bridge/buildOptimisticOperation.ts +7 -3
  56. package/src/bridge/getTransactionStatus.test.ts +27 -5
  57. package/src/bridge/getTransactionStatus.ts +14 -13
  58. package/src/bridge/index.ts +3 -0
  59. package/src/bridge/utils.integration.test.ts +52 -0
  60. package/src/bridge/utils.ts +49 -2
  61. package/src/errors.ts +12 -0
@@ -1,4 +1,5 @@
1
1
  import BigNumber from "bignumber.js";
2
+ import { InvalidAddress } from "@ledgerhq/errors";
2
3
  import cvsApi from "@ledgerhq/live-countervalues/api/index";
3
4
  import { encodeTokenAccountId } from "@ledgerhq/coin-framework/account";
4
5
  import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture";
@@ -12,6 +13,7 @@ import {
12
13
  getSubAccounts,
13
14
  getSyncHash,
14
15
  mergeSubAccounts,
16
+ safeParseAccountId,
15
17
  patchOperationWithExtra,
16
18
  prepareOperations,
17
19
  } from "./utils";
@@ -23,6 +25,7 @@ import {
23
25
  import { getMockedOperation } from "../test/fixtures/operation.fixture";
24
26
  import { HederaOperationExtra } from "../types";
25
27
  import { getAccount } from "../api/mirror";
28
+ import { HederaRecipientInvalidChecksum } from "../errors";
26
29
  import { isValidExtra } from "../logic";
27
30
  import { getMockedMirrorToken } from "../test/fixtures/mirror.fixture";
28
31
  import { HEDERA_OPERATION_TYPES, HEDERA_TRANSACTION_KINDS } from "../constants";
@@ -539,5 +542,54 @@ describe("utils", () => {
539
542
  const result = await checkAccountTokenAssociationStatus(accountId, tokenId);
540
543
  expect(result).toBe(false);
541
544
  });
545
+
546
+ test("supports addresses with checksum", async () => {
547
+ const addressWithChecksum = "0.0.9124531-xrxlv";
548
+
549
+ mockedGetAccount.mockResolvedValueOnce({
550
+ account: accountId,
551
+ max_automatic_token_associations: 0,
552
+ balance: {
553
+ balance: 1,
554
+ timestamp: "",
555
+ tokens: [{ token_id: "0.0.9999", balance: 1 }],
556
+ },
557
+ });
558
+
559
+ await checkAccountTokenAssociationStatus(addressWithChecksum, tokenId);
560
+ expect(mockedGetAccount).toHaveBeenCalledWith("0.0.9124531");
561
+ });
562
+ });
563
+
564
+ describe("safeParseAccountId", () => {
565
+ test("returns account id and no checksum for valid address without checksum", () => {
566
+ const [error, result] = safeParseAccountId("0.0.9124531");
567
+
568
+ expect(error).toBeNull();
569
+ expect(result?.accountId).toBe("0.0.9124531");
570
+ expect(result?.checksum).toBeNull();
571
+ });
572
+
573
+ test("returns account id and checksum for valid address with correct checksum", () => {
574
+ const [error, result] = safeParseAccountId("0.0.9124531-xrxlv");
575
+
576
+ expect(error).toBeNull();
577
+ expect(result?.accountId).toBe("0.0.9124531");
578
+ expect(result?.checksum).toBe("xrxlv");
579
+ });
580
+
581
+ test("returns error for valid address with incorrect checksum", () => {
582
+ const [error, accountId] = safeParseAccountId("0.0.9124531-invld");
583
+
584
+ expect(error).toBeInstanceOf(HederaRecipientInvalidChecksum);
585
+ expect(accountId).toBeNull();
586
+ });
587
+
588
+ test("returns error for invalid address format", () => {
589
+ const [error, accountId] = safeParseAccountId("not-a-valid-address");
590
+
591
+ expect(error).toBeInstanceOf(InvalidAddress);
592
+ expect(accountId).toBeNull();
593
+ });
542
594
  });
543
595
  });
@@ -1,9 +1,12 @@
1
1
  import BigNumber from "bignumber.js";
2
2
  import murmurhash from "imurmurhash";
3
3
  import invariant from "invariant";
4
+ import { AccountId } from "@hashgraph/sdk";
5
+ import { InvalidAddress } from "@ledgerhq/errors";
4
6
  import type { Account, Operation, TokenAccount } from "@ledgerhq/types-live";
5
7
  import cvsApi from "@ledgerhq/live-countervalues/api/index";
6
8
  import {
9
+ findCryptoCurrencyById,
7
10
  findTokenByAddressInCurrency,
8
11
  getFiatCurrencyByTicker,
9
12
  listTokensForCryptoCurrency,
@@ -22,7 +25,9 @@ import { makeLRUCache, seconds } from "@ledgerhq/live-network/cache";
22
25
  import { estimateMaxSpendable } from "./estimateMaxSpendable";
23
26
  import type { HederaOperationExtra, Transaction } from "../types";
24
27
  import { getAccount } from "../api/mirror";
28
+ import { getHederaClient } from "../api/network";
25
29
  import type { HederaMirrorToken } from "../api/types";
30
+ import { HederaRecipientInvalidChecksum } from "../errors";
26
31
  import { isTokenAssociateTransaction, isValidExtra } from "../logic";
27
32
  import { BASE_USD_FEE_BY_OPERATION_TYPE, HEDERA_OPERATION_TYPES } from "../constants";
28
33
 
@@ -453,8 +458,15 @@ export function patchOperationWithExtra(
453
458
  }
454
459
 
455
460
  export const checkAccountTokenAssociationStatus = makeLRUCache(
456
- async (accountId: string, tokenId: string) => {
457
- const mirrorAccount = await getAccount(accountId);
461
+ async (address: string, tokenId: string) => {
462
+ const [parsingError, parsingResult] = safeParseAccountId(address);
463
+
464
+ if (parsingError) {
465
+ throw parsingError;
466
+ }
467
+
468
+ const addressWithoutChecksum = parsingResult.accountId;
469
+ const mirrorAccount = await getAccount(addressWithoutChecksum);
458
470
 
459
471
  // auto association is enabled
460
472
  if (mirrorAccount.max_automatic_token_associations === -1) {
@@ -470,3 +482,38 @@ export const checkAccountTokenAssociationStatus = makeLRUCache(
470
482
  (accountId, tokenId) => `${accountId}-${tokenId}`,
471
483
  seconds(30),
472
484
  );
485
+
486
+ export const safeParseAccountId = (
487
+ address: string,
488
+ ): [Error, null] | [null, { accountId: string; checksum: string | null }] => {
489
+ const currency = findCryptoCurrencyById("hedera");
490
+ const currencyName = currency?.name ?? "Hedera";
491
+
492
+ try {
493
+ const accountId = AccountId.fromString(address);
494
+
495
+ // verify checksum if present
496
+ // FIXME: migrate to EntityIdHelper methods once SDK is upgraded:
497
+ // https://github.com/hiero-ledger/hiero-sdk-js/blob/main/src/EntityIdHelper.js#L197
498
+ // https://github.com/hiero-ledger/hiero-sdk-js/blob/main/src/EntityIdHelper.js#L446
499
+ const checksum: string | null = address.split("-")[1] ?? null;
500
+ if (checksum) {
501
+ const client = getHederaClient();
502
+ const recipientWithChecksum = accountId.toStringWithChecksum(client);
503
+ const expectedChecksum = recipientWithChecksum.split("-")[1];
504
+
505
+ if (checksum !== expectedChecksum) {
506
+ return [new HederaRecipientInvalidChecksum(), null];
507
+ }
508
+ }
509
+
510
+ const result = {
511
+ accountId: accountId.toString(),
512
+ checksum,
513
+ };
514
+
515
+ return [null, result];
516
+ } catch (err) {
517
+ return [new InvalidAddress("", { currencyName }), null];
518
+ }
519
+ };
package/src/errors.ts CHANGED
@@ -1,3 +1,15 @@
1
1
  import { createCustomErrorClass } from "@ledgerhq/errors";
2
2
 
3
3
  export const HederaAddAccountError = createCustomErrorClass("HederaAddAccountError");
4
+ export const HederaRecipientInvalidChecksum = createCustomErrorClass(
5
+ "HederaRecipientInvalidChecksum",
6
+ );
7
+ export const HederaInsufficientFundsForAssociation = createCustomErrorClass(
8
+ "HederaInsufficientFundsForAssociation",
9
+ );
10
+ export const HederaRecipientTokenAssociationRequired = createCustomErrorClass(
11
+ "HederaRecipientTokenAssociationRequired",
12
+ );
13
+ export const HederaRecipientTokenAssociationUnverified = createCustomErrorClass(
14
+ "HederaRecipientTokenAssociationUnverified",
15
+ );