@ledgerhq/coin-canton 0.7.0 → 0.8.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 (133) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/lib/api/index.d.ts.map +1 -1
  4. package/lib/api/index.js +3 -0
  5. package/lib/api/index.js.map +1 -1
  6. package/lib/api/lastBlock.integ.test.js +0 -15
  7. package/lib/api/lastBlock.integ.test.js.map +1 -1
  8. package/lib/bridge/getTransactionStatus.test.d.ts +2 -0
  9. package/lib/bridge/getTransactionStatus.test.d.ts.map +1 -0
  10. package/lib/bridge/getTransactionStatus.test.js +365 -0
  11. package/lib/bridge/getTransactionStatus.test.js.map +1 -0
  12. package/lib/bridge/index.d.ts.map +1 -1
  13. package/lib/bridge/index.js +5 -5
  14. package/lib/bridge/index.js.map +1 -1
  15. package/lib/bridge/onboard.d.ts +11 -6
  16. package/lib/bridge/onboard.d.ts.map +1 -1
  17. package/lib/bridge/onboard.integ.test.js +49 -27
  18. package/lib/bridge/onboard.integ.test.js.map +1 -1
  19. package/lib/bridge/onboard.js +45 -152
  20. package/lib/bridge/onboard.js.map +1 -1
  21. package/lib/bridge/signOperation.d.ts.map +1 -1
  22. package/lib/bridge/signOperation.js +5 -5
  23. package/lib/bridge/signOperation.js.map +1 -1
  24. package/lib/bridge/sync.d.ts +3 -2
  25. package/lib/bridge/sync.d.ts.map +1 -1
  26. package/lib/bridge/sync.integ.test.js +39 -17
  27. package/lib/bridge/sync.integ.test.js.map +1 -1
  28. package/lib/bridge/sync.js +71 -57
  29. package/lib/bridge/sync.js.map +1 -1
  30. package/lib/common-logic/utils.d.ts.map +1 -1
  31. package/lib/common-logic/utils.js +3 -1
  32. package/lib/common-logic/utils.js.map +1 -1
  33. package/lib/common-logic/utils.test.d.ts +2 -0
  34. package/lib/common-logic/utils.test.d.ts.map +1 -0
  35. package/lib/common-logic/utils.test.js +104 -0
  36. package/lib/common-logic/utils.test.js.map +1 -0
  37. package/lib/config.d.ts +1 -1
  38. package/lib/config.d.ts.map +1 -1
  39. package/lib/network/gateway.d.ts +14 -10
  40. package/lib/network/gateway.d.ts.map +1 -1
  41. package/lib/network/gateway.integ.test.js +31 -17
  42. package/lib/network/gateway.integ.test.js.map +1 -1
  43. package/lib/network/gateway.js +34 -16
  44. package/lib/network/gateway.js.map +1 -1
  45. package/lib/network/gateway.test.d.ts +2 -0
  46. package/lib/network/gateway.test.d.ts.map +1 -0
  47. package/lib/network/gateway.test.js +59 -0
  48. package/lib/network/gateway.test.js.map +1 -0
  49. package/lib/types/bridge.d.ts +6 -16
  50. package/lib/types/bridge.d.ts.map +1 -1
  51. package/lib/types/onboard.d.ts +5 -5
  52. package/lib/types/onboard.d.ts.map +1 -1
  53. package/lib/types/onboard.js +10 -10
  54. package/lib/types/onboard.js.map +1 -1
  55. package/lib-es/api/index.d.ts.map +1 -1
  56. package/lib-es/api/index.js +3 -0
  57. package/lib-es/api/index.js.map +1 -1
  58. package/lib-es/api/lastBlock.integ.test.js +0 -15
  59. package/lib-es/api/lastBlock.integ.test.js.map +1 -1
  60. package/lib-es/bridge/getTransactionStatus.test.d.ts +2 -0
  61. package/lib-es/bridge/getTransactionStatus.test.d.ts.map +1 -0
  62. package/lib-es/bridge/getTransactionStatus.test.js +360 -0
  63. package/lib-es/bridge/getTransactionStatus.test.js.map +1 -0
  64. package/lib-es/bridge/index.d.ts.map +1 -1
  65. package/lib-es/bridge/index.js +6 -6
  66. package/lib-es/bridge/index.js.map +1 -1
  67. package/lib-es/bridge/onboard.d.ts +11 -6
  68. package/lib-es/bridge/onboard.d.ts.map +1 -1
  69. package/lib-es/bridge/onboard.integ.test.js +37 -15
  70. package/lib-es/bridge/onboard.integ.test.js.map +1 -1
  71. package/lib-es/bridge/onboard.js +44 -152
  72. package/lib-es/bridge/onboard.js.map +1 -1
  73. package/lib-es/bridge/signOperation.d.ts.map +1 -1
  74. package/lib-es/bridge/signOperation.js +5 -5
  75. package/lib-es/bridge/signOperation.js.map +1 -1
  76. package/lib-es/bridge/sync.d.ts +3 -2
  77. package/lib-es/bridge/sync.d.ts.map +1 -1
  78. package/lib-es/bridge/sync.integ.test.js +34 -12
  79. package/lib-es/bridge/sync.integ.test.js.map +1 -1
  80. package/lib-es/bridge/sync.js +71 -56
  81. package/lib-es/bridge/sync.js.map +1 -1
  82. package/lib-es/common-logic/utils.d.ts.map +1 -1
  83. package/lib-es/common-logic/utils.js +3 -1
  84. package/lib-es/common-logic/utils.js.map +1 -1
  85. package/lib-es/common-logic/utils.test.d.ts +2 -0
  86. package/lib-es/common-logic/utils.test.d.ts.map +1 -0
  87. package/lib-es/common-logic/utils.test.js +99 -0
  88. package/lib-es/common-logic/utils.test.js.map +1 -0
  89. package/lib-es/config.d.ts +1 -1
  90. package/lib-es/config.d.ts.map +1 -1
  91. package/lib-es/network/gateway.d.ts +14 -10
  92. package/lib-es/network/gateway.d.ts.map +1 -1
  93. package/lib-es/network/gateway.integ.test.js +31 -17
  94. package/lib-es/network/gateway.integ.test.js.map +1 -1
  95. package/lib-es/network/gateway.js +34 -16
  96. package/lib-es/network/gateway.js.map +1 -1
  97. package/lib-es/network/gateway.test.d.ts +2 -0
  98. package/lib-es/network/gateway.test.d.ts.map +1 -0
  99. package/lib-es/network/gateway.test.js +54 -0
  100. package/lib-es/network/gateway.test.js.map +1 -0
  101. package/lib-es/types/bridge.d.ts +6 -16
  102. package/lib-es/types/bridge.d.ts.map +1 -1
  103. package/lib-es/types/onboard.d.ts +5 -5
  104. package/lib-es/types/onboard.d.ts.map +1 -1
  105. package/lib-es/types/onboard.js +9 -9
  106. package/lib-es/types/onboard.js.map +1 -1
  107. package/package.json +8 -7
  108. package/src/api/index.ts +9 -0
  109. package/src/api/lastBlock.integ.test.ts +0 -18
  110. package/src/bridge/getTransactionStatus.test.ts +446 -0
  111. package/src/bridge/index.ts +6 -6
  112. package/src/bridge/onboard.integ.test.ts +44 -31
  113. package/src/bridge/onboard.ts +61 -209
  114. package/src/bridge/signOperation.ts +5 -6
  115. package/src/bridge/sync.integ.test.ts +38 -13
  116. package/src/bridge/sync.ts +90 -72
  117. package/src/common-logic/utils.test.ts +108 -0
  118. package/src/common-logic/utils.ts +4 -1
  119. package/src/config.ts +1 -1
  120. package/src/network/gateway.integ.test.ts +48 -21
  121. package/src/network/gateway.test.ts +66 -0
  122. package/src/network/gateway.ts +60 -37
  123. package/src/types/bridge.ts +8 -19
  124. package/src/types/onboard.ts +5 -5
  125. package/lib/bridge/serialization.d.ts +0 -4
  126. package/lib/bridge/serialization.d.ts.map +0 -1
  127. package/lib/bridge/serialization.js +0 -31
  128. package/lib/bridge/serialization.js.map +0 -1
  129. package/lib-es/bridge/serialization.d.ts +0 -4
  130. package/lib-es/bridge/serialization.d.ts.map +0 -1
  131. package/lib-es/bridge/serialization.js +0 -27
  132. package/lib-es/bridge/serialization.js.map +0 -1
  133. package/src/bridge/serialization.ts +0 -36
@@ -1,10 +1,8 @@
1
1
  import { Observable } from "rxjs";
2
2
  import { SignerContext } from "@ledgerhq/coin-framework/signer";
3
- import { emptyHistoryCache } from "@ledgerhq/coin-framework/account/index";
4
- import { getDerivationModesForCurrency } from "@ledgerhq/coin-framework/derivation";
5
- import { getAccountShape } from "./sync";
6
- import { CantonAccount, CantonSigner } from "../types";
7
- import type { Account, DerivationMode } from "@ledgerhq/types-live";
3
+ import type { Account, Operation } from "@ledgerhq/types-live";
4
+ import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
5
+ import { log } from "@ledgerhq/logs";
8
6
  import {
9
7
  prepareOnboarding,
10
8
  submitOnboarding,
@@ -16,148 +14,84 @@ import {
16
14
  } from "../network/gateway";
17
15
  import {
18
16
  OnboardStatus,
19
- PreApprovalStatus,
17
+ AuthorizeStatus,
20
18
  CantonOnboardProgress,
21
19
  CantonOnboardResult,
22
- CantonPreApprovalProgress,
23
- CantonPreApprovalResult,
24
- PrepareTransactionResponse,
20
+ CantonAuthorizeProgress,
21
+ CantonAuthorizeResult,
25
22
  } from "../types/onboard";
26
23
  import resolver from "../signer";
27
- import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
24
+ import type { CantonSigner } from "../types";
28
25
 
29
- async function _getKeypair(
30
- signerContext: SignerContext<CantonSigner>,
31
- deviceId: string,
32
- derivationPath: string,
33
- ) {
34
- return signerContext(deviceId, async signer => {
35
- const { publicKey, address } = await signer.getAddress(derivationPath);
36
- return { signer, publicKey: publicKey.replace("0x", ""), address };
37
- });
38
- }
39
-
40
- export const isAccountOnboarded = async (
41
- currency: CryptoCurrency,
42
- publicKey: string,
43
- ): Promise<{ isOnboarded: boolean; party_id?: string }> => {
26
+ export const isAccountOnboarded = async (currency: CryptoCurrency, publicKey: string) => {
44
27
  try {
45
28
  const { party_id } = await getPartyByPubKey(currency, publicKey);
46
29
 
47
30
  if (party_id) {
48
- return { isOnboarded: true, party_id };
31
+ return { isOnboarded: true, partyId: party_id };
49
32
  } else {
50
33
  return { isOnboarded: false };
51
34
  }
52
35
  } catch (err) {
53
- log("[isAccountOnboarded] Error checking party status (likely not onboarded):", err);
54
36
  return { isOnboarded: false };
55
37
  }
56
38
  };
57
39
 
40
+ export const isAccountAuthorized = async (operations: Operation[], partyId: string) => {
41
+ // temporary solution to check if the account is authorized
42
+ return operations.some(operation => operation.senders.includes(partyId));
43
+ };
44
+
58
45
  export const buildOnboardAccount =
59
46
  (signerContext: SignerContext<CantonSigner>) =>
60
47
  (
61
48
  currency: CryptoCurrency,
62
49
  deviceId: string,
63
- derivationPath: string,
50
+ account: Account,
64
51
  ): Observable<CantonOnboardProgress | CantonOnboardResult> =>
65
- new Observable(observer => {
52
+ new Observable(o => {
66
53
  async function main() {
67
- observer.next({
68
- status: OnboardStatus.INIT,
69
- });
70
- const derivationMode = getDerivationModesForCurrency(currency)[0];
54
+ o.next({ status: OnboardStatus.INIT });
55
+
71
56
  const getAddress = resolver(signerContext);
72
- const { address, publicKey } = await getAddress(deviceId, {
73
- path: derivationPath,
57
+ const { publicKey } = await getAddress(deviceId, {
58
+ path: account.freshAddressPath,
74
59
  currency,
75
- derivationMode: derivationMode || "",
60
+ derivationMode: account.derivationMode,
76
61
  });
77
62
 
78
- observer.next({
79
- status: OnboardStatus.PREPARE,
80
- });
63
+ o.next({ status: OnboardStatus.PREPARE });
81
64
 
82
- const { party_id: partyId } = await isAccountOnboarded(currency, publicKey);
65
+ let { partyId } = await isAccountOnboarded(currency, publicKey);
83
66
  if (partyId) {
84
- const account = await createAccount({
85
- address,
86
- derivationPath,
87
- partyId,
88
- currency,
89
- derivationMode,
90
- });
91
- observer.next({
92
- partyId,
93
- account,
94
- });
95
- observer.complete();
67
+ o.next({ partyId, account }); // success
96
68
  return;
97
69
  }
98
70
 
99
- const preparedTransaction = await prepareOnboarding(currency, publicKey, "ed25519");
71
+ const preparedTransaction = await prepareOnboarding(currency, publicKey);
72
+ partyId = preparedTransaction.party_id;
100
73
 
101
- observer.next({
102
- status: OnboardStatus.SIGN,
103
- });
74
+ o.next({ status: OnboardStatus.SIGN });
104
75
 
105
76
  const signature = await signerContext(deviceId, signer =>
106
- signer.signTransaction(derivationPath, preparedTransaction.transactions.combined_hash),
77
+ signer.signTransaction(
78
+ account.freshAddressPath,
79
+ preparedTransaction.transactions.combined_hash,
80
+ ),
107
81
  );
108
82
 
109
- observer.next({
110
- status: OnboardStatus.SUBMIT,
111
- });
83
+ o.next({ status: OnboardStatus.SUBMIT });
112
84
 
113
- const result = await submitOnboarding(
114
- currency,
115
- { public_key: publicKey, public_key_type: "ed25519" },
116
- preparedTransaction,
117
- signature,
118
- ).catch(async err => {
119
- if (err.type === "PARTY_ALREADY_EXISTS") {
120
- const account = await createAccount({
121
- address,
122
- derivationPath,
123
- partyId: preparedTransaction.party_id,
124
- currency,
125
- derivationMode,
126
- });
127
- observer.next({
128
- partyId: preparedTransaction.party_id,
129
- account,
130
- });
131
- return observer.complete();
132
- }
133
- throw err;
134
- });
135
-
136
- if (result) {
137
- observer.next({
138
- status: OnboardStatus.SUCCESS,
139
- });
140
- const account = await createAccount({
141
- address,
142
- derivationPath,
143
- partyId: result.party.party_id,
144
- currency,
145
- derivationMode,
146
- });
147
- observer.next({
148
- partyId: result.party.party_id,
149
- account,
150
- });
151
- }
85
+ await submitOnboarding(currency, publicKey, preparedTransaction, signature);
152
86
 
153
- observer.complete();
87
+ o.next({ partyId, account }); // success
154
88
  }
155
89
 
156
90
  main().then(
157
- () => observer.complete(),
91
+ () => o.complete(),
158
92
  error => {
159
- log("[onboardAccount] Error:", error);
160
- observer.error(error);
93
+ log("[canton:onboard] onboardAccount failed:", error);
94
+ o.error(error);
161
95
  },
162
96
  );
163
97
  });
@@ -167,146 +101,64 @@ export const buildAuthorizePreapproval =
167
101
  (
168
102
  currency: CryptoCurrency,
169
103
  deviceId: string,
170
- derivationPath: string,
104
+ account: Account,
171
105
  partyId: string,
172
- ): Observable<CantonPreApprovalProgress | CantonPreApprovalResult> =>
173
- new Observable(observer => {
106
+ ): Observable<CantonAuthorizeProgress | CantonAuthorizeResult> =>
107
+ new Observable(o => {
174
108
  async function main() {
175
- observer.next({
176
- status: PreApprovalStatus.PREPARE,
177
- });
109
+ o.next({ status: AuthorizeStatus.INIT });
178
110
 
179
- const preparedTransaction: PrepareTransactionResponse = await preparePreApprovalTransaction(
180
- currency,
181
- partyId,
182
- );
111
+ const isAuthorized = await isAccountAuthorized(account.operations, partyId);
183
112
 
184
- observer.next({
185
- status: PreApprovalStatus.SIGN,
186
- });
113
+ if (!isAuthorized) {
114
+ o.next({ status: AuthorizeStatus.PREPARE });
187
115
 
188
- const signature = await signerContext(deviceId, signer =>
189
- signer.signTransaction(derivationPath, preparedTransaction.hash),
190
- );
116
+ const preparedTransaction = await preparePreApprovalTransaction(currency, partyId);
191
117
 
192
- observer.next({
193
- status: PreApprovalStatus.SUBMIT,
194
- });
118
+ o.next({ status: AuthorizeStatus.SIGN });
195
119
 
196
- const { isApproved } = await submitPreApprovalTransaction(
197
- currency,
198
- partyId,
199
- preparedTransaction,
200
- signature,
201
- );
120
+ const signature = await signerContext(deviceId, signer =>
121
+ signer.signTransaction(account.freshAddressPath, preparedTransaction.hash),
122
+ );
202
123
 
203
- observer.next({
204
- status: PreApprovalStatus.SUCCESS,
205
- });
124
+ o.next({ status: AuthorizeStatus.SUBMIT });
206
125
 
207
- observer.next({
208
- isApproved,
209
- });
126
+ await submitPreApprovalTransaction(currency, partyId, preparedTransaction, signature);
127
+ }
128
+
129
+ o.next({ isApproved: true }); // success
210
130
 
211
131
  const handleTapRequest = async () => {
212
132
  try {
213
- const { serialized, hash } = await prepareTapRequest(currency, {
214
- partyId,
215
- });
133
+ const { serialized, hash } = await prepareTapRequest(currency, { partyId });
216
134
 
217
135
  if (serialized && hash) {
218
- observer.next({
219
- status: PreApprovalStatus.SIGN,
220
- });
136
+ o.next({ status: AuthorizeStatus.SIGN });
221
137
 
222
138
  const signature = await signerContext(deviceId, signer =>
223
- signer.signTransaction(derivationPath, hash),
139
+ signer.signTransaction(account.freshAddressPath, hash),
224
140
  );
225
141
 
226
- observer.next({
227
- status: PreApprovalStatus.SUBMIT,
228
- });
142
+ o.next({ status: AuthorizeStatus.SUBMIT });
229
143
 
230
144
  await submitTapRequest(currency, {
231
145
  partyId,
232
146
  serialized,
233
147
  signature,
234
148
  });
235
-
236
- observer.next({
237
- status: PreApprovalStatus.SUCCESS,
238
- });
239
149
  }
240
150
  } catch (err) {
241
151
  // Tap request failure should not break the pre-approval flow
242
152
  }
243
153
  };
244
154
  await handleTapRequest();
245
-
246
- observer.complete();
247
155
  }
248
156
 
249
157
  main().then(
250
- () => observer.complete(),
158
+ () => o.complete(),
251
159
  error => {
252
- log("[buildAuthorizePreapproval] Error:", error);
253
- observer.error(error);
160
+ log("[canton:onboard] authorizePreapproval failed:", error);
161
+ o.error(error);
254
162
  },
255
163
  );
256
164
  });
257
-
258
- const createAccount = async ({
259
- address,
260
- partyId,
261
- derivationPath,
262
- currency,
263
- derivationMode,
264
- index = 0,
265
- }: {
266
- address: string;
267
- derivationPath: string;
268
- partyId: string;
269
- currency: CryptoCurrency;
270
- derivationMode: DerivationMode;
271
- index?: number;
272
- }): Promise<Partial<Account>> => {
273
- const accountShape = await getAccountShape(
274
- {
275
- address,
276
- currency,
277
- derivationMode,
278
- derivationPath,
279
- index,
280
- rest: {
281
- cantonResources: {
282
- partyId,
283
- },
284
- },
285
- },
286
- { paginationConfig: {} },
287
- );
288
-
289
- const account: Partial<CantonAccount> = {
290
- ...accountShape,
291
- type: "Account",
292
- xpub: partyId.replace(/:/g, "_"),
293
- index,
294
- // operations: [],
295
- currency,
296
- derivationMode,
297
- lastSyncDate: new Date(),
298
- pendingOperations: [],
299
- seedIdentifier: address,
300
- balanceHistoryCache: emptyHistoryCache,
301
- cantonResources: {
302
- partyId,
303
- },
304
- };
305
-
306
- return account;
307
- };
308
-
309
- const log = (message: string, ...rest: unknown[]) => {
310
- // eslint-disable-next-line no-console
311
- console.log(message, ...rest);
312
- };
@@ -4,6 +4,7 @@ import { FeeNotLoaded } from "@ledgerhq/errors";
4
4
  import { AccountBridge, Operation } from "@ledgerhq/types-live";
5
5
  import { SignerContext } from "@ledgerhq/coin-framework/signer";
6
6
  import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
7
+ import { decodeAccountId } from "@ledgerhq/coin-framework/account";
7
8
  import { combine, craftTransaction } from "../common-logic";
8
9
  import { Transaction, CantonSigner } from "../types";
9
10
 
@@ -22,10 +23,8 @@ export const buildSignOperation =
22
23
  });
23
24
 
24
25
  const signature = await signerContext(deviceId, async signer => {
25
- const { freshAddressPath: derivationPath } = account;
26
- const partyId = (account as unknown as { cantonResources: { partyId: string } })
27
- .cantonResources.partyId;
28
-
26
+ const { id, freshAddressPath: derivationPath, xpub } = account;
27
+ const address = xpub ?? decodeAccountId(id).xpubOrAddress;
29
28
  const params: {
30
29
  recipient?: string;
31
30
  amount: BigNumber;
@@ -45,13 +44,13 @@ export const buildSignOperation =
45
44
  const { hash, serializedTransaction } = await craftTransaction(
46
45
  account.currency,
47
46
  {
48
- address: partyId,
47
+ address,
49
48
  },
50
49
  params,
51
50
  );
52
51
  const transactionSignature = await signer.signTransaction(derivationPath, hash);
53
52
 
54
- return combine(serializedTransaction, `${transactionSignature}__PARTY__${partyId}`);
53
+ return combine(serializedTransaction, `${transactionSignature}__PARTY__${address}`);
55
54
  });
56
55
 
57
56
  o.next({
@@ -9,7 +9,9 @@ import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
9
9
  import { Account, Operation } from "@ledgerhq/types-live";
10
10
  import coinConfig from "../config";
11
11
  import * as gateway from "../network/gateway";
12
- import { getAccountShape } from "./sync";
12
+ import { generateMockKeyPair, createMockSigner } from "../test/cantonTestUtils";
13
+ import { CantonAccount } from "../types";
14
+ import { makeGetAccountShape } from "./sync";
13
15
 
14
16
  const TEST_ADDRESS =
15
17
  "b6400f93ea1c74aea86be39b0ccc846fc5de01f12b2ad0d7c31848d6fb6eb6d9::1220c81315e2bf2524a9141bcc6cbf19b61c151e0dcaa95343c0ccf53aed7415c4ec";
@@ -22,14 +24,25 @@ const derivationPath = runDerivationScheme(
22
24
  account: 0,
23
25
  },
24
26
  );
25
- const ACCOUNT_SHAPE_INFO: AccountShapeInfo = {
27
+ const xpub = TEST_ADDRESS;
28
+ const ACCOUNT_SHAPE_INFO: AccountShapeInfo<CantonAccount> = {
26
29
  address: TEST_ADDRESS,
27
30
  currency,
28
31
  derivationMode,
29
32
  derivationPath,
30
33
  index: 0,
34
+ initialAccount: {
35
+ xpub,
36
+ } as CantonAccount,
31
37
  };
32
38
 
39
+ // Mock signer context for testing
40
+ const keyPair = generateMockKeyPair();
41
+ const mockSigner = createMockSigner(keyPair);
42
+ const mockSignerContext = jest.fn().mockImplementation((deviceId, callback) => {
43
+ return callback(mockSigner);
44
+ });
45
+
33
46
  describe("sync (devnet)", () => {
34
47
  beforeAll(async () => {
35
48
  coinConfig.setCoinConfig(() => ({
@@ -43,13 +56,14 @@ describe("sync (devnet)", () => {
43
56
  }));
44
57
  });
45
58
 
46
- describe("getAccountShape", () => {
59
+ describe("makeGetAccountShape", () => {
47
60
  it("should fetch account shape for a valid address", async () => {
61
+ const getAccountShape = makeGetAccountShape(mockSignerContext);
48
62
  const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });
49
63
 
50
64
  expect(result).toBeDefined();
51
65
  expect(result.id).toBeDefined();
52
- expect(result.xpub).toBe(TEST_ADDRESS.replace(/:/g, "_"));
66
+ expect(result.xpub).toBe(TEST_ADDRESS);
53
67
  expect(result.blockHeight).toBeGreaterThan(0);
54
68
  expect(result.balance).toBeDefined();
55
69
  expect(result.spendableBalance).toBeDefined();
@@ -67,12 +81,15 @@ describe("sync (devnet)", () => {
67
81
  });
68
82
 
69
83
  it("should handle address with colons correctly", async () => {
84
+ const getAccountShape = makeGetAccountShape(mockSignerContext);
70
85
  const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });
71
86
 
72
- expect(result.xpub).toBe(TEST_ADDRESS.replace(/:/g, "_"));
87
+ expect(result.xpub).toContain("::");
73
88
  });
74
89
 
75
90
  it("should merge operations correctly with initial account", async () => {
91
+ const getAccountShape = makeGetAccountShape(mockSignerContext);
92
+
76
93
  const operations: Operation[] = [
77
94
  {
78
95
  id: "test-op-1",
@@ -95,7 +112,7 @@ describe("sync (devnet)", () => {
95
112
  {
96
113
  ...ACCOUNT_SHAPE_INFO,
97
114
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
98
- initialAccount: { operations } as Account,
115
+ initialAccount: { xpub, operations } as Account,
99
116
  },
100
117
  { paginationConfig: {} },
101
118
  );
@@ -112,20 +129,26 @@ describe("sync (devnet)", () => {
112
129
  mockGetBalance.mockResolvedValue([
113
130
  {
114
131
  instrument_id: "Amulet",
115
- amount: 1000000,
132
+ amount: "500",
133
+ locked: false,
134
+ },
135
+ {
136
+ instrument_id: "LockedAmulet",
137
+ amount: "1000000",
116
138
  locked: true,
117
139
  },
118
140
  ]);
119
141
 
142
+ const getAccountShape = makeGetAccountShape(mockSignerContext);
120
143
  const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });
121
144
 
122
- expect(result.balance?.toNumber()).toBe(1000000);
123
- expect(result.spendableBalance?.toNumber()).toBe(0);
145
+ expect(result.balance?.toNumber()).toBe(1000500);
146
+ expect(result.spendableBalance?.toNumber()).toBe(500);
124
147
 
125
148
  mockGetBalance.mockRestore();
126
149
  });
127
150
 
128
- it("should call getOperations with correct cursor based with initial account", async () => {
151
+ it("should call getOperations with correct cursor based on initial account", async () => {
129
152
  const mockGetOperations = jest.spyOn(gateway, "getOperations");
130
153
  const operation: Operation = {
131
154
  id: "test-op-1",
@@ -144,8 +167,9 @@ describe("sync (devnet)", () => {
144
167
  };
145
168
 
146
169
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
147
- const initialAccount = { operations: [operation] } as Account;
170
+ const initialAccount = { xpub, operations: [operation] } as Account;
148
171
 
172
+ const getAccountShape = makeGetAccountShape(mockSignerContext);
149
173
  const result = await getAccountShape(
150
174
  {
151
175
  ...ACCOUNT_SHAPE_INFO,
@@ -154,7 +178,7 @@ describe("sync (devnet)", () => {
154
178
  { paginationConfig: {} },
155
179
  );
156
180
 
157
- expect(mockGetOperations).toHaveBeenCalledWith(TEST_ADDRESS, {
181
+ expect(mockGetOperations).toHaveBeenCalledWith(currency, TEST_ADDRESS, {
158
182
  cursor: (operation.blockHeight || 0) + 1,
159
183
  limit: 100,
160
184
  });
@@ -165,12 +189,13 @@ describe("sync (devnet)", () => {
165
189
  it("should call getOperations with cursor 0 when no initial account", async () => {
166
190
  const mockGetOperations = jest.spyOn(gateway, "getOperations");
167
191
 
192
+ const getAccountShape = makeGetAccountShape(mockSignerContext);
168
193
  const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });
169
194
 
170
195
  expect(result.operations).toBeDefined();
171
196
  expect(result.operationsCount).toBeGreaterThanOrEqual(1);
172
197
 
173
- expect(mockGetOperations).toHaveBeenCalledWith(TEST_ADDRESS, {
198
+ expect(mockGetOperations).toHaveBeenCalledWith(currency, TEST_ADDRESS, {
174
199
  cursor: 0,
175
200
  limit: 100,
176
201
  });