@ledgerhq/coin-canton 0.7.0-nightly.2 → 0.7.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 (102) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +17 -0
  3. package/lib/api/lastBlock.integ.test.js +0 -15
  4. package/lib/api/lastBlock.integ.test.js.map +1 -1
  5. package/lib/bridge/index.d.ts.map +1 -1
  6. package/lib/bridge/index.js +2 -5
  7. package/lib/bridge/index.js.map +1 -1
  8. package/lib/bridge/onboard.d.ts +11 -6
  9. package/lib/bridge/onboard.d.ts.map +1 -1
  10. package/lib/bridge/onboard.integ.test.js +49 -27
  11. package/lib/bridge/onboard.integ.test.js.map +1 -1
  12. package/lib/bridge/onboard.js +45 -152
  13. package/lib/bridge/onboard.js.map +1 -1
  14. package/lib/bridge/signOperation.d.ts.map +1 -1
  15. package/lib/bridge/signOperation.js +5 -5
  16. package/lib/bridge/signOperation.js.map +1 -1
  17. package/lib/bridge/sync.d.ts +3 -2
  18. package/lib/bridge/sync.d.ts.map +1 -1
  19. package/lib/bridge/sync.integ.test.js +31 -14
  20. package/lib/bridge/sync.integ.test.js.map +1 -1
  21. package/lib/bridge/sync.js +71 -57
  22. package/lib/bridge/sync.js.map +1 -1
  23. package/lib/common-logic/utils.js +2 -2
  24. package/lib/common-logic/utils.js.map +1 -1
  25. package/lib/common-logic/utils.test.js +21 -6
  26. package/lib/common-logic/utils.test.js.map +1 -1
  27. package/lib/config.d.ts +1 -1
  28. package/lib/config.d.ts.map +1 -1
  29. package/lib/network/gateway.d.ts +9 -9
  30. package/lib/network/gateway.d.ts.map +1 -1
  31. package/lib/network/gateway.integ.test.js +31 -17
  32. package/lib/network/gateway.integ.test.js.map +1 -1
  33. package/lib/network/gateway.js +33 -15
  34. package/lib/network/gateway.js.map +1 -1
  35. package/lib/types/bridge.d.ts +6 -16
  36. package/lib/types/bridge.d.ts.map +1 -1
  37. package/lib/types/onboard.d.ts +5 -5
  38. package/lib/types/onboard.d.ts.map +1 -1
  39. package/lib/types/onboard.js +10 -10
  40. package/lib/types/onboard.js.map +1 -1
  41. package/lib-es/api/lastBlock.integ.test.js +0 -15
  42. package/lib-es/api/lastBlock.integ.test.js.map +1 -1
  43. package/lib-es/bridge/index.d.ts.map +1 -1
  44. package/lib-es/bridge/index.js +3 -6
  45. package/lib-es/bridge/index.js.map +1 -1
  46. package/lib-es/bridge/onboard.d.ts +11 -6
  47. package/lib-es/bridge/onboard.d.ts.map +1 -1
  48. package/lib-es/bridge/onboard.integ.test.js +37 -15
  49. package/lib-es/bridge/onboard.integ.test.js.map +1 -1
  50. package/lib-es/bridge/onboard.js +44 -152
  51. package/lib-es/bridge/onboard.js.map +1 -1
  52. package/lib-es/bridge/signOperation.d.ts.map +1 -1
  53. package/lib-es/bridge/signOperation.js +5 -5
  54. package/lib-es/bridge/signOperation.js.map +1 -1
  55. package/lib-es/bridge/sync.d.ts +3 -2
  56. package/lib-es/bridge/sync.d.ts.map +1 -1
  57. package/lib-es/bridge/sync.integ.test.js +26 -9
  58. package/lib-es/bridge/sync.integ.test.js.map +1 -1
  59. package/lib-es/bridge/sync.js +71 -56
  60. package/lib-es/bridge/sync.js.map +1 -1
  61. package/lib-es/common-logic/utils.js +2 -2
  62. package/lib-es/common-logic/utils.js.map +1 -1
  63. package/lib-es/common-logic/utils.test.js +21 -6
  64. package/lib-es/common-logic/utils.test.js.map +1 -1
  65. package/lib-es/config.d.ts +1 -1
  66. package/lib-es/config.d.ts.map +1 -1
  67. package/lib-es/network/gateway.d.ts +9 -9
  68. package/lib-es/network/gateway.d.ts.map +1 -1
  69. package/lib-es/network/gateway.integ.test.js +31 -17
  70. package/lib-es/network/gateway.integ.test.js.map +1 -1
  71. package/lib-es/network/gateway.js +33 -15
  72. package/lib-es/network/gateway.js.map +1 -1
  73. package/lib-es/types/bridge.d.ts +6 -16
  74. package/lib-es/types/bridge.d.ts.map +1 -1
  75. package/lib-es/types/onboard.d.ts +5 -5
  76. package/lib-es/types/onboard.d.ts.map +1 -1
  77. package/lib-es/types/onboard.js +9 -9
  78. package/lib-es/types/onboard.js.map +1 -1
  79. package/package.json +5 -4
  80. package/src/api/lastBlock.integ.test.ts +0 -18
  81. package/src/bridge/index.ts +3 -6
  82. package/src/bridge/onboard.integ.test.ts +44 -31
  83. package/src/bridge/onboard.ts +61 -209
  84. package/src/bridge/signOperation.ts +5 -6
  85. package/src/bridge/sync.integ.test.ts +30 -10
  86. package/src/bridge/sync.ts +87 -72
  87. package/src/common-logic/utils.test.ts +21 -6
  88. package/src/common-logic/utils.ts +2 -2
  89. package/src/config.ts +1 -1
  90. package/src/network/gateway.integ.test.ts +48 -21
  91. package/src/network/gateway.ts +49 -34
  92. package/src/types/bridge.ts +8 -19
  93. package/src/types/onboard.ts +5 -5
  94. package/lib/bridge/serialization.d.ts +0 -4
  95. package/lib/bridge/serialization.d.ts.map +0 -1
  96. package/lib/bridge/serialization.js +0 -31
  97. package/lib/bridge/serialization.js.map +0 -1
  98. package/lib-es/bridge/serialization.d.ts +0 -4
  99. package/lib-es/bridge/serialization.d.ts.map +0 -1
  100. package/lib-es/bridge/serialization.js +0 -27
  101. package/lib-es/bridge/serialization.js.map +0 -1
  102. package/src/bridge/serialization.ts +0 -36
@@ -1,11 +1,14 @@
1
1
  import BigNumber from "bignumber.js";
2
2
  import { Operation, OperationType } from "@ledgerhq/types-live";
3
- import { decodeAccountId, encodeAccountId } from "@ledgerhq/coin-framework/account/index";
3
+ import { encodeAccountId } from "@ledgerhq/coin-framework/account/index";
4
4
  import { GetAccountShape, mergeOps } from "@ledgerhq/coin-framework/bridge/jsHelpers";
5
5
  import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
6
+ import { SignerContext } from "@ledgerhq/coin-framework/signer";
6
7
  import { getBalance, getLedgerEnd, getOperations, type OperationInfo } from "../network/gateway";
7
- import { CantonAccount } from "../types";
8
8
  import coinConfig from "../config";
9
+ import resolver from "../signer";
10
+ import { CantonAccount, CantonSigner } from "../types";
11
+ import { isAccountOnboarded, isAccountAuthorized } from "./onboard";
9
12
 
10
13
  const txInfoToOperationAdapter =
11
14
  (accountId: string, partyId: string) =>
@@ -30,7 +33,6 @@ const txInfoToOperationAdapter =
30
33
  } else if (txInfo.type === "Initialize") {
31
34
  type = "PRE_APPROVAL";
32
35
  }
33
-
34
36
  const value = new BigNumber(transferValue);
35
37
  const feeValue = new BigNumber(fee);
36
38
  const memo = details.metadata.reason;
@@ -65,76 +67,89 @@ const filterOperations = (
65
67
  return transactions.map(txInfoToOperationAdapter(accountId, partyId));
66
68
  };
67
69
 
68
- export const getAccountShape: GetAccountShape<CantonAccount> = async info => {
69
- const { address, initialAccount, currency, derivationMode, derivationPath, rest } = info;
70
-
71
- const xpubOrAddress = (
72
- (initialAccount?.id && decodeAccountId(initialAccount.id).xpubOrAddress) ||
73
- ""
74
- ).replace(/:/g, "_");
75
- const partyId =
76
- rest?.cantonResources?.partyId ||
77
- initialAccount?.cantonResources?.partyId ||
78
- xpubOrAddress.replace(/_/g, ":");
79
-
80
- const accountId = encodeAccountId({
81
- type: "js",
82
- version: "2",
83
- currencyId: currency.id,
84
- xpubOrAddress,
85
- derivationMode,
86
- });
87
-
88
- // Account info retrieval + spendable balance calculation
89
- // const accountInfo = await getAccountInfo(address);
90
- const balances = await getBalance(currency, partyId);
91
-
92
- const balanceData = balances.find(
93
- balance => balance.instrument_id === coinConfig.getCoinConfig(currency).nativeInstrumentId,
94
- ) || {
95
- instrument_id: coinConfig.getCoinConfig(currency).nativeInstrumentId,
96
- amount: 0,
97
- locked: false,
98
- };
70
+ export function makeGetAccountShape(
71
+ signerContext: SignerContext<CantonSigner>,
72
+ ): GetAccountShape<CantonAccount> {
73
+ return async info => {
74
+ const { address, currency, derivationMode, derivationPath, initialAccount } = info;
75
+
76
+ let xpubOrAddress = initialAccount?.xpub || "";
99
77
 
100
- const balance = new BigNumber(balanceData.amount);
101
- const reserveMin = coinConfig.getCoinConfig(currency).minReserve || 0;
102
- const lockedAmount = balanceData.locked ? balance : new BigNumber(0);
103
- const spendableBalance = BigNumber.max(
104
- 0,
105
- balance.minus(lockedAmount).minus(BigNumber(reserveMin)),
106
- );
107
-
108
- let operations: Operation[] = [];
109
- // Tx history fetching if xpubOrAddress is not empty
110
- if (xpubOrAddress) {
111
- const oldOperations = initialAccount?.operations || [];
112
- const startAt = oldOperations.length ? (oldOperations[0].blockHeight || 0) + 1 : 0;
113
- const transactionData = await getOperations(currency, partyId, {
114
- cursor: startAt,
115
- limit: 100,
78
+ if (!xpubOrAddress) {
79
+ const getAddress = resolver(signerContext);
80
+ const { publicKey } = await getAddress(info.deviceId || "", {
81
+ path: derivationPath,
82
+ currency: currency,
83
+ derivationMode: derivationMode,
84
+ verify: false,
85
+ });
86
+
87
+ const { isOnboarded, partyId } = await isAccountOnboarded(currency, publicKey);
88
+ if (isOnboarded && partyId) {
89
+ xpubOrAddress = partyId;
90
+ }
91
+ }
92
+
93
+ const accountId = encodeAccountId({
94
+ type: "js",
95
+ version: "2",
96
+ currencyId: currency.id,
97
+ xpubOrAddress: xpubOrAddress,
98
+ derivationMode,
116
99
  });
117
100
 
118
- const newOperations = filterOperations(transactionData.operations, accountId, partyId);
119
- operations = mergeOps(oldOperations, newOperations);
120
- }
121
- // blockheight retrieval
122
- const blockHeight = await getLedgerEnd(currency);
123
- // We return the new account shape
124
- const shape = {
125
- id: accountId,
126
- xpub: xpubOrAddress,
127
- blockHeight,
128
- balance,
129
- spendableBalance,
130
- operations,
131
- operationsCount: operations.length,
132
- freshAddress: address,
133
- freshAddressPath: derivationPath,
134
- cantonResources: {
135
- partyId,
136
- },
137
- };
101
+ const { nativeInstrumentId } = coinConfig.getCoinConfig(currency);
102
+ const balances = xpubOrAddress ? await getBalance(currency, xpubOrAddress) : [];
103
+ const balanceData = balances.find(balance =>
104
+ balance.instrument_id.includes(nativeInstrumentId),
105
+ ) || {
106
+ instrument_id: nativeInstrumentId,
107
+ amount: 0,
108
+ locked: false,
109
+ };
110
+ const balance = new BigNumber(balanceData.amount);
111
+ const reserveMin = new BigNumber(coinConfig.getCoinConfig(currency).minReserve || 0);
112
+ const lockedAmount = balanceData.locked ? balance : new BigNumber(0);
113
+ const spendableBalance = BigNumber.max(0, balance.minus(lockedAmount).minus(reserveMin));
138
114
 
139
- return shape;
140
- };
115
+ let operations: Operation[] = [];
116
+ if (xpubOrAddress) {
117
+ const oldOperations = initialAccount?.operations || [];
118
+ const startAt = oldOperations.length ? (oldOperations[0].blockHeight || 0) + 1 : 0;
119
+ const transactionData = await getOperations(currency, xpubOrAddress, {
120
+ cursor: startAt,
121
+ limit: 100,
122
+ });
123
+ const newOperations = filterOperations(transactionData.operations, accountId, xpubOrAddress);
124
+ operations = mergeOps(oldOperations, newOperations);
125
+ }
126
+
127
+ const isAuthorized = await isAccountAuthorized(operations, xpubOrAddress);
128
+ const used = isAuthorized && balance.gt(0);
129
+
130
+ const blockHeight = await getLedgerEnd(currency);
131
+
132
+ const creationDate =
133
+ operations.length > 0
134
+ ? new Date(Math.min(...operations.map(op => op.date.getTime())))
135
+ : new Date();
136
+
137
+ const shape = {
138
+ id: accountId,
139
+ type: "Account" as const,
140
+ balance,
141
+ blockHeight,
142
+ creationDate,
143
+ lastSyncDate: new Date(),
144
+ freshAddress: address,
145
+ seedIdentifier: address,
146
+ operations,
147
+ operationsCount: operations.length,
148
+ spendableBalance,
149
+ xpub: xpubOrAddress,
150
+ used,
151
+ };
152
+
153
+ return shape;
154
+ };
155
+ }
@@ -13,6 +13,10 @@ describe("utils", () => {
13
13
  "user123::42",
14
14
  "canton_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z::123",
15
15
  "test::123456789",
16
+ "abc::abc", // letters after ::
17
+ "test::ABC123", // mixed case letters and numbers
18
+ "user::a1b2c3", // alphanumeric after ::
19
+ "contract::XyZ789", // mixed case with numbers
16
20
  ];
17
21
 
18
22
  validAddresses.forEach(address => {
@@ -24,20 +28,23 @@ describe("utils", () => {
24
28
  const invalidAddresses = [
25
29
  "", // empty string
26
30
  "::123", // no characters before ::
27
- "abc::", // no numbers after ::
31
+ "abc::", // no characters after ::
28
32
  "abc:123", // single colon instead of double
29
- "abc::abc", // letters instead of numbers after ::
30
33
  "abc::", // empty after ::
31
34
  "::", // only colons
32
35
  "abc", // no colons
33
36
  "123", // no colons
34
- "abc::123abc", // mixed letters and numbers after ::
35
37
  "abc::123.456", // decimal numbers
36
38
  "abc::-123", // negative numbers
37
39
  "abc::+123", // positive sign
38
- "abc:: 123", // space before number
39
- "abc::123 ", // space after number
40
+ "abc:: 123", // space before alphanumeric
41
+ "abc::123 ", // space after alphanumeric
40
42
  "abc:: 123", // space after ::
43
+ "abc::123-abc", // dash in alphanumeric part
44
+ "abc::123_abc", // underscore in alphanumeric part
45
+ "abc::123.abc", // dot in alphanumeric part
46
+ "abc::123 abc", // space in alphanumeric part
47
+ "abc::", // empty after ::
41
48
  ];
42
49
 
43
50
  invalidAddresses.forEach(address => {
@@ -46,18 +53,26 @@ describe("utils", () => {
46
53
  });
47
54
 
48
55
  it("should handle edge cases", () => {
49
- expect(isRecipientValid("a::1")).toBe(true); // minimum valid case
56
+ expect(isRecipientValid("a::1")).toBe(true); // minimum valid case with number
57
+ expect(isRecipientValid("a::a")).toBe(true); // minimum valid case with letter
50
58
  expect(isRecipientValid("1::1")).toBe(true); // number before ::
59
+ expect(isRecipientValid("1::a")).toBe(true); // number before ::, letter after
51
60
  expect(isRecipientValid("_::1")).toBe(true); // underscore before ::
61
+ expect(isRecipientValid("_::a")).toBe(true); // underscore before ::, letter after
52
62
  expect(isRecipientValid("-::1")).toBe(true); // dash before ::
63
+ expect(isRecipientValid("-::a")).toBe(true); // dash before ::, letter after
53
64
  expect(isRecipientValid(".::1")).toBe(true); // dot before ::
65
+ expect(isRecipientValid(".::a")).toBe(true); // dot before ::, letter after
54
66
  });
55
67
 
56
68
  it("should handle addresses with spaces and multiple colons", () => {
57
69
  // These are valid according to our regex but might not be ideal Canton addresses
58
70
  expect(isRecipientValid(" abc::123")).toBe(true); // space before address
71
+ expect(isRecipientValid(" abc::abc")).toBe(true); // space before address with letters
59
72
  expect(isRecipientValid("abc ::123")).toBe(true); // space before ::
73
+ expect(isRecipientValid("abc ::abc")).toBe(true); // space before :: with letters
60
74
  expect(isRecipientValid("abc::123::456")).toBe(true); // multiple ::
75
+ expect(isRecipientValid("abc::abc::def")).toBe(true); // multiple :: with letters
61
76
  });
62
77
  });
63
78
 
@@ -8,9 +8,9 @@ export const validateTag = (tag: BigNumber) => {
8
8
  );
9
9
  };
10
10
 
11
- const CANTON_ADDRESS_REGEX = /^.+::\d+$/;
11
+ const CANTON_ADDRESS_REGEX = /^.+::[a-zA-Z0-9]+$/;
12
12
 
13
13
  export function isRecipientValid(recipient: string): boolean {
14
- // Canton address format: at least 1 character :: at least 1 number
14
+ // Canton address format: at least 1 character :: at least 1 alphanumeric character
15
15
  return CANTON_ADDRESS_REGEX.test(recipient);
16
16
  }
package/src/config.ts CHANGED
@@ -7,7 +7,7 @@ export type CantonConfig = {
7
7
  gatewayUrl?: string;
8
8
  // TODELETE
9
9
  minReserve?: number;
10
- networkType: "mainnet" | "devnet" | "localnet";
10
+ networkType: "mainnet" | "devnet" | "testnet" | "localnet";
11
11
  useGateway?: boolean;
12
12
  nativeInstrumentId: string;
13
13
  fee?: number;
@@ -12,6 +12,7 @@ import {
12
12
  submitTapRequest,
13
13
  preparePreApprovalTransaction,
14
14
  submitPreApprovalTransaction,
15
+ type OnboardingPrepareResponse,
15
16
  } from "./gateway";
16
17
  import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
17
18
 
@@ -25,6 +26,8 @@ describe("gateway (devnet)", () => {
25
26
  partyId: string;
26
27
  } | null = null;
27
28
 
29
+ let prepareResponse: OnboardingPrepareResponse | null = null;
30
+
28
31
  beforeAll(async () => {
29
32
  coinConfig.setCoinConfig(() => ({
30
33
  gatewayUrl: "https://canton-gateway.api.live.ledger-test.com",
@@ -35,7 +38,7 @@ describe("gateway (devnet)", () => {
35
38
  type: "active",
36
39
  },
37
40
  }));
38
- }, 60000);
41
+ });
39
42
 
40
43
  const getOnboardedAccount = () => {
41
44
  if (!onboardedAccount) {
@@ -56,7 +59,7 @@ describe("gateway (devnet)", () => {
56
59
  };
57
60
 
58
61
  // WHEN
59
- const response = await prepareOnboarding(mockCurrency, keyPair.publicKeyHex, "ed25519");
62
+ const response = await prepareOnboarding(mockCurrency, keyPair.publicKeyHex);
60
63
 
61
64
  // THEN
62
65
  expect(response).toHaveProperty("party_id");
@@ -68,30 +71,26 @@ describe("gateway (devnet)", () => {
68
71
  expect(typeof response.party_name).toBe("string");
69
72
 
70
73
  expect(response.public_key_fingerprint).toBe(keyPair.fingerprint);
71
- }, 30000);
74
+ });
72
75
  });
73
76
 
74
77
  describe("submitOnboarding", () => {
75
78
  it("should submit onboarding with proper signature", async () => {
76
79
  // GIVEN
77
80
  const { keyPair } = getOnboardedAccount();
78
- const prepareRequest = { public_key: keyPair.publicKeyHex, public_key_type: "ed25519" };
79
- const prepareResponse = await prepareOnboarding(
80
- mockCurrency,
81
- keyPair.publicKeyHex,
82
- "ed25519",
83
- );
81
+ // Save prepare response for next test
82
+ prepareResponse = await prepareOnboarding(mockCurrency, keyPair.publicKeyHex);
84
83
  const signature = keyPair.sign(prepareResponse.transactions.combined_hash);
85
84
 
86
85
  // WHEN
87
86
  const response = await submitOnboarding(
88
87
  mockCurrency,
89
- prepareRequest,
88
+ keyPair.publicKeyHex,
90
89
  prepareResponse,
91
90
  signature,
92
91
  );
93
92
 
94
- // Save onboarded account for all tests that need a valid party ID
93
+ // Save onboarded account for next tests that need a valid party ID
95
94
  onboardedAccount = {
96
95
  keyPair,
97
96
  partyId: response.party.party_id,
@@ -103,6 +102,31 @@ describe("gateway (devnet)", () => {
103
102
  expect(response.party).toHaveProperty("public_key");
104
103
  expect(response.party.public_key).toBe(keyPair.publicKeyHex);
105
104
  }, 30000);
105
+
106
+ const testIfPrepared = prepareResponse ? it.skip : it;
107
+ testIfPrepared(
108
+ "should not throw when already onboarded",
109
+ async () => {
110
+ // GIVEN
111
+ const { keyPair } = getOnboardedAccount();
112
+ const signature = keyPair.sign(prepareResponse!.transactions.combined_hash);
113
+
114
+ // WHEN
115
+ const response = await submitOnboarding(
116
+ mockCurrency,
117
+ keyPair.publicKeyHex,
118
+ prepareResponse!,
119
+ signature,
120
+ );
121
+
122
+ // THEN
123
+ expect(response).toHaveProperty("party");
124
+ expect(response.party).toHaveProperty("party_id");
125
+ expect(response.party).toHaveProperty("public_key");
126
+ expect(response.party.public_key).toBe(keyPair.publicKeyHex);
127
+ },
128
+ 30000,
129
+ );
106
130
  });
107
131
 
108
132
  describe("getLedgerEnd", () => {
@@ -125,17 +149,20 @@ describe("gateway (devnet)", () => {
125
149
  });
126
150
 
127
151
  describe("getPartyById", () => {
128
- it.skip("should return party info", async () => {
129
- const party = await getPartyById(mockCurrency, "4f2e1485107adf5f");
152
+ it("should return party info", async () => {
153
+ const party = await getPartyById(
154
+ mockCurrency,
155
+ "ldg::12208b12fa34be8a079bcbb68bba828e58313046c4208855b39885fab48661322e68",
156
+ );
130
157
  expect(party).toBeDefined();
131
158
  });
132
159
  });
133
160
 
134
161
  describe("getPartyByPubKey", () => {
135
- it.skip("should return party info", async () => {
162
+ it("should return party info", async () => {
136
163
  const party = await getPartyByPubKey(
137
164
  mockCurrency,
138
- "122027c6dbbbdbffe0fa3122ae05175f3b9328e879e9ce96b670354deb64a45683c1",
165
+ "c5cdb19624833f9a929a0125978c886ec4297320c14cea6bf667dc1d23a8e650",
139
166
  );
140
167
  expect(party).toBeDefined();
141
168
  });
@@ -153,7 +180,7 @@ describe("gateway (devnet)", () => {
153
180
  });
154
181
 
155
182
  describe("prepareTapRequest", () => {
156
- it("should prepare tap request for onboarded party", async () => {
183
+ it.skip("should prepare tap request for onboarded party", async () => {
157
184
  // GIVEN
158
185
  const { partyId } = getOnboardedAccount();
159
186
  const amount = 1000;
@@ -166,11 +193,11 @@ describe("gateway (devnet)", () => {
166
193
  expect(response).toHaveProperty("hash");
167
194
  expect(typeof response.serialized).toBe("string");
168
195
  expect(typeof response.hash).toBe("string");
169
- }, 30000);
196
+ });
170
197
  });
171
198
 
172
199
  describe("submitTapRequest", () => {
173
- it("should submit tap request with proper signature", async () => {
200
+ it.skip("should submit tap request with proper signature", async () => {
174
201
  // GIVEN
175
202
  const { keyPair, partyId } = getOnboardedAccount();
176
203
  const tapPrepareResponse = await prepareTapRequest(mockCurrency, {
@@ -191,7 +218,7 @@ describe("gateway (devnet)", () => {
191
218
  expect(response).toHaveProperty("update_id");
192
219
  expect(typeof response.submission_id).toBe("string");
193
220
  expect(typeof response.update_id).toBe("string");
194
- }, 30000);
221
+ });
195
222
  });
196
223
 
197
224
  describe("preparePreApprovalTransaction", () => {
@@ -207,7 +234,7 @@ describe("gateway (devnet)", () => {
207
234
  expect(response).toHaveProperty("hash");
208
235
  expect(typeof response.serialized).toBe("string");
209
236
  expect(typeof response.hash).toBe("string");
210
- }, 30000);
237
+ });
211
238
  });
212
239
 
213
240
  describe("submitPreApprovalTransaction", () => {
@@ -232,6 +259,6 @@ describe("gateway (devnet)", () => {
232
259
  expect(response.isApproved).toBe(true);
233
260
  expect(typeof response.submissionId).toBe("string");
234
261
  expect(typeof response.updateId).toBe("string");
235
- }, 30000);
262
+ });
236
263
  });
237
264
  });
@@ -11,7 +11,7 @@ import {
11
11
  } from "../types/onboard";
12
12
  import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
13
13
 
14
- type OnboardingPrepareResponse = {
14
+ export type OnboardingPrepareResponse = {
15
15
  party_id: string;
16
16
  party_name: string;
17
17
  public_key_fingerprint: string;
@@ -274,7 +274,7 @@ export type OperationInfo =
274
274
 
275
275
  const getGatewayUrl = (currency: CryptoCurrency) => coinConfig.getCoinConfig(currency).gatewayUrl;
276
276
  const getNodeId = (currency: CryptoCurrency) =>
277
- coinConfig.getCoinConfig(currency).nodeId || "ledger-devnet-stg";
277
+ coinConfig.getCoinConfig(currency).nodeId || "ledger-live-devnet";
278
278
  const getNetworkType = (currency: CryptoCurrency) => coinConfig.getCoinConfig(currency).networkType;
279
279
 
280
280
  const gatewayNetwork = <T, U = unknown>(req: LiveNetworkRequest<U>) => {
@@ -288,11 +288,7 @@ const gatewayNetwork = <T, U = unknown>(req: LiveNetworkRequest<U>) => {
288
288
  });
289
289
  };
290
290
 
291
- export async function prepareOnboarding(
292
- currency: CryptoCurrency,
293
- pubKey: string,
294
- pubKeyType: string,
295
- ): Promise<OnboardingPrepareResponse> {
291
+ export async function prepareOnboarding(currency: CryptoCurrency, pubKey: string) {
296
292
  const gatewayUrl = getGatewayUrl(currency);
297
293
  const nodeId = getNodeId(currency);
298
294
  const fullUrl = `${gatewayUrl}/v1/node/${nodeId}/onboarding/prepare`;
@@ -302,29 +298,54 @@ export async function prepareOnboarding(
302
298
  url: fullUrl,
303
299
  data: {
304
300
  public_key: pubKey,
305
- public_key_type: pubKeyType,
301
+ public_key_type: "ed25519",
306
302
  },
307
303
  });
308
304
 
309
305
  return data;
310
306
  }
311
307
 
308
+ type OnboardingSubmitError409 = {
309
+ partyId: string;
310
+ status: 409;
311
+ type: "PARTY_ALREADY_EXISTS";
312
+ message: string;
313
+ };
314
+
312
315
  export async function submitOnboarding(
313
316
  currency: CryptoCurrency,
314
- prepareRequest: OnboardingPrepareRequest,
317
+ publicKey: string,
315
318
  prepareResponse: OnboardingPrepareResponse,
316
319
  signature: string,
317
320
  ) {
318
- const { data } = await gatewayNetwork<OnboardingSubmitResponse, OnboardingSubmitRequest>({
319
- method: "POST",
320
- url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/onboarding/submit`,
321
- data: {
322
- prepare_request: prepareRequest,
323
- prepare_response: prepareResponse,
324
- signature,
325
- },
326
- });
327
- return data;
321
+ try {
322
+ const { data } = await gatewayNetwork<OnboardingSubmitResponse, OnboardingSubmitRequest>({
323
+ method: "POST",
324
+ url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/onboarding/submit`,
325
+ data: {
326
+ prepare_request: {
327
+ public_key: publicKey,
328
+ public_key_type: "ed25519",
329
+ },
330
+ prepare_response: prepareResponse,
331
+ signature,
332
+ },
333
+ });
334
+ return data;
335
+ } catch (e) {
336
+ if (e instanceof Error && "type" in e && e.type === "PARTY_ALREADY_EXISTS") {
337
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
338
+ const { partyId } = e as unknown as OnboardingSubmitError409;
339
+ return {
340
+ party: {
341
+ party_id: partyId,
342
+ public_key: publicKey,
343
+ },
344
+ };
345
+ }
346
+
347
+ throw e;
348
+ }
328
349
  }
329
350
 
330
351
  export async function submit(
@@ -344,10 +365,7 @@ export async function submit(
344
365
  return data;
345
366
  }
346
367
 
347
- export async function getBalance(
348
- currency: CryptoCurrency,
349
- partyId: string,
350
- ): Promise<InstrumentBalance[]> {
368
+ export async function getBalance(currency: CryptoCurrency, partyId: string) {
351
369
  const { data } = await gatewayNetwork<InstrumentBalance[]>({
352
370
  method: "GET",
353
371
  url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/balance`,
@@ -421,7 +439,7 @@ enum TransactionType {
421
439
  export async function prepareTapRequest(
422
440
  currency: CryptoCurrency,
423
441
  { partyId, amount = 1000000 }: PrepareTapRequest,
424
- ): Promise<PrepareTapResponse> {
442
+ ) {
425
443
  if (getNetworkType(currency) === "mainnet") {
426
444
  return {
427
445
  serialized: "",
@@ -429,11 +447,11 @@ export async function prepareTapRequest(
429
447
  hash: "",
430
448
  };
431
449
  }
432
- const { data } = await gatewayNetwork<PrepareTapResponse, { amount: number; type: string }>({
450
+ const { data } = await gatewayNetwork<PrepareTapResponse, { amount: string; type: string }>({
433
451
  method: "POST",
434
452
  url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`,
435
453
  data: {
436
- amount: parseInt(amount.toString(), 10), // Convert to integer to avoid scientific notation
454
+ amount: amount.toString(),
437
455
  type: TransactionType.TAP_REQUEST,
438
456
  },
439
457
  });
@@ -454,7 +472,7 @@ type SubmitTapRequestResponse = {
454
472
  export async function submitTapRequest(
455
473
  currency: CryptoCurrency,
456
474
  { partyId, serialized, signature }: SubmitTapRequestRequest,
457
- ): Promise<SubmitTapRequestResponse> {
475
+ ) {
458
476
  const { data } = await gatewayNetwork<
459
477
  SubmitTapRequestResponse,
460
478
  Omit<SubmitTapRequestRequest, "partyId">
@@ -473,7 +491,7 @@ export async function prepareTransferRequest(
473
491
  currency: CryptoCurrency,
474
492
  partyId: string,
475
493
  params: PrepareTransferRequest,
476
- ): Promise<PrepareTransferResponse> {
494
+ ) {
477
495
  const { data } = await gatewayNetwork<PrepareTransferResponse, PrepareTransferRequest>({
478
496
  method: "POST",
479
497
  url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`,
@@ -490,10 +508,7 @@ export async function getLedgerEnd(currency: CryptoCurrency): Promise<number> {
490
508
  return data;
491
509
  }
492
510
 
493
- export async function preparePreApprovalTransaction(
494
- currency: CryptoCurrency,
495
- partyId: string,
496
- ): Promise<PrepareTransactionResponse> {
511
+ export async function preparePreApprovalTransaction(currency: CryptoCurrency, partyId: string) {
497
512
  const { data } = await gatewayNetwork<PrepareTransactionResponse, PrepareTransactionRequest>({
498
513
  method: "POST",
499
514
  url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`,
@@ -510,7 +525,7 @@ export async function submitPreApprovalTransaction(
510
525
  partyId: string,
511
526
  { serialized }: PrepareTransactionResponse,
512
527
  signature: string,
513
- ): Promise<PreApprovalResult> {
528
+ ) {
514
529
  const { data } = await gatewayNetwork<SubmitTransactionResponse, SubmitTransactionRequest>({
515
530
  method: "POST",
516
531
  url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/submit`,
@@ -524,5 +539,5 @@ export async function submitPreApprovalTransaction(
524
539
  isApproved: true,
525
540
  submissionId: data.submission_id,
526
541
  updateId: data.update_id,
527
- };
542
+ } satisfies PreApprovalResult;
528
543
  }
@@ -9,26 +9,26 @@ import type {
9
9
  TransactionStatusCommon,
10
10
  TransactionStatusCommonRaw,
11
11
  } from "@ledgerhq/types-live";
12
+ import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
12
13
  import type {
13
14
  CantonOnboardProgress,
14
15
  CantonOnboardResult,
15
- CantonPreApprovalProgress,
16
- CantonPreApprovalResult,
16
+ CantonAuthorizeProgress,
17
+ CantonAuthorizeResult,
17
18
  } from "./onboard";
18
- import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
19
19
 
20
20
  export interface CantonCurrencyBridge extends CurrencyBridge {
21
21
  onboardAccount: (
22
22
  currency: CryptoCurrency,
23
23
  deviceId: string,
24
- derivationPath: string,
24
+ creatableAccount: Account,
25
25
  ) => Observable<CantonOnboardProgress | CantonOnboardResult>;
26
26
  authorizePreapproval: (
27
27
  currency: CryptoCurrency,
28
28
  deviceId: string,
29
- derivationPath: string,
29
+ creatableAccount: Account,
30
30
  partyId: string,
31
- ) => Observable<CantonPreApprovalProgress | CantonPreApprovalResult>;
31
+ ) => Observable<CantonAuthorizeProgress | CantonAuthorizeResult>;
32
32
  }
33
33
 
34
34
  export type NetworkInfo = {
@@ -60,16 +60,5 @@ export type TransactionRaw = TransactionCommonRaw & {
60
60
  export type TransactionStatus = TransactionStatusCommon;
61
61
  export type TransactionStatusRaw = TransactionStatusCommonRaw;
62
62
 
63
- export type CantonResources = {
64
- partyId: string;
65
- };
66
- export type CantonResourcesRaw = {
67
- partyId: string;
68
- };
69
-
70
- export type CantonAccount = Account & {
71
- cantonResources?: CantonResources;
72
- };
73
- export type CantonAccountRaw = AccountRaw & {
74
- cantonResources: CantonResourcesRaw;
75
- };
63
+ export type CantonAccount = Account;
64
+ export type CantonAccountRaw = AccountRaw;