@ledgerhq/coin-canton 0.11.0-nightly.20251205111238 → 0.11.0

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 (84) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +27 -10
  3. package/README.md +1 -1
  4. package/lib/bridge/acceptOffer.d.ts.map +1 -1
  5. package/lib/bridge/acceptOffer.js +0 -8
  6. package/lib/bridge/acceptOffer.js.map +1 -1
  7. package/lib/bridge/getTransactionStatus.d.ts +0 -1
  8. package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
  9. package/lib/bridge/getTransactionStatus.js +17 -48
  10. package/lib/bridge/getTransactionStatus.js.map +1 -1
  11. package/lib/bridge/onboard.d.ts.map +1 -1
  12. package/lib/bridge/onboard.js +1 -4
  13. package/lib/bridge/onboard.js.map +1 -1
  14. package/lib/bridge/serialization.d.ts.map +1 -1
  15. package/lib/bridge/serialization.js +1 -3
  16. package/lib/bridge/serialization.js.map +1 -1
  17. package/lib/bridge/sync.d.ts.map +1 -1
  18. package/lib/bridge/sync.js +2 -6
  19. package/lib/bridge/sync.js.map +1 -1
  20. package/lib/network/gateway.d.ts +3 -20
  21. package/lib/network/gateway.d.ts.map +1 -1
  22. package/lib/network/gateway.js +16 -75
  23. package/lib/network/gateway.js.map +1 -1
  24. package/lib/test/cantonTestUtils.d.ts +1 -3
  25. package/lib/test/cantonTestUtils.d.ts.map +1 -1
  26. package/lib/test/cantonTestUtils.js +1 -1
  27. package/lib/test/cantonTestUtils.js.map +1 -1
  28. package/lib/types/bridge.d.ts +0 -2
  29. package/lib/types/bridge.d.ts.map +1 -1
  30. package/lib/types/errors.d.ts +0 -3
  31. package/lib/types/errors.d.ts.map +1 -1
  32. package/lib/types/errors.js +1 -2
  33. package/lib/types/errors.js.map +1 -1
  34. package/lib-es/bridge/acceptOffer.d.ts.map +1 -1
  35. package/lib-es/bridge/acceptOffer.js +0 -8
  36. package/lib-es/bridge/acceptOffer.js.map +1 -1
  37. package/lib-es/bridge/getTransactionStatus.d.ts +0 -1
  38. package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
  39. package/lib-es/bridge/getTransactionStatus.js +17 -47
  40. package/lib-es/bridge/getTransactionStatus.js.map +1 -1
  41. package/lib-es/bridge/onboard.d.ts.map +1 -1
  42. package/lib-es/bridge/onboard.js +2 -5
  43. package/lib-es/bridge/onboard.js.map +1 -1
  44. package/lib-es/bridge/serialization.d.ts.map +1 -1
  45. package/lib-es/bridge/serialization.js +1 -3
  46. package/lib-es/bridge/serialization.js.map +1 -1
  47. package/lib-es/bridge/sync.d.ts.map +1 -1
  48. package/lib-es/bridge/sync.js +2 -6
  49. package/lib-es/bridge/sync.js.map +1 -1
  50. package/lib-es/network/gateway.d.ts +3 -20
  51. package/lib-es/network/gateway.d.ts.map +1 -1
  52. package/lib-es/network/gateway.js +15 -70
  53. package/lib-es/network/gateway.js.map +1 -1
  54. package/lib-es/test/cantonTestUtils.d.ts +1 -3
  55. package/lib-es/test/cantonTestUtils.d.ts.map +1 -1
  56. package/lib-es/test/cantonTestUtils.js +1 -1
  57. package/lib-es/test/cantonTestUtils.js.map +1 -1
  58. package/lib-es/types/bridge.d.ts +0 -2
  59. package/lib-es/types/bridge.d.ts.map +1 -1
  60. package/lib-es/types/errors.d.ts +0 -3
  61. package/lib-es/types/errors.d.ts.map +1 -1
  62. package/lib-es/types/errors.js +0 -1
  63. package/lib-es/types/errors.js.map +1 -1
  64. package/package.json +6 -6
  65. package/src/api/getBalance.integ.test.ts +1 -1
  66. package/src/api/lastBlock.integ.test.ts +1 -1
  67. package/src/api/listOperations.integ.test.ts +1 -1
  68. package/src/bridge/acceptOffer.test.ts +4 -43
  69. package/src/bridge/acceptOffer.ts +1 -9
  70. package/src/bridge/getTransactionStatus.test.ts +3 -95
  71. package/src/bridge/getTransactionStatus.ts +24 -61
  72. package/src/bridge/onboard.integ.test.ts +4 -85
  73. package/src/bridge/onboard.test.ts +1 -107
  74. package/src/bridge/onboard.ts +1 -6
  75. package/src/bridge/prepareTransaction.test.ts +1 -1
  76. package/src/bridge/serialization.ts +1 -3
  77. package/src/bridge/sync.integ.test.ts +1 -1
  78. package/src/bridge/sync.ts +2 -6
  79. package/src/network/gateway.integ.test.ts +1 -24
  80. package/src/network/gateway.test.ts +2 -64
  81. package/src/network/gateway.ts +26 -98
  82. package/src/test/cantonTestUtils.ts +1 -1
  83. package/src/types/bridge.ts +0 -2
  84. package/src/types/errors.ts +0 -2
@@ -15,11 +15,10 @@ function isCantonAccountRaw(accountRaw: AccountRaw): accountRaw is CantonAccount
15
15
  }
16
16
 
17
17
  function toResourcesRaw(r: CantonResources): CantonResourcesRaw {
18
- const { instrumentUtxoCounts, pendingTransferProposals, publicKey } = r;
18
+ const { instrumentUtxoCounts, pendingTransferProposals } = r;
19
19
  return {
20
20
  instrumentUtxoCounts,
21
21
  pendingTransferProposals,
22
- publicKey,
23
22
  };
24
23
  }
25
24
 
@@ -27,7 +26,6 @@ function fromResourcesRaw(r: CantonResourcesRaw): CantonResources {
27
26
  return {
28
27
  instrumentUtxoCounts: r.instrumentUtxoCounts,
29
28
  pendingTransferProposals: r.pendingTransferProposals,
30
- publicKey: r.publicKey,
31
29
  };
32
30
  }
33
31
 
@@ -47,7 +47,7 @@ const mockSignerContext = jest.fn().mockImplementation((deviceId, callback) => {
47
47
  describe.skip("sync (devnet)", () => {
48
48
  beforeAll(async () => {
49
49
  coinConfig.setCoinConfig(() => ({
50
- gatewayUrl: "https://canton-gateway-devnet.api.live.ledger-test.com",
50
+ gatewayUrl: "https://canton-gateway.api.live.ledger-test.com",
51
51
  useGateway: true,
52
52
  networkType: "devnet",
53
53
  nativeInstrumentId: "Amulet",
@@ -94,17 +94,15 @@ export function makeGetAccountShape(
94
94
  const { address, currency, derivationMode, derivationPath, initialAccount } = info;
95
95
 
96
96
  let xpubOrAddress = initialAccount?.xpub || "";
97
- let publicKey: string | undefined = initialAccount?.cantonResources?.publicKey;
98
97
 
99
- if (!xpubOrAddress || !publicKey) {
98
+ if (!xpubOrAddress) {
100
99
  const getAddress = resolver(signerContext);
101
- const addressResult = await getAddress(info.deviceId || "", {
100
+ const { publicKey } = await getAddress(info.deviceId || "", {
102
101
  path: derivationPath,
103
102
  currency: currency,
104
103
  derivationMode: derivationMode,
105
104
  verify: false,
106
105
  });
107
- publicKey = addressResult.publicKey;
108
106
 
109
107
  const { isOnboarded, partyId } = await isAccountOnboarded(currency, publicKey);
110
108
  if (isOnboarded && partyId) {
@@ -170,7 +168,6 @@ export function makeGetAccountShape(
170
168
  cantonResources: {
171
169
  instrumentUtxoCounts,
172
170
  pendingTransferProposals,
173
- publicKey,
174
171
  },
175
172
  });
176
173
 
@@ -198,7 +195,6 @@ export function makeGetAccountShape(
198
195
  cantonResources: {
199
196
  instrumentUtxoCounts,
200
197
  pendingTransferProposals,
201
- publicKey,
202
198
  },
203
199
  };
204
200
 
@@ -30,7 +30,7 @@ describe("gateway (devnet)", () => {
30
30
 
31
31
  beforeAll(async () => {
32
32
  coinConfig.setCoinConfig(() => ({
33
- gatewayUrl: "https://canton-gateway-devnet.api.live.ledger-test.com",
33
+ gatewayUrl: "https://canton-gateway.api.live.ledger-test.com",
34
34
  useGateway: true,
35
35
  nativeInstrumentId: "Amulet",
36
36
  networkType: "devnet",
@@ -126,29 +126,6 @@ describe("gateway (devnet)", () => {
126
126
  },
127
127
  30000,
128
128
  );
129
-
130
- it("should handle PARTY_ALREADY_EXISTS error and return party_id and public_key", async () => {
131
- // GIVEN
132
- const { keyPair, partyId } = getOnboardedAccount();
133
- const signature = keyPair.sign(prepareResponse?.transactions?.combined_hash || "");
134
-
135
- // WHEN
136
- const response = await submitOnboarding(
137
- mockCurrency,
138
- keyPair.publicKeyHex,
139
- prepareResponse!,
140
- {
141
- signature,
142
- },
143
- );
144
-
145
- // THEN
146
- expect(response).toHaveProperty("party");
147
- expect(response.party).toHaveProperty("party_id");
148
- expect(response.party).toHaveProperty("public_key");
149
- expect(response.party.party_id).toBe(partyId);
150
- expect(response.party.public_key).toBe(keyPair.publicKeyHex);
151
- }, 30000);
152
129
  });
153
130
 
154
131
  describe("getLedgerEnd", () => {
@@ -1,15 +1,6 @@
1
- import {
2
- getBalance,
3
- isPartyAlreadyExists,
4
- submitOnboarding,
5
- type GetBalanceResponse,
6
- type InstrumentBalance,
7
- type OnboardingPrepareResponse,
8
- } from "./gateway";
1
+ import { getBalance, type GetBalanceResponse, type InstrumentBalance } from "./gateway";
9
2
  import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
10
3
  import coinConfig from "../config";
11
- import { TopologyChangeError } from "../types/errors";
12
- import { LedgerAPI4xx } from "@ledgerhq/errors";
13
4
 
14
5
  jest.mock("@ledgerhq/live-network", () => ({
15
6
  __esModule: true,
@@ -40,7 +31,7 @@ describe("getBalance", () => {
40
31
 
41
32
  beforeAll(() => {
42
33
  coinConfig.setCoinConfig(() => ({
43
- gatewayUrl: "https://canton-gateway-devnet.api.live.ledger-test.com",
34
+ gatewayUrl: "https://canton-gateway.api.live.ledger-test.com",
44
35
  useGateway: true,
45
36
  networkType: "devnet",
46
37
  nativeInstrumentId: "Amulet",
@@ -72,57 +63,4 @@ describe("getBalance", () => {
72
63
 
73
64
  expect(result).toEqual(mockResponse.balances);
74
65
  });
75
-
76
- it("should handle PARTY_ALREADY_EXISTS error", async () => {
77
- const mockPartyId = "test-party-id-123";
78
- const mockPublicKey = "test-public-key-456";
79
- const error = new LedgerAPI4xx("Party already exists", {
80
- status: 409,
81
- url: undefined,
82
- method: "POST",
83
- });
84
- const mockPrepareResponse: OnboardingPrepareResponse = {
85
- party_id: mockPartyId,
86
- party_name: "Test Party",
87
- public_key_fingerprint: "fingerprint",
88
- transactions: {
89
- namespace_transaction: {
90
- serialized: "",
91
- transaction: { operation: "", serial: 0, mapping: {} },
92
- hash: "",
93
- },
94
- party_to_key_transaction: {
95
- serialized: "",
96
- transaction: { operation: "", serial: 0, mapping: {} },
97
- hash: "",
98
- },
99
- party_to_participant_transaction: {
100
- serialized: "",
101
- transaction: { operation: "", serial: 0, mapping: {} },
102
- hash: "",
103
- },
104
- combined_hash: "",
105
- },
106
- };
107
-
108
- mockNetwork.mockRejectedValue(error);
109
-
110
- const result = await submitOnboarding(mockCurrency, mockPublicKey, mockPrepareResponse, {
111
- signature: "test-signature",
112
- });
113
-
114
- expect(isPartyAlreadyExists(error)).toBe(true);
115
- expect(result.party).toEqual({ party_id: mockPartyId, public_key: mockPublicKey });
116
- });
117
-
118
- it("should handle PARTY_NOT_FOUND_BY_ID error", async () => {
119
- const partyNotFoundError = new LedgerAPI4xx("Party not found", {
120
- status: 400,
121
- url: undefined,
122
- method: "GET",
123
- });
124
- mockNetwork.mockRejectedValue(partyNotFoundError);
125
-
126
- await expect(getBalance(mockCurrency, "test-party-id")).rejects.toThrow(TopologyChangeError);
127
- });
128
66
  });
@@ -1,6 +1,5 @@
1
1
  import network from "@ledgerhq/live-network";
2
- import type { LiveNetworkRequest, LiveNetworkResponse } from "@ledgerhq/live-network/network";
3
- import { makeLRUCache, minutes } from "@ledgerhq/live-network/cache";
2
+ import type { LiveNetworkRequest } from "@ledgerhq/live-network/network";
4
3
  import { getEnv } from "@ledgerhq/live-env";
5
4
  import coinConfig from "../config";
6
5
  import {
@@ -12,7 +11,6 @@ import {
12
11
  } from "../types/onboard";
13
12
  import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
14
13
  import type { CantonSignature } from "../types/signer";
15
- import { TopologyChangeError } from "../types/errors";
16
14
 
17
15
  export type OnboardingPrepareResponse = {
18
16
  party_id: string;
@@ -21,29 +19,17 @@ export type OnboardingPrepareResponse = {
21
19
  transactions: {
22
20
  namespace_transaction: {
23
21
  serialized: string;
24
- transaction: {
25
- operation: string;
26
- serial: number;
27
- mapping: Record<string, unknown>;
28
- };
22
+ json: object;
29
23
  hash: string;
30
24
  };
31
25
  party_to_key_transaction: {
32
26
  serialized: string;
33
- transaction: {
34
- operation: string;
35
- serial: number;
36
- mapping: Record<string, unknown>;
37
- };
27
+ json: object;
38
28
  hash: string;
39
29
  };
40
30
  party_to_participant_transaction: {
41
31
  serialized: string;
42
- transaction: {
43
- operation: string;
44
- serial: number;
45
- mapping: Record<string, unknown>;
46
- };
32
+ json: object;
47
33
  hash: string;
48
34
  };
49
35
  combined_hash: string;
@@ -328,56 +314,23 @@ export type OperationInfo =
328
314
  };
329
315
 
330
316
  const getGatewayUrl = (currency: CryptoCurrency) => coinConfig.getCoinConfig(currency).gatewayUrl;
331
- const getNodeId = (currency: CryptoCurrency) => {
332
- const overrideNodeId = getEnv("CANTON_NODE_ID_OVERRIDE");
333
- if (overrideNodeId) {
334
- return overrideNodeId;
335
- }
336
- return coinConfig.getCoinConfig(currency).nodeId || "ledger-live-devnet";
337
- };
317
+ const getNodeId = (currency: CryptoCurrency) =>
318
+ coinConfig.getCoinConfig(currency).nodeId || "ledger-live-devnet";
338
319
  export const getNetworkType = (currency: CryptoCurrency) =>
339
320
  coinConfig.getCoinConfig(currency).networkType;
340
321
 
341
- export const isPartyNotFound = (error: unknown): boolean => {
342
- if (error instanceof Error) {
343
- const errorMessage = error.message.toLowerCase().replace(/_/g, " ");
344
- return errorMessage.includes("party") && errorMessage.includes("not found");
345
- }
346
- return false;
347
- };
348
-
349
- export const isPartyAlreadyExists = (error: unknown): boolean => {
350
- if (error instanceof Error) {
351
- const errorMessage = error.message.toLowerCase().replace(/_/g, " ");
352
- return errorMessage.includes("party") && errorMessage.includes("already exists");
353
- }
354
- return false;
355
- };
356
-
357
- const gatewayNetwork = async <T, U = unknown>(
358
- req: LiveNetworkRequest<U>,
359
- ): Promise<LiveNetworkResponse<T>> => {
322
+ const gatewayNetwork = <T, U = unknown>(req: LiveNetworkRequest<U>) => {
360
323
  const API_KEY = getEnv("CANTON_API_KEY");
361
- try {
362
- return await network<T, U>({
363
- ...req,
364
- headers: {
365
- ...(req.headers || {}),
366
- ...(API_KEY && { "X-Ledger-Canton-Api-Key": API_KEY }),
367
- },
368
- });
369
- } catch (error) {
370
- if (isPartyNotFound(error)) {
371
- throw new TopologyChangeError("Topology change detected. Re-onboarding required.");
372
- }
373
- throw error;
374
- }
324
+ return network<T, U>({
325
+ ...req,
326
+ headers: {
327
+ ...(req.headers || {}),
328
+ ...(API_KEY && { "X-Ledger-Canton-Api-Key": API_KEY }),
329
+ },
330
+ });
375
331
  };
376
332
 
377
- export async function prepareOnboarding(
378
- currency: CryptoCurrency,
379
- pubKey: string,
380
- ): Promise<OnboardingPrepareResponse> {
333
+ export async function prepareOnboarding(currency: CryptoCurrency, pubKey: string) {
381
334
  const gatewayUrl = getGatewayUrl(currency);
382
335
  const nodeId = getNodeId(currency);
383
336
  const fullUrl = `${gatewayUrl}/v1/node/${nodeId}/onboarding/prepare`;
@@ -394,38 +347,13 @@ export async function prepareOnboarding(
394
347
  return data;
395
348
  }
396
349
 
397
- export async function isTopologyChangeRequired(currency: CryptoCurrency, pubKey: string) {
398
- try {
399
- const response = await prepareOnboarding(currency, pubKey);
400
- // if response is not undefined (we have a transaction to sign) topology change is required
401
- if (response) {
402
- return true;
403
- }
404
- return false;
405
- } catch (error) {
406
- if (isPartyAlreadyExists(error)) {
407
- return false;
408
- }
409
- throw error;
410
- }
411
- }
412
-
413
- const getIsTopologyChangeRequiredCacheKey = (currency: CryptoCurrency, pubKey: string): string => {
414
- const nodeId = getNodeId(currency);
415
- return `${pubKey}_${nodeId}`;
350
+ type OnboardingSubmitError409 = {
351
+ partyId: string;
352
+ status: 409;
353
+ type: "PARTY_ALREADY_EXISTS";
354
+ message: string;
416
355
  };
417
356
 
418
- export const isTopologyChangeRequiredCached = makeLRUCache(
419
- isTopologyChangeRequired,
420
- getIsTopologyChangeRequiredCacheKey,
421
- minutes(10),
422
- );
423
-
424
- export function clearIsTopologyChangeRequiredCache(currency: CryptoCurrency, pubKey: string): void {
425
- const cacheKey = getIsTopologyChangeRequiredCacheKey(currency, pubKey);
426
- isTopologyChangeRequiredCached.clear(cacheKey);
427
- }
428
-
429
357
  export async function submitOnboarding(
430
358
  currency: CryptoCurrency,
431
359
  publicKey: string,
@@ -447,19 +375,19 @@ export async function submitOnboarding(
447
375
  },
448
376
  });
449
377
  return data;
450
- } catch (error) {
451
- if (isPartyAlreadyExists(error)) {
452
- // If party already exists, use party_id from prepare response
453
- // The network layer strips custom properties from errors, so we can't extract partyId from error
378
+ } catch (e) {
379
+ if (e instanceof Error && "type" in e && e.type === "PARTY_ALREADY_EXISTS") {
380
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
381
+ const { partyId } = e as unknown as OnboardingSubmitError409;
454
382
  return {
455
383
  party: {
456
- party_id: prepareResponse.party_id,
384
+ party_id: partyId,
457
385
  public_key: publicKey,
458
386
  },
459
387
  };
460
388
  }
461
389
 
462
- throw error;
390
+ throw e;
463
391
  }
464
392
  }
465
393
 
@@ -225,7 +225,7 @@ export function createMockSigner(keyPair: CantonTestKeyPair) {
225
225
 
226
226
  const cleanHash = hashToSign.startsWith("0x") ? hashToSign.slice(2) : hashToSign;
227
227
  const signature = keyPair.sign(cleanHash);
228
- return { signature };
228
+ return signature;
229
229
  },
230
230
  };
231
231
  }
@@ -78,12 +78,10 @@ export type TransactionStatusRaw = TransactionStatusCommonRaw;
78
78
  export type CantonResources = {
79
79
  instrumentUtxoCounts: Record<string, number>;
80
80
  pendingTransferProposals: TransferProposal[];
81
- publicKey: string;
82
81
  };
83
82
  export type CantonResourcesRaw = {
84
83
  instrumentUtxoCounts: Record<string, number>;
85
84
  pendingTransferProposals: TransferProposal[];
86
- publicKey: string;
87
85
  };
88
86
 
89
87
  export type CantonAccount = Account & {
@@ -4,5 +4,3 @@ export const SimulationError = createCustomErrorClass("SimulationError");
4
4
 
5
5
  export const TooManyUtxosCritical = createCustomErrorClass("TooManyUtxosCritical");
6
6
  export const TooManyUtxosWarning = createCustomErrorClass("TooManyUtxosWarning");
7
-
8
- export const TopologyChangeError = createCustomErrorClass("TopologyChangeError");