@ledgerhq/live-common 34.52.0-nightly.0 → 34.52.0-nightly.1

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 (34) hide show
  1. package/lib/bridge/generic-alpaca/buildSubAccounts.d.ts +5 -8
  2. package/lib/bridge/generic-alpaca/buildSubAccounts.d.ts.map +1 -1
  3. package/lib/bridge/generic-alpaca/buildSubAccounts.js +37 -9
  4. package/lib/bridge/generic-alpaca/buildSubAccounts.js.map +1 -1
  5. package/lib/bridge/generic-alpaca/getAccountShape.d.ts.map +1 -1
  6. package/lib/bridge/generic-alpaca/getAccountShape.js +18 -17
  7. package/lib/bridge/generic-alpaca/getAccountShape.js.map +1 -1
  8. package/lib/bridge/generic-alpaca/types.d.ts +4 -1
  9. package/lib/bridge/generic-alpaca/types.d.ts.map +1 -1
  10. package/lib/bridge/generic-alpaca/utils.d.ts +2 -1
  11. package/lib/bridge/generic-alpaca/utils.d.ts.map +1 -1
  12. package/lib/bridge/generic-alpaca/utils.js +35 -3
  13. package/lib/bridge/generic-alpaca/utils.js.map +1 -1
  14. package/lib-es/bridge/generic-alpaca/buildSubAccounts.d.ts +5 -8
  15. package/lib-es/bridge/generic-alpaca/buildSubAccounts.d.ts.map +1 -1
  16. package/lib-es/bridge/generic-alpaca/buildSubAccounts.js +35 -7
  17. package/lib-es/bridge/generic-alpaca/buildSubAccounts.js.map +1 -1
  18. package/lib-es/bridge/generic-alpaca/getAccountShape.d.ts.map +1 -1
  19. package/lib-es/bridge/generic-alpaca/getAccountShape.js +20 -19
  20. package/lib-es/bridge/generic-alpaca/getAccountShape.js.map +1 -1
  21. package/lib-es/bridge/generic-alpaca/types.d.ts +4 -1
  22. package/lib-es/bridge/generic-alpaca/types.d.ts.map +1 -1
  23. package/lib-es/bridge/generic-alpaca/utils.d.ts +2 -1
  24. package/lib-es/bridge/generic-alpaca/utils.d.ts.map +1 -1
  25. package/lib-es/bridge/generic-alpaca/utils.js +33 -2
  26. package/lib-es/bridge/generic-alpaca/utils.js.map +1 -1
  27. package/package.json +10 -10
  28. package/src/bridge/generic-alpaca/buildSubAccounts.test.ts +537 -0
  29. package/src/bridge/generic-alpaca/buildSubAccounts.ts +58 -21
  30. package/src/bridge/generic-alpaca/getAccountShape.ts +26 -23
  31. package/src/bridge/generic-alpaca/tests/getAccountShape.test.ts +10 -1
  32. package/src/bridge/generic-alpaca/types.ts +5 -1
  33. package/src/bridge/generic-alpaca/utils.test.ts +31 -1
  34. package/src/bridge/generic-alpaca/utils.ts +48 -4
@@ -3,10 +3,18 @@ import { GetAccountShape, mergeOps } from "@ledgerhq/coin-framework/bridge/jsHel
3
3
  import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
4
4
  import BigNumber from "bignumber.js";
5
5
  import { getAlpacaApi } from "./alpaca";
6
- import { adaptCoreOperationToLiveOperation, extractBalance } from "./utils";
6
+ import { adaptCoreOperationToLiveOperation, cleanedOperation, extractBalance } from "./utils";
7
7
  import { inferSubOperations } from "@ledgerhq/coin-framework/serialization";
8
- import { buildSubAccounts, OperationCommon } from "./buildSubAccounts";
9
- import { Pagination } from "@ledgerhq/coin-framework/api/types";
8
+ import { buildSubAccounts, mergeSubAccounts } from "./buildSubAccounts";
9
+ import type { Operation, Pagination } from "@ledgerhq/coin-framework/api/types";
10
+ import type { OperationCommon } from "./types";
11
+
12
+ function isNftCoreOp(operation: Operation): boolean {
13
+ return (
14
+ typeof operation.details?.ledgerOpType === "string" &&
15
+ ["NFT_IN", "NFT_OUT"].includes(operation.details?.ledgerOpType)
16
+ );
17
+ }
10
18
 
11
19
  export function genericGetAccountShape(network: string, kind: string): GetAccountShape {
12
20
  return async (info, syncConfig) => {
@@ -56,39 +64,34 @@ export function genericGetAccountShape(network: string, kind: string): GetAccoun
56
64
  }
57
65
 
58
66
  const [newCoreOps] = await alpacaApi.listOperations(address, paginationParams);
59
- const newOps = newCoreOps.map(op =>
60
- adaptCoreOperationToLiveOperation(accountId, op),
61
- ) as OperationCommon[];
62
- const mergedOps = mergeOps(oldOps, newOps) as OperationCommon[];
67
+ const newOps = newCoreOps
68
+ .filter(op => !isNftCoreOp(op))
69
+ .map(op => adaptCoreOperationToLiveOperation(accountId, op)) as OperationCommon[];
63
70
 
64
- const assetOperations: OperationCommon[] = [];
65
- mergedOps.forEach(operation => {
66
- if (
71
+ const newAssetOperations = newOps.filter(
72
+ operation =>
67
73
  operation?.extra?.assetReference &&
68
74
  operation?.extra?.assetOwner &&
69
- !["OPT_IN", "OPT_OUT"].includes(operation.type)
70
- ) {
71
- assetOperations.push(operation);
72
- }
73
- });
74
-
75
- const subAccounts = await buildSubAccounts({
76
- currency,
75
+ !["OPT_IN", "OPT_OUT"].includes(operation.type),
76
+ );
77
+ const newSubAccounts = await buildSubAccounts({
77
78
  accountId,
78
79
  allTokenAssetsBalances,
79
80
  syncConfig,
80
- operations: assetOperations,
81
+ operations: newAssetOperations,
81
82
  getTokenFromAsset: alpacaApi.getTokenFromAsset,
82
83
  });
84
+ const subAccounts = mergeSubAccounts(initialAccount?.subAccounts ?? [], newSubAccounts);
83
85
 
84
- const operations = mergedOps.map(op => {
85
- const subOperations = inferSubOperations(op.hash, subAccounts);
86
+ const newOpsWithSubs = newOps.map(op => {
87
+ const subOperations = inferSubOperations(op.hash, newSubAccounts);
86
88
 
87
- return {
89
+ return cleanedOperation({
88
90
  ...op,
89
91
  subOperations,
90
- };
92
+ });
91
93
  });
94
+ const operations = mergeOps(oldOps, newOpsWithSubs) as OperationCommon[];
92
95
 
93
96
  const res = {
94
97
  id: accountId,
@@ -29,9 +29,11 @@ jest.mock("../alpaca", () => ({
29
29
 
30
30
  const adaptCoreOperationToLiveOperationMock = jest.fn();
31
31
  const extractBalanceMock = jest.fn();
32
+ const cleanedOperationMock = jest.fn();
32
33
  jest.mock("../utils", () => ({
33
34
  adaptCoreOperationToLiveOperation: (...a: any[]) => adaptCoreOperationToLiveOperationMock(...a),
34
35
  extractBalance: (...a: any[]) => extractBalanceMock(...a),
36
+ cleanedOperation: (...a: any[]) => cleanedOperationMock(...a),
35
37
  }));
36
38
 
37
39
  const inferSubOperationsMock = jest.fn();
@@ -40,8 +42,10 @@ jest.mock("@ledgerhq/coin-framework/serialization", () => ({
40
42
  }));
41
43
 
42
44
  const buildSubAccountsMock = jest.fn();
45
+ const mergeSubAccountsMock = jest.fn();
43
46
  jest.mock("../buildSubAccounts", () => ({
44
47
  buildSubAccounts: (...a: any[]) => buildSubAccountsMock(...a),
48
+ mergeSubAccounts: (...a: any[]) => mergeSubAccountsMock(...a),
45
49
  }));
46
50
 
47
51
  // Test matrix for Stellar & XRP
@@ -51,7 +55,7 @@ const chains = [
51
55
  { currency: { id: "tezos", name: "Tezos" }, network: "mainnet" },
52
56
  ];
53
57
 
54
- describe("genericGetAccountShape (stellar & xrp)", () => {
58
+ describe("genericGetAccountShape", () => {
55
59
  beforeEach(() => {
56
60
  jest.clearAllMocks();
57
61
  });
@@ -88,6 +92,11 @@ describe("genericGetAccountShape (stellar & xrp)", () => {
88
92
  }));
89
93
 
90
94
  mergeOpsMock.mockImplementation((oldOps, newOps) => [...newOps, ...oldOps]);
95
+ cleanedOperationMock.mockImplementation(operation => operation);
96
+ mergeSubAccountsMock.mockImplementation((oldSubAccounts, newSubAccounts) => [
97
+ ...newSubAccounts,
98
+ ...oldSubAccounts,
99
+ ]);
91
100
 
92
101
  buildSubAccountsMock.mockReturnValue([
93
102
  { id: `${currency.id}_subAcc1`, type: "TokenAccount" },
@@ -1,4 +1,4 @@
1
- import { TransactionCommon } from "@ledgerhq/types-live";
1
+ import type { Operation, TransactionCommon } from "@ledgerhq/types-live";
2
2
  import BigNumber from "bignumber.js";
3
3
  import type { Unit } from "@ledgerhq/types-cryptoassets";
4
4
 
@@ -32,3 +32,7 @@ export type GenericTransaction = TransactionCommon & {
32
32
  assetOwner?: string;
33
33
  networkInfo?: NetworkInfo | null;
34
34
  };
35
+
36
+ export interface OperationCommon extends Operation {
37
+ extra: Record<string, any>;
38
+ }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  adaptCoreOperationToLiveOperation,
3
+ cleanedOperation,
3
4
  extractBalance,
4
5
  findCryptoCurrencyByNetwork,
5
6
  transactionToIntent,
@@ -7,9 +8,38 @@ import {
7
8
  import BigNumber from "bignumber.js";
8
9
  import { Operation as CoreOperation } from "@ledgerhq/coin-framework/api/types";
9
10
  import { Account } from "@ledgerhq/types-live";
10
- import { GenericTransaction } from "./types";
11
+ import { GenericTransaction, OperationCommon } from "./types";
11
12
 
12
13
  describe("Alpaca utils", () => {
14
+ describe("cleanedOperation", () => {
15
+ it("creates a cleaned version of an operation without mutating it", () => {
16
+ const dirty = {
17
+ id: "id",
18
+ hash: "hash",
19
+ senders: ["sender"],
20
+ recipients: ["recipient"],
21
+ extra: { assetAmount: 5, assetReference: "USDC", paginationToken: "pagination" },
22
+ } as unknown as OperationCommon;
23
+
24
+ const clean = cleanedOperation(dirty);
25
+
26
+ expect(clean).toEqual({
27
+ id: "id",
28
+ hash: "hash",
29
+ senders: ["sender"],
30
+ recipients: ["recipient"],
31
+ extra: { paginationToken: "pagination" },
32
+ });
33
+ expect(dirty).toEqual({
34
+ id: "id",
35
+ hash: "hash",
36
+ senders: ["sender"],
37
+ recipients: ["recipient"],
38
+ extra: { assetAmount: 5, assetReference: "USDC", paginationToken: "pagination" },
39
+ });
40
+ });
41
+ });
42
+
13
43
  describe("transactionToIntent", () => {
14
44
  describe("type", () => {
15
45
  it("fallbacks to 'Payment' without a transaction mode", () => {
@@ -9,7 +9,7 @@ import {
9
9
  } from "@ledgerhq/coin-framework/api/types";
10
10
  import { findCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
11
11
  import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
12
- import { GenericTransaction } from "./types";
12
+ import { GenericTransaction, OperationCommon } from "./types";
13
13
 
14
14
  export function findCryptoCurrencyByNetwork(network: string): CryptoCurrency | undefined {
15
15
  const networksRemap = {
@@ -27,6 +27,30 @@ export function extractBalance(balances: Balance[], type: string): Balance {
27
27
  );
28
28
  }
29
29
 
30
+ function isStringArray(value: unknown): value is string[] {
31
+ return Array.isArray(value) && value.every(item => typeof item === "string");
32
+ }
33
+
34
+ export function cleanedOperation(operation: OperationCommon): OperationCommon {
35
+ if (!operation.extra) return operation;
36
+
37
+ const extraToClean = new Set([
38
+ "assetReference",
39
+ "assetAmount",
40
+ "assetOwner",
41
+ "assetSenders",
42
+ "assetRecipients",
43
+ "parentSenders",
44
+ "parentRecipients",
45
+ "ledgerOpType",
46
+ ]);
47
+ const cleanedExtra = Object.fromEntries(
48
+ Object.entries(operation.extra).filter(([key]) => !extraToClean.has(key)),
49
+ );
50
+
51
+ return { ...operation, extra: cleanedExtra };
52
+ }
53
+
30
54
  export function adaptCoreOperationToLiveOperation(accountId: string, op: CoreOperation): Operation {
31
55
  const opType = op.type as OperationType;
32
56
 
@@ -34,6 +58,10 @@ export function adaptCoreOperationToLiveOperation(accountId: string, op: CoreOpe
34
58
  assetReference?: string;
35
59
  assetOwner?: string;
36
60
  assetAmount?: string | undefined;
61
+ assetSenders?: string[];
62
+ assetRecipients?: string[];
63
+ parentSenders?: string[];
64
+ parentRecipients?: string[];
37
65
  ledgerOpType?: string | undefined;
38
66
  memo?: string | undefined;
39
67
  } = {};
@@ -46,6 +74,22 @@ export function adaptCoreOperationToLiveOperation(accountId: string, op: CoreOpe
46
74
  extra.assetAmount = op.details.assetAmount as string;
47
75
  }
48
76
 
77
+ if (isStringArray(op.details?.assetSenders)) {
78
+ extra.assetSenders = op.details?.assetSenders;
79
+ }
80
+
81
+ if (isStringArray(op.details?.assetRecipients)) {
82
+ extra.assetRecipients = op.details?.assetRecipients;
83
+ }
84
+
85
+ if (isStringArray(op.details?.parentSenders)) {
86
+ extra.parentSenders = op.details?.parentSenders;
87
+ }
88
+
89
+ if (isStringArray(op.details?.parentRecipients)) {
90
+ extra.parentRecipients = op.details?.parentRecipients;
91
+ }
92
+
49
93
  if (op.asset?.type !== "native") {
50
94
  extra.assetReference =
51
95
  "assetReference" in (op.asset ?? {}) ? (op.asset as any).assetReference : "";
@@ -67,11 +111,11 @@ export function adaptCoreOperationToLiveOperation(accountId: string, op: CoreOpe
67
111
  fee: bnFees,
68
112
  blockHash: op.tx.block.hash,
69
113
  blockHeight: op.tx.block.height,
70
- senders: op.senders,
71
- recipients: op.recipients,
114
+ senders: extra.parentSenders ?? op.senders,
115
+ recipients: extra.parentRecipients ?? op.recipients,
72
116
  date: op.tx.date,
73
117
  transactionSequenceNumber: op.details?.sequence as number,
74
- hasFailed: (op.details as unknown as { status?: string })?.status === "failed",
118
+ hasFailed: op.details?.status === "failed",
75
119
  extra,
76
120
  };
77
121