@ledgerhq/coin-canton 0.8.0-nightly.1 → 0.8.0-nightly.3

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 (46) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/lib/bridge/onboard.d.ts +2 -2
  4. package/lib/bridge/onboard.d.ts.map +1 -1
  5. package/lib/bridge/onboard.js +7 -4
  6. package/lib/bridge/onboard.js.map +1 -1
  7. package/lib/bridge/onboard.test.d.ts +2 -0
  8. package/lib/bridge/onboard.test.d.ts.map +1 -0
  9. package/lib/bridge/onboard.test.js +84 -0
  10. package/lib/bridge/onboard.test.js.map +1 -0
  11. package/lib/bridge/sync.d.ts.map +1 -1
  12. package/lib/bridge/sync.js +6 -2
  13. package/lib/bridge/sync.js.map +1 -1
  14. package/lib/bridge/sync.test.d.ts +2 -0
  15. package/lib/bridge/sync.test.d.ts.map +1 -0
  16. package/lib/bridge/sync.test.js +201 -0
  17. package/lib/bridge/sync.test.js.map +1 -0
  18. package/lib/network/gateway.d.ts +9 -0
  19. package/lib/network/gateway.d.ts.map +1 -1
  20. package/lib/network/gateway.js +8 -0
  21. package/lib/network/gateway.js.map +1 -1
  22. package/lib-es/bridge/onboard.d.ts +2 -2
  23. package/lib-es/bridge/onboard.d.ts.map +1 -1
  24. package/lib-es/bridge/onboard.js +8 -5
  25. package/lib-es/bridge/onboard.js.map +1 -1
  26. package/lib-es/bridge/onboard.test.d.ts +2 -0
  27. package/lib-es/bridge/onboard.test.d.ts.map +1 -0
  28. package/lib-es/bridge/onboard.test.js +49 -0
  29. package/lib-es/bridge/onboard.test.js.map +1 -0
  30. package/lib-es/bridge/sync.d.ts.map +1 -1
  31. package/lib-es/bridge/sync.js +6 -2
  32. package/lib-es/bridge/sync.js.map +1 -1
  33. package/lib-es/bridge/sync.test.d.ts +2 -0
  34. package/lib-es/bridge/sync.test.d.ts.map +1 -0
  35. package/lib-es/bridge/sync.test.js +163 -0
  36. package/lib-es/bridge/sync.test.js.map +1 -0
  37. package/lib-es/network/gateway.d.ts +9 -0
  38. package/lib-es/network/gateway.d.ts.map +1 -1
  39. package/lib-es/network/gateway.js +7 -0
  40. package/lib-es/network/gateway.js.map +1 -1
  41. package/package.json +4 -4
  42. package/src/bridge/onboard.test.ts +59 -0
  43. package/src/bridge/onboard.ts +10 -5
  44. package/src/bridge/sync.test.ts +185 -0
  45. package/src/bridge/sync.ts +8 -2
  46. package/src/network/gateway.ts +17 -0
@@ -0,0 +1,185 @@
1
+ import { makeGetAccountShape } from "./sync";
2
+ import { OperationInfo } from "../network/gateway";
3
+ import * as gateway from "../network/gateway";
4
+ import * as onboard from "./onboard";
5
+ import * as config from "../config";
6
+ import resolver from "../signer";
7
+ import { AccountShapeInfo } from "@ledgerhq/coin-framework/bridge/jsHelpers";
8
+ import { Account } from "@ledgerhq/types-live";
9
+ import BigNumber from "bignumber.js";
10
+
11
+ jest.mock("../network/gateway");
12
+ jest.mock("../signer");
13
+ jest.mock("../config");
14
+ jest.mock("./onboard");
15
+
16
+ const mockedGetBalance = gateway.getBalance as jest.Mock;
17
+ const mockedGetLedgerEnd = gateway.getLedgerEnd as jest.Mock;
18
+ const mockedGetOperations = gateway.getOperations as jest.Mock;
19
+ const mockedResolver = resolver as jest.Mock;
20
+ const mockedIsOnboarded = onboard.isAccountOnboarded as jest.Mock;
21
+ const mockedIsAuthorized = onboard.isAccountAuthorized as jest.Mock;
22
+ const mockedCoinConfig = config.default.getCoinConfig as jest.Mock;
23
+
24
+ const sampleCurrency = {
25
+ id: "testcoin",
26
+ };
27
+
28
+ describe("makeGetAccountShape", () => {
29
+ const fakeSignerContext = {} as any;
30
+
31
+ const defaultInfo = {
32
+ address: "addr1",
33
+ currency: sampleCurrency,
34
+ derivationMode: "",
35
+ derivationPath: "44'/0'/0'/0/0",
36
+ deviceId: "fakeDevice",
37
+ initialAccount: undefined,
38
+ };
39
+
40
+ beforeEach(() => {
41
+ jest.clearAllMocks();
42
+
43
+ mockedResolver.mockReturnValue(async () => ({
44
+ publicKey: "FAKE_PUBLIC_KEY",
45
+ }));
46
+
47
+ mockedIsOnboarded.mockResolvedValue({
48
+ isOnboarded: true,
49
+ partyId: "party123",
50
+ });
51
+
52
+ mockedCoinConfig.mockReturnValue({
53
+ nativeInstrumentId: "Native",
54
+ minReserve: "0",
55
+ });
56
+
57
+ mockedIsAuthorized.mockResolvedValue(true);
58
+ mockedGetLedgerEnd.mockResolvedValue(12345);
59
+ });
60
+
61
+ it("should return a valid account shape with correct balances and operations", async () => {
62
+ mockedGetBalance.mockResolvedValue([
63
+ {
64
+ instrument_id: "Native",
65
+ amount: "1000",
66
+ locked: false,
67
+ },
68
+ ]);
69
+ mockedGetOperations.mockResolvedValue({
70
+ operations: [
71
+ {
72
+ transaction_hash: "tx1",
73
+ uid: "uid1",
74
+ type: "Send",
75
+ fee: { value: "5" },
76
+ transfers: [
77
+ {
78
+ value: "100",
79
+ details: {
80
+ metadata: {
81
+ reason: "test transfer",
82
+ },
83
+ },
84
+ },
85
+ ],
86
+ transaction_timestamp: new Date().toISOString(),
87
+ senders: ["party123"],
88
+ recipients: ["party456"],
89
+ block: {
90
+ height: 1,
91
+ hash: "blockhash1",
92
+ },
93
+ } as OperationInfo,
94
+ ],
95
+ });
96
+ const getAccountShape = makeGetAccountShape(fakeSignerContext);
97
+ const shape = await getAccountShape(defaultInfo as AccountShapeInfo<Account>, {
98
+ paginationConfig: {},
99
+ });
100
+
101
+ expect(shape).toHaveProperty("id");
102
+ expect(shape.balance).toEqual(BigNumber(1000));
103
+ expect(shape.operations?.length).toBe(1);
104
+ expect((shape.operations as any)[0].type).toBe("OUT");
105
+ expect((shape.operations as any)[0].value).toEqual(BigNumber(105)); // 100 + 5 fee
106
+ expect(shape.spendableBalance).toEqual(BigNumber(1000));
107
+ expect(shape.used).toBe(true);
108
+ });
109
+
110
+ it("should handle locked balances correctly", async () => {
111
+ mockedGetBalance.mockResolvedValue([
112
+ {
113
+ instrument_id: "LockedNative",
114
+ amount: "1000",
115
+ locked: true,
116
+ },
117
+ {
118
+ instrument_id: "Native",
119
+ amount: "10",
120
+ locked: false,
121
+ },
122
+ ]);
123
+
124
+ const getAccountShape = makeGetAccountShape(fakeSignerContext);
125
+ const shape = await getAccountShape(defaultInfo as AccountShapeInfo<Account>, {
126
+ paginationConfig: {},
127
+ });
128
+
129
+ expect(shape).toBeDefined();
130
+ expect(shape.balance).toEqual(BigNumber(1010));
131
+ expect(shape.spendableBalance).toEqual(BigNumber(10));
132
+ });
133
+
134
+ it("should handle empty balances correctly", async () => {
135
+ mockedGetBalance.mockResolvedValue([]);
136
+
137
+ const getAccountShape = makeGetAccountShape(fakeSignerContext);
138
+ const shape = await getAccountShape(defaultInfo as AccountShapeInfo<Account>, {
139
+ paginationConfig: {},
140
+ });
141
+
142
+ expect(shape).toBeDefined();
143
+ expect(shape.balance).toEqual(BigNumber(0));
144
+ expect(shape.spendableBalance).toEqual(BigNumber(0));
145
+ });
146
+
147
+ it("should default to FEES operation type when transferValue is 0", async () => {
148
+ mockedGetOperations.mockResolvedValue({
149
+ operations: [
150
+ {
151
+ transaction_hash: "tx2",
152
+ uid: "uid2",
153
+ type: "Send",
154
+ fee: { value: "3" },
155
+ transfers: [
156
+ {
157
+ value: "0",
158
+ details: {
159
+ metadata: {
160
+ reason: "fee only",
161
+ },
162
+ },
163
+ },
164
+ ],
165
+ transaction_timestamp: new Date().toISOString(),
166
+ senders: ["party123"],
167
+ recipients: ["party456"],
168
+ block: {
169
+ height: 2,
170
+ hash: "blockhash2",
171
+ },
172
+ } as OperationInfo,
173
+ ],
174
+ });
175
+
176
+ const getAccountShape = makeGetAccountShape(fakeSignerContext);
177
+ const shape: any = await getAccountShape(defaultInfo as AccountShapeInfo<Account>, {
178
+ paginationConfig: {},
179
+ });
180
+ expect(shape).toBeDefined();
181
+ expect(shape.operations[0].type).toBe("FEES");
182
+ // In this case, value should equal the fee
183
+ expect(shape.operations[0].value).toEqual(BigNumber(3));
184
+ });
185
+ });
@@ -33,7 +33,13 @@ const txInfoToOperationAdapter =
33
33
  } else if (txInfo.type === "Initialize") {
34
34
  type = "PRE_APPROVAL";
35
35
  }
36
- const value = new BigNumber(transferValue);
36
+ let value = new BigNumber(transferValue);
37
+
38
+ if (type === "OUT" || type === "FEES") {
39
+ // We add fees when it's an outgoing transaction or a fees-only transaction
40
+ value = value.plus(fee);
41
+ }
42
+
37
43
  const feeValue = new BigNumber(fee);
38
44
  const memo = details.metadata.reason;
39
45
 
@@ -127,7 +133,7 @@ export function makeGetAccountShape(
127
133
  operations = mergeOps(oldOperations, newOperations);
128
134
  }
129
135
 
130
- const isAuthorized = await isAccountAuthorized(operations, xpubOrAddress);
136
+ const isAuthorized = await isAccountAuthorized(currency, xpubOrAddress);
131
137
  const used = isAuthorized && totalBalance.gt(0);
132
138
 
133
139
  const blockHeight = await getLedgerEnd(currency);
@@ -549,3 +549,20 @@ export async function submitPreApprovalTransaction(
549
549
  updateId: data.update_id,
550
550
  } satisfies PreApprovalResult;
551
551
  }
552
+
553
+ type GetTransferPreApprovalResponse = {
554
+ contract_id: string;
555
+ receiver: string;
556
+ provider: string;
557
+ valid_from: string; // ISO 8601 date string
558
+ last_renewed_at: string; // ISO 8601 date
559
+ expires_at: string; // ISO 8601 date string
560
+ };
561
+
562
+ export async function getTransferPreApproval(currency: CryptoCurrency, partyId: string) {
563
+ const { data } = await gatewayNetwork<GetTransferPreApprovalResponse>({
564
+ method: "GET",
565
+ url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transfer-preapproval`,
566
+ });
567
+ return data;
568
+ }