@canton-network/wallet-gateway-remote 0.24.0 → 0.26.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 (88) hide show
  1. package/dist/auth/jwt-auth-service.d.ts.map +1 -1
  2. package/dist/auth/jwt-auth-service.js +25 -0
  3. package/dist/config/Config.d.ts +31 -0
  4. package/dist/config/Config.d.ts.map +1 -1
  5. package/dist/config/Config.js +23 -0
  6. package/dist/config/Config.test.js +1 -1
  7. package/dist/dapp-api/controller.d.ts +1 -0
  8. package/dist/dapp-api/controller.d.ts.map +1 -1
  9. package/dist/dapp-api/controller.js +26 -1
  10. package/dist/dapp-api/rpc-gen/index.d.ts +3 -0
  11. package/dist/dapp-api/rpc-gen/index.d.ts.map +1 -1
  12. package/dist/dapp-api/rpc-gen/index.js +1 -0
  13. package/dist/dapp-api/rpc-gen/typings.d.ts +3 -2
  14. package/dist/dapp-api/rpc-gen/typings.d.ts.map +1 -1
  15. package/dist/dapp-api/server.test.js +1 -1
  16. package/dist/env.d.ts +1 -0
  17. package/dist/env.d.ts.map +1 -1
  18. package/dist/env.js +1 -0
  19. package/dist/example-config.d.ts +5 -0
  20. package/dist/example-config.d.ts.map +1 -1
  21. package/dist/example-config.js +5 -0
  22. package/dist/index.d.ts +2 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +18 -5
  25. package/dist/init.d.ts.map +1 -1
  26. package/dist/init.js +9 -4
  27. package/dist/ledger/party-allocation-service.test.js +19 -17
  28. package/dist/ledger/transaction-service.d.ts +1 -0
  29. package/dist/ledger/transaction-service.d.ts.map +1 -1
  30. package/dist/ledger/transaction-service.js +65 -53
  31. package/dist/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.d.ts.map +1 -1
  32. package/dist/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.js +2 -1
  33. package/dist/ledger/wallet-allocation/wallet-allocation-service.test.js +22 -22
  34. package/dist/ledger/wallet-sync-service.d.ts.map +1 -1
  35. package/dist/ledger/wallet-sync-service.js +11 -8
  36. package/dist/ledger/wallet-sync-service.test.js +15 -13
  37. package/dist/middleware/rateLimit.d.ts +5 -0
  38. package/dist/middleware/rateLimit.d.ts.map +1 -1
  39. package/dist/middleware/rateLimit.js +40 -0
  40. package/dist/middleware/rateLimit.test.d.ts +2 -0
  41. package/dist/middleware/rateLimit.test.d.ts.map +1 -0
  42. package/dist/middleware/rateLimit.test.js +30 -0
  43. package/dist/user-api/controller.d.ts.map +1 -1
  44. package/dist/user-api/controller.js +6 -5
  45. package/dist/user-api/rpc-gen/typings.d.ts +12 -14
  46. package/dist/user-api/rpc-gen/typings.d.ts.map +1 -1
  47. package/dist/user-api/server.test.js +1 -1
  48. package/dist/web/frontend/404/index.html +2 -2
  49. package/dist/web/frontend/{transactions → activities}/index.html +6 -7
  50. package/dist/web/frontend/approve/index.html +7 -7
  51. package/dist/web/frontend/assets/404-CEw-fKbi.js +5 -0
  52. package/dist/web/frontend/assets/activities-BdSUE0hv.js +60 -0
  53. package/dist/web/frontend/assets/addIdentityProvider-Bzz1fUGn.js +35 -0
  54. package/dist/web/frontend/assets/addNetwork-B4FpgV7D.js +38 -0
  55. package/dist/web/frontend/assets/addParty-D-UmQ0Oh.js +38 -0
  56. package/dist/web/frontend/assets/approve-DP3Lfyhw.js +21 -0
  57. package/dist/web/frontend/assets/{callback-Bbv8zOQx.js → callback-CXLUjUAK.js} +1 -1
  58. package/dist/web/frontend/assets/identityProviders-CtFhI1gg.js +71 -0
  59. package/dist/web/frontend/assets/index-BuC7gGqj.js +3942 -0
  60. package/dist/web/frontend/assets/{index-NP2zGQqX.js → index-CiMGhYb1.js} +2 -2
  61. package/dist/web/frontend/assets/index-DFhaSBOK.js +91 -0
  62. package/dist/web/frontend/assets/{index-64ZRFXyL.js → index-dDAXt5F2.js} +1 -1
  63. package/dist/web/frontend/assets/login-CmwQEYxS.js +10 -0
  64. package/dist/web/frontend/assets/networks-f8gSZxSb.js +73 -0
  65. package/dist/web/frontend/assets/reviewIdentityProvider-CJFOHumn.js +43 -0
  66. package/dist/web/frontend/assets/reviewNetwork-D3QSssLB.js +43 -0
  67. package/dist/web/frontend/assets/{settings-Bv6XHxUD.js → settings-Cc-Ij_uP.js} +2 -2
  68. package/dist/web/frontend/assets/{state-BsGwKQMR.js → state-CZ6wI2d4.js} +1 -1
  69. package/dist/web/frontend/assets/{utils-Ou54c_Bf.js → utils-CVOqcw_M.js} +1 -1
  70. package/dist/web/frontend/callback/index.html +2 -2
  71. package/dist/web/frontend/identity-providers/add/index.html +16 -0
  72. package/dist/web/frontend/{wallets → identity-providers}/index.html +5 -6
  73. package/dist/web/frontend/identity-providers/review/index.html +16 -0
  74. package/dist/web/frontend/index.html +1 -1
  75. package/dist/web/frontend/login/index.html +15 -7
  76. package/dist/web/frontend/networks/add/index.html +16 -0
  77. package/dist/web/frontend/networks/index.html +16 -0
  78. package/dist/web/frontend/networks/review/index.html +16 -0
  79. package/dist/web/frontend/parties/add/index.html +18 -0
  80. package/dist/web/frontend/parties/index.html +17 -0
  81. package/dist/web/frontend/settings/index.html +3 -3
  82. package/package.json +26 -29
  83. package/dist/web/frontend/assets/404-mTP2h7GO.js +0 -8
  84. package/dist/web/frontend/assets/approve-B7ioWbgg.js +0 -20
  85. package/dist/web/frontend/assets/index-CMV7Immb.js +0 -1698
  86. package/dist/web/frontend/assets/login-jo0xuh02.js +0 -7
  87. package/dist/web/frontend/assets/transactions-Cvi9DMx7.js +0 -28
  88. package/dist/web/frontend/assets/wallets-DFBcTQHX.js +0 -62
@@ -15,6 +15,13 @@ export class TransactionService {
15
15
  this.signingDrivers = signingDrivers;
16
16
  this.notifier = notifier;
17
17
  }
18
+ async loadPreparedTransactionForSigning(commandId) {
19
+ const existingTx = await this.store.getTransaction(commandId);
20
+ if (!existingTx) {
21
+ throw new Error(`Transaction not found with commandId: ${commandId}`);
22
+ }
23
+ return existingTx;
24
+ }
18
25
  signWithParticipant(wallet) {
19
26
  return {
20
27
  status: 'signed',
@@ -29,11 +36,11 @@ export class TransactionService {
29
36
  throw new Error('Wallet Kernel signing driver not available');
30
37
  }
31
38
  const driver = signingProvider.controller(userId);
32
- const { preparedTransaction, preparedTransactionHash, commandId } = signParams;
39
+ const tx = await this.loadPreparedTransactionForSigning(signParams.commandId);
33
40
  const { signature } = await driver
34
41
  .signTransaction({
35
- tx: preparedTransaction,
36
- txHash: preparedTransactionHash,
42
+ tx: tx.preparedTransaction,
43
+ txHash: tx.preparedTransactionHash,
37
44
  keyIdentifier: {
38
45
  publicKey: wallet.publicKey,
39
46
  },
@@ -42,20 +49,19 @@ export class TransactionService {
42
49
  if (!signature) {
43
50
  throw new Error('Failed to sign transaction: ' + JSON.stringify(signature));
44
51
  }
45
- const existingTx = await this.store.getTransaction(commandId);
46
52
  const now = new Date();
47
53
  const signedTx = {
48
- commandId,
54
+ commandId: tx.commandId,
49
55
  status: 'signed',
50
- preparedTransaction,
51
- preparedTransactionHash,
52
- origin: existingTx?.origin ?? null,
53
- ...(existingTx?.createdAt && {
54
- createdAt: existingTx.createdAt,
56
+ preparedTransaction: tx.preparedTransaction,
57
+ preparedTransactionHash: tx.preparedTransactionHash,
58
+ origin: tx?.origin ?? null,
59
+ ...(tx?.createdAt && {
60
+ createdAt: tx.createdAt,
55
61
  }),
56
62
  signedAt: now,
57
63
  };
58
- this.store.setTransaction(signedTx);
64
+ await this.store.setTransactionSigned(tx.commandId, now);
59
65
  this.notifier.emit('txChanged', signedTx);
60
66
  return {
61
67
  status: 'signed',
@@ -70,14 +76,13 @@ export class TransactionService {
70
76
  throw new Error('Blockdaemon signing driver not available');
71
77
  }
72
78
  const driver = signingProvider.controller(userId);
73
- const { preparedTransaction, preparedTransactionHash, commandId } = signParams;
79
+ const tx = await this.loadPreparedTransactionForSigning(signParams.commandId);
74
80
  let signingResult;
75
- const existingTx = await this.store.getTransaction(commandId);
76
- if (existingTx && existingTx.externalTxId) {
81
+ if (tx && tx.externalTxId) {
77
82
  signingResult = await driver
78
83
  .getTransaction({
79
84
  userId,
80
- txId: existingTx.externalTxId,
85
+ txId: tx.externalTxId,
81
86
  })
82
87
  .then(handleSigningError);
83
88
  }
@@ -88,8 +93,8 @@ export class TransactionService {
88
93
  .substring(0, 16);
89
94
  signingResult = await driver
90
95
  .signTransaction({
91
- tx: preparedTransaction,
92
- txHash: preparedTransactionHash,
96
+ tx: tx.preparedTransaction,
97
+ txHash: tx.preparedTransactionHash,
93
98
  keyIdentifier: {
94
99
  publicKey: wallet.publicKey,
95
100
  },
@@ -103,18 +108,18 @@ export class TransactionService {
103
108
  throw new Error('No signature returned from signing driver');
104
109
  }
105
110
  const signedTx = {
106
- commandId,
111
+ commandId: tx.commandId,
107
112
  status: signingResult.status,
108
- preparedTransaction,
109
- preparedTransactionHash,
110
- origin: existingTx?.origin ?? null,
111
- ...(existingTx?.createdAt && {
112
- createdAt: existingTx.createdAt,
113
+ preparedTransaction: tx.preparedTransaction,
114
+ preparedTransactionHash: tx.preparedTransactionHash,
115
+ origin: tx?.origin ?? null,
116
+ ...(tx?.createdAt && {
117
+ createdAt: tx.createdAt,
113
118
  }),
114
119
  signedAt: now,
115
120
  externalTxId: signingResult.txId,
116
121
  };
117
- this.store.setTransaction(signedTx);
122
+ await this.store.setTransactionSigned(tx.commandId, now, signingResult.txId);
118
123
  this.notifier.emit('txChanged', signedTx);
119
124
  return {
120
125
  status: signingResult.status,
@@ -127,17 +132,19 @@ export class TransactionService {
127
132
  else {
128
133
  const status = signingResult.status === 'pending' ? 'pending' : 'failed';
129
134
  const pendingTx = {
130
- commandId,
135
+ commandId: tx.commandId,
131
136
  status,
132
- preparedTransaction,
133
- preparedTransactionHash,
137
+ preparedTransaction: tx.preparedTransaction,
138
+ preparedTransactionHash: tx.preparedTransactionHash,
134
139
  externalTxId: signingResult.txId,
135
- origin: existingTx?.origin ?? null,
136
- ...(existingTx?.createdAt && {
137
- createdAt: existingTx.createdAt,
140
+ origin: tx?.origin ?? null,
141
+ ...(tx?.createdAt && {
142
+ createdAt: tx.createdAt,
138
143
  }),
139
144
  };
140
- this.store.setTransaction(pendingTx);
145
+ await this.store.setTransactionStatus(tx.commandId, status, {
146
+ externalTxId: signingResult.txId,
147
+ });
141
148
  this.notifier.emit('txChanged', pendingTx);
142
149
  return {
143
150
  status: signingResult.status,
@@ -152,14 +159,13 @@ export class TransactionService {
152
159
  throw new Error('Fireblocks signing driver not available');
153
160
  }
154
161
  const driver = signingProvider.controller(userId);
155
- const { preparedTransaction, preparedTransactionHash, commandId } = signParams;
162
+ const tx = await this.loadPreparedTransactionForSigning(signParams.commandId);
156
163
  let signingResult;
157
- const existingTx = await this.store.getTransaction(commandId);
158
- if (existingTx && existingTx.externalTxId) {
164
+ if (tx && tx.externalTxId) {
159
165
  signingResult = await driver
160
166
  .getTransaction({
161
167
  userId,
162
- txId: existingTx.externalTxId,
168
+ txId: tx.externalTxId,
163
169
  })
164
170
  .then(handleSigningError);
165
171
  }
@@ -167,8 +173,8 @@ export class TransactionService {
167
173
  signingResult = await driver
168
174
  .signTransaction({
169
175
  userId,
170
- tx: preparedTransaction,
171
- txHash: Buffer.from(preparedTransactionHash, 'base64').toString('hex'),
176
+ tx: tx.preparedTransaction,
177
+ txHash: Buffer.from(tx.preparedTransactionHash, 'base64').toString('hex'),
172
178
  keyIdentifier: {
173
179
  publicKey: wallet.publicKey,
174
180
  },
@@ -181,18 +187,18 @@ export class TransactionService {
181
187
  throw new Error('No signature returned from signing driver');
182
188
  }
183
189
  const signedTx = {
184
- commandId,
190
+ commandId: tx.commandId,
185
191
  status: signingResult.status,
186
- preparedTransaction,
187
- preparedTransactionHash,
188
- origin: existingTx?.origin ?? null,
189
- ...(existingTx?.createdAt && {
190
- createdAt: existingTx.createdAt,
192
+ preparedTransaction: tx.preparedTransaction,
193
+ preparedTransactionHash: tx.preparedTransactionHash,
194
+ origin: tx?.origin ?? null,
195
+ ...(tx?.createdAt && {
196
+ createdAt: tx.createdAt,
191
197
  }),
192
198
  signedAt: now,
193
199
  externalTxId: signingResult.txId,
194
200
  };
195
- this.store.setTransaction(signedTx);
201
+ await this.store.setTransactionSigned(tx.commandId, now, signingResult.txId);
196
202
  this.notifier.emit('txChanged', signedTx);
197
203
  // return signature in format that is already usable in execute
198
204
  const decodedSignature = Buffer.from(signingResult.signature, 'hex').toString('base64');
@@ -207,17 +213,19 @@ export class TransactionService {
207
213
  else {
208
214
  const status = signingResult.status === 'pending' ? 'pending' : 'failed';
209
215
  const pendingTx = {
210
- commandId,
216
+ commandId: tx.commandId,
211
217
  status,
212
- preparedTransaction,
213
- preparedTransactionHash,
218
+ preparedTransaction: tx.preparedTransaction,
219
+ preparedTransactionHash: tx.preparedTransactionHash,
214
220
  externalTxId: signingResult.txId,
215
- origin: existingTx?.origin ?? null,
216
- ...(existingTx?.createdAt && {
217
- createdAt: existingTx.createdAt,
221
+ origin: tx?.origin ?? null,
222
+ ...(tx?.createdAt && {
223
+ createdAt: tx.createdAt,
218
224
  }),
219
225
  };
220
- this.store.setTransaction(pendingTx);
226
+ await this.store.setTransactionStatus(tx.commandId, status, {
227
+ externalTxId: signingResult.txId,
228
+ });
221
229
  this.notifier.emit('txChanged', pendingTx);
222
230
  return {
223
231
  status: signingResult.status,
@@ -245,7 +253,9 @@ export class TransactionService {
245
253
  signedAt: transaction.signedAt,
246
254
  }),
247
255
  };
248
- this.store.setTransaction(executedTx);
256
+ await this.store.setTransactionStatus(commandId, 'executed', {
257
+ payload: res,
258
+ });
249
259
  this.notifier.emit('txChanged', executedTx);
250
260
  return res;
251
261
  }
@@ -289,7 +299,9 @@ export class TransactionService {
289
299
  signedAt: transaction.signedAt,
290
300
  }),
291
301
  };
292
- this.store.setTransaction(executedTx);
302
+ await this.store.setTransactionStatus(commandId, 'executed', {
303
+ payload: result,
304
+ });
293
305
  this.notifier.emit('txChanged', executedTx);
294
306
  return result;
295
307
  }
@@ -1 +1 @@
1
- {"version":3,"file":"blockdaemon-wallet-allocator.d.ts","sourceRoot":"","sources":["../../../../src/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAA;AACzD,OAAO,EAAE,KAAK,EAAgB,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAC/E,OAAO,EAEH,sBAAsB,EAEzB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAYtE,qBAAa,0BAA2B,YAAW,eAAe;IAE1D,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,aAAa;gBAHb,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,sBAAsB,EACtC,aAAa,EAAE,sBAAsB;IAG3C,YAAY,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,OAAe,GACzB,OAAO,CAAC,MAAM,CAAC;IAwGZ,aAAa,CACf,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC;CA6DnB"}
1
+ {"version":3,"file":"blockdaemon-wallet-allocator.d.ts","sourceRoot":"","sources":["../../../../src/ledger/wallet-allocation/signing-providers/blockdaemon-wallet-allocator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAA;AACzD,OAAO,EAAE,KAAK,EAAgB,MAAM,EAAE,MAAM,mCAAmC,CAAA;AAC/E,OAAO,EAEH,sBAAsB,EAEzB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAYtE,qBAAa,0BAA2B,YAAW,eAAe;IAE1D,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,aAAa;gBAHb,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,sBAAsB,EACtC,aAAa,EAAE,sBAAsB;IAG3C,YAAY,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,OAAe,GACzB,OAAO,CAAC,MAAM,CAAC;IAwGZ,aAAa,CACf,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC;CAgEnB"}
@@ -102,7 +102,7 @@ export class BlockdaemonWalletAllocator {
102
102
  throw new Error('Existing wallet is missing field externalTxId or topologyTransactions');
103
103
  }
104
104
  const driver = this.signingDriver.controller(email);
105
- const { signature, status } = await driver
105
+ const { signature, status, metadata } = await driver
106
106
  .getTransaction({
107
107
  txId: existingWallet.externalTxId,
108
108
  })
@@ -131,6 +131,7 @@ export class BlockdaemonWalletAllocator {
131
131
  };
132
132
  }
133
133
  else {
134
+ this.logger.warn(`Topology transaction for wallet ${existingWallet.partyId} was ${status} with ${JSON.stringify(metadata)}`);
134
135
  const reason = status === 'rejected'
135
136
  ? WALLET_DISABLED_REASON.TOPOLOGY_TRANSACTION_REJECTED
136
137
  : WALLET_DISABLED_REASON.TOPOLOGY_TRANSACTION_FAILED;
@@ -1,6 +1,6 @@
1
1
  // Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { jest, describe, it, expect, beforeEach, afterEach, } from '@jest/globals';
3
+ import { vi, describe, it, expect, beforeEach, afterEach, } from 'vitest';
4
4
  import { pino } from 'pino';
5
5
  import { sink } from 'pino-test';
6
6
  import { WalletAllocationService } from './wallet-allocation-service.js';
@@ -34,14 +34,14 @@ function createFireblocksDriver(options) {
34
34
  };
35
35
  const getTransactionResult = options.getTransactionResult ?? signTransactionResult;
36
36
  return {
37
- controller: jest.fn().mockReturnValue({
38
- getKeys: jest
37
+ controller: vi.fn().mockReturnValue({
38
+ getKeys: vi
39
39
  .fn()
40
40
  .mockResolvedValue(getKeysResult),
41
- signTransaction: jest
41
+ signTransaction: vi
42
42
  .fn()
43
43
  .mockResolvedValue(signTransactionResult),
44
- getTransaction: jest
44
+ getTransaction: vi
45
45
  .fn()
46
46
  .mockResolvedValue(getTransactionResult),
47
47
  }),
@@ -54,16 +54,16 @@ function createBlockdaemonDriver(options) {
54
54
  };
55
55
  const getTransactionResult = options.getTransactionResult ?? signTransactionResult;
56
56
  return {
57
- controller: jest.fn().mockReturnValue({
58
- createKey: jest
57
+ controller: vi.fn().mockReturnValue({
58
+ createKey: vi
59
59
  .fn()
60
60
  .mockResolvedValue({
61
61
  publicKey: 'bd-pk',
62
62
  }),
63
- signTransaction: jest
63
+ signTransaction: vi
64
64
  .fn()
65
65
  .mockResolvedValue(signTransactionResult),
66
- getTransaction: jest
66
+ getTransaction: vi
67
67
  .fn()
68
68
  .mockResolvedValue(getTransactionResult),
69
69
  }),
@@ -80,19 +80,19 @@ describe('WalletAllocationService', () => {
80
80
  beforeEach(async () => {
81
81
  mockLogger = pino(sink());
82
82
  mockStore = {
83
- getWallets: jest.fn(),
84
- removeWallet: jest.fn(),
85
- addWallet: jest.fn(),
86
- updateWallet: jest.fn(),
87
- getCurrentNetwork: jest
83
+ getWallets: vi.fn(),
84
+ removeWallet: vi.fn(),
85
+ addWallet: vi.fn(),
86
+ updateWallet: vi.fn(),
87
+ getCurrentNetwork: vi
88
88
  .fn()
89
89
  .mockResolvedValue({ id: 'network1' }),
90
90
  };
91
91
  mockPartyAllocator = {
92
- allocateParty: jest.fn(),
93
- allocatePartyWithExistingWallet: jest.fn(),
94
- createFingerprintFromKey: jest.fn().mockReturnValue('fingerprint'),
95
- generateTopologyTransactions: jest
92
+ allocateParty: vi.fn(),
93
+ allocatePartyWithExistingWallet: vi.fn(),
94
+ createFingerprintFromKey: vi.fn().mockReturnValue('fingerprint'),
95
+ generateTopologyTransactions: vi
96
96
  .fn()
97
97
  .mockResolvedValue({
98
98
  topologyTransactions: ['tx1'],
@@ -100,14 +100,14 @@ describe('WalletAllocationService', () => {
100
100
  }),
101
101
  };
102
102
  mockController = {
103
- createKey: jest
103
+ createKey: vi
104
104
  .fn()
105
105
  .mockResolvedValue({
106
106
  id: 'key-id',
107
107
  name: 'test-key',
108
108
  publicKey: 'new-public-key',
109
109
  }),
110
- signTransaction: jest
110
+ signTransaction: vi
111
111
  .fn()
112
112
  .mockResolvedValue({
113
113
  txId: 'tx-id',
@@ -116,14 +116,14 @@ describe('WalletAllocationService', () => {
116
116
  }),
117
117
  };
118
118
  mockWalletKernelDriver = {
119
- controller: jest.fn(() => mockController),
119
+ controller: vi.fn(() => mockController),
120
120
  };
121
121
  service = createService({
122
122
  [SigningProvider.WALLET_KERNEL]: mockWalletKernelDriver,
123
123
  });
124
124
  });
125
125
  afterEach(() => {
126
- jest.restoreAllMocks();
126
+ vi.restoreAllMocks();
127
127
  });
128
128
  describe('Participant', () => {
129
129
  it('createWallet allocates new party and adds wallet', async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"wallet-sync-service.d.ts","sourceRoot":"","sources":["../../src/ledger/wallet-sync-service.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,YAAY,EAEf,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAC9D,OAAO,EACH,KAAK,EAIR,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACH,sBAAsB,EACtB,eAAe,EAClB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAGlE,qBAAa,iBAAiB;IAEtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IAGtB,OAAO,CAAC,cAAc;gBAPd,KAAK,EAAE,KAAK,EACZ,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,OAAO,CAC3B,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAClD,YAAK,EACE,cAAc,EAAE,sBAAsB;IAGlD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAwB;IAE5D,OAAO,CAAC,UAAU;IAUZ,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAU3B,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAC5D;QACI,iBAAiB,EAAE,eAAe,CAAC,WAAW,CAAA;QAC9C,OAAO,EAAE,OAAO,CAAA;KACnB,GACD;QACI,iBAAiB,EAAE,OAAO,CACtB,eAAe,EACf,eAAe,CAAC,WAAW,CAC9B,CAAA;QACD,SAAS,EAAE,MAAM,CAAA;QACjB,OAAO,EAAE,OAAO,CAAA;KACnB,CACN;YA4Ha,iBAAiB;IA2DzB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;YA8D9B,yBAAyB;YAkEzB,0BAA0B;YAgD1B,mBAAmB;IAsB3B,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAuGlD"}
1
+ {"version":3,"file":"wallet-sync-service.d.ts","sourceRoot":"","sources":["../../src/ledger/wallet-sync-service.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,YAAY,EAEf,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAC9D,OAAO,EACH,KAAK,EAIR,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACH,sBAAsB,EACtB,eAAe,EAClB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAGlE,qBAAa,iBAAiB;IAEtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IAGtB,OAAO,CAAC,cAAc;gBAPd,KAAK,EAAE,KAAK,EACZ,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,OAAO,CAC3B,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAClD,YAAK,EACE,cAAc,EAAE,sBAAsB;IAGlD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAwB;IAE5D,OAAO,CAAC,UAAU;IAUZ,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAU3B,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAC5D;QACI,iBAAiB,EAAE,eAAe,CAAC,WAAW,CAAA;QAC9C,OAAO,EAAE,OAAO,CAAA;KACnB,GACD;QACI,iBAAiB,EAAE,OAAO,CACtB,eAAe,EACf,eAAe,CAAC,WAAW,CAC9B,CAAA;QACD,SAAS,EAAE,MAAM,CAAA;QACjB,OAAO,EAAE,OAAO,CAAA;KACnB,CACN;YA4Ha,iBAAiB;IA8DzB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;YA8D9B,yBAAyB;YAkEzB,0BAA0B;YAgD1B,mBAAmB;IAsB3B,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAuGlD"}
@@ -138,24 +138,27 @@ export class WalletSyncService {
138
138
  return created;
139
139
  };
140
140
  rights.rights?.forEach((right) => {
141
- if ('CanActAs' in right.kind) {
142
- const party = right.kind.CanActAs.value.party;
141
+ const kind = right.kind;
142
+ if (!kind)
143
+ return;
144
+ if ('CanActAs' in kind) {
145
+ const party = kind.CanActAs.value.party;
143
146
  getOrCreateRights(party).add(PartyLevelRight.CanActAs);
144
147
  }
145
- else if ('CanExecuteAs' in right.kind) {
146
- const party = right.kind.CanExecuteAs.value.party;
148
+ else if ('CanExecuteAs' in kind) {
149
+ const party = kind.CanExecuteAs.value.party;
147
150
  getOrCreateRights(party).add(PartyLevelRight.CanExecuteAs);
148
151
  }
149
- else if ('CanReadAs' in right.kind) {
150
- const party = right.kind.CanReadAs.value.party;
152
+ else if ('CanReadAs' in kind) {
153
+ const party = kind.CanReadAs.value.party;
151
154
  getOrCreateRights(party).add(PartyLevelRight.CanReadAs);
152
155
  }
153
- else if ('CanReadAsAnyParty' in right.kind) {
156
+ else if ('CanReadAsAnyParty' in kind) {
154
157
  rightsByUser
155
158
  .get(this.authContext.userId)
156
159
  ?.add(UserLevelRight.CanReadAsAnyParty);
157
160
  }
158
- else if ('CanExecuteAsAnyParty' in right.kind) {
161
+ else if ('CanExecuteAsAnyParty' in kind) {
159
162
  rightsByUser
160
163
  .get(this.authContext.userId)
161
164
  ?.add(UserLevelRight.CanExecuteAsAnyParty);
@@ -1,6 +1,6 @@
1
1
  // Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { jest, describe, it, expect, beforeEach, afterEach, } from '@jest/globals';
3
+ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
4
4
  import { pino } from 'pino';
5
5
  import { sink } from 'pino-test';
6
6
  import { SigningProvider, } from '@canton-network/core-signing-lib';
@@ -11,9 +11,11 @@ import { PartyLevelRight, } from '@canton-network/core-wallet-store';
11
11
  import { StoreInternal } from '@canton-network/core-wallet-store-inmemory';
12
12
  import { WalletSyncService } from './wallet-sync-service.js';
13
13
  import { PartyAllocationService } from './party-allocation-service.js';
14
- const mockLedgerGet = jest.fn();
15
- jest.unstable_mockModule('@canton-network/core-ledger-client', () => ({
16
- LedgerClient: jest.fn().mockImplementation(() => {
14
+ const { mockLedgerGet } = vi.hoisted(() => ({
15
+ mockLedgerGet: vi.fn(),
16
+ }));
17
+ vi.mock('@canton-network/core-ledger-client', () => ({
18
+ LedgerClient: vi.fn(function LedgerClientMock() {
17
19
  return {
18
20
  getWithRetry: mockLedgerGet,
19
21
  };
@@ -83,7 +85,7 @@ describe('WalletSyncService - resolveSigningProvider', () => {
83
85
  }, partyAllocator);
84
86
  });
85
87
  afterEach(() => {
86
- jest.restoreAllMocks();
88
+ vi.restoreAllMocks();
87
89
  mockLedgerGet.mockClear();
88
90
  });
89
91
  it('resolves participant when namespace matches participant namespace', async () => {
@@ -128,8 +130,8 @@ describe('WalletSyncService - resolveSigningProvider', () => {
128
130
  const normalizedKey = partyAllocator.normalizePublicKeyToBase64(fireblocksPublicKeyHex);
129
131
  const namespace = partyAllocator.createFingerprintFromKey(normalizedKey);
130
132
  const mockFireblocksDriver = {
131
- controller: jest.fn().mockReturnValue({
132
- getKeys: jest
133
+ controller: vi.fn().mockReturnValue({
134
+ getKeys: vi
133
135
  .fn()
134
136
  .mockResolvedValue({
135
137
  keys: [
@@ -253,7 +255,7 @@ describe('WalletSyncService - multi-network features', () => {
253
255
  service = new WalletSyncService(store, mockLedgerClient, authContext, mockLogger, {}, partyAllocator);
254
256
  });
255
257
  afterEach(() => {
256
- jest.restoreAllMocks();
258
+ vi.restoreAllMocks();
257
259
  mockLedgerGet.mockClear();
258
260
  });
259
261
  it('isWalletSyncNeeded should filter by current network', async () => {
@@ -305,7 +307,7 @@ describe('WalletSyncService - multi-network features', () => {
305
307
  await store.addNetwork(network1);
306
308
  await setSession('network1');
307
309
  await store.addWallet(createWallet('party1::namespace', 'network1'));
308
- const addWalletSpy = jest.spyOn(store, 'addWallet');
310
+ const addWalletSpy = vi.spyOn(store, 'addWallet');
309
311
  mockLedgerGet
310
312
  .mockResolvedValueOnce({
311
313
  rights: [
@@ -542,7 +544,7 @@ describe('WalletSyncService - multi-network features', () => {
542
544
  .mockResolvedValueOnce({
543
545
  participantId: 'participant1::namespace',
544
546
  });
545
- const updateWalletSpy = jest.spyOn(store, 'updateWallet');
547
+ const updateWalletSpy = vi.spyOn(store, 'updateWallet');
546
548
  await service.syncWallets();
547
549
  expect(updateWalletSpy).toHaveBeenCalledWith(expect.objectContaining({
548
550
  partyId: 'party1::namespace',
@@ -575,7 +577,7 @@ describe('WalletSyncService - multi-network features', () => {
575
577
  .mockResolvedValueOnce({
576
578
  participantId: 'participant1::namespace',
577
579
  });
578
- const updateWalletSpy = jest.spyOn(store, 'updateWallet');
580
+ const updateWalletSpy = vi.spyOn(store, 'updateWallet');
579
581
  await service.syncWallets();
580
582
  expect(updateWalletSpy).not.toHaveBeenCalled();
581
583
  const wallets = await store.getWallets();
@@ -603,7 +605,7 @@ describe('WalletSyncService - multi-network features', () => {
603
605
  .mockResolvedValueOnce({
604
606
  participantId: 'participant1::namespace',
605
607
  });
606
- const updateWalletSpy = jest.spyOn(store, 'updateWallet');
608
+ const updateWalletSpy = vi.spyOn(store, 'updateWallet');
607
609
  await service.syncWallets();
608
610
  expect(updateWalletSpy).toHaveBeenCalledTimes(2);
609
611
  expect(updateWalletSpy).toHaveBeenCalledWith(expect.objectContaining({
@@ -639,7 +641,7 @@ describe('WalletSyncService - multi-network features', () => {
639
641
  .mockResolvedValueOnce({
640
642
  participantId: 'participant1::namespace',
641
643
  });
642
- const updateWalletSpy = jest.spyOn(store, 'updateWallet');
644
+ const updateWalletSpy = vi.spyOn(store, 'updateWallet');
643
645
  await service.syncWallets();
644
646
  expect(updateWalletSpy).toHaveBeenCalledWith(expect.objectContaining({
645
647
  partyId: 'party1::namespace',
@@ -1,2 +1,7 @@
1
+ import type { Request } from 'express';
2
+ export declare function ipRateLimitKeyGenerator(req: Request): string;
3
+ export declare function rateLimitKeyGenerator(req: Request): string;
1
4
  export declare function rateLimiter(requestRateLimit: number): import("express-rate-limit").RateLimitRequestHandler;
5
+ export declare function preAuthIpRateLimiter(requestRateLimit: number): import("express-rate-limit").RateLimitRequestHandler;
6
+ export declare function authenticatedRateLimiter(requestRateLimit: number): import("express-rate-limit").RateLimitRequestHandler;
2
7
  //# sourceMappingURL=rateLimit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"AAKA,wBAAgB,WAAW,CAAC,gBAAgB,EAAE,MAAM,wDAOnD"}
1
+ {"version":3,"file":"rateLimit.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAWtC,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAE5D;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAO1D;AAED,wBAAgB,WAAW,CAAC,gBAAgB,EAAE,MAAM,wDAQnD;AAED,wBAAgB,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,wDAU5D;AAED,wBAAgB,wBAAwB,CAAC,gBAAgB,EAAE,MAAM,wDAUhE"}
@@ -1,10 +1,50 @@
1
1
  // Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import rateLimit from 'express-rate-limit';
4
+ function hasBearerToken(req) {
5
+ const authHeader = req.headers.authorization;
6
+ if (typeof authHeader === 'string' && authHeader.startsWith('Bearer ')) {
7
+ return true;
8
+ }
9
+ return typeof req.query.token === 'string' && req.query.token.length > 0;
10
+ }
11
+ export function ipRateLimitKeyGenerator(req) {
12
+ return `ip:${req.ip || req.socket.remoteAddress || 'unknown'}`;
13
+ }
14
+ export function rateLimitKeyGenerator(req) {
15
+ // Prefer authenticated identity to avoid shared proxy IP buckets.
16
+ if (req.authContext?.userId) {
17
+ return `user:${req.authContext.userId}`;
18
+ }
19
+ return ipRateLimitKeyGenerator(req);
20
+ }
4
21
  export function rateLimiter(requestRateLimit) {
5
22
  return rateLimit({
6
23
  windowMs: 1 * 60 * 1000, // 1 minute
7
24
  max: requestRateLimit, // limit each IP to requestRateLimit requests per windowMs
25
+ keyGenerator: rateLimitKeyGenerator,
26
+ standardHeaders: true,
27
+ legacyHeaders: false,
28
+ });
29
+ }
30
+ export function preAuthIpRateLimiter(requestRateLimit) {
31
+ return rateLimit({
32
+ windowMs: 1 * 60 * 1000,
33
+ max: requestRateLimit,
34
+ keyGenerator: ipRateLimitKeyGenerator,
35
+ // Only protect unauthenticated traffic before JWT verification.
36
+ skip: hasBearerToken,
37
+ standardHeaders: true,
38
+ legacyHeaders: false,
39
+ });
40
+ }
41
+ export function authenticatedRateLimiter(requestRateLimit) {
42
+ return rateLimit({
43
+ windowMs: 1 * 60 * 1000,
44
+ max: requestRateLimit,
45
+ keyGenerator: rateLimitKeyGenerator,
46
+ // Apply only after JWT middleware has attached an authenticated context.
47
+ skip: (req) => !req.authContext?.userId,
8
48
  standardHeaders: true,
9
49
  legacyHeaders: false,
10
50
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rateLimit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rateLimit.test.d.ts","sourceRoot":"","sources":["../../src/middleware/rateLimit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ // Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { describe, expect, test } from 'vitest';
4
+ import { ipRateLimitKeyGenerator, rateLimitKeyGenerator } from './rateLimit.js';
5
+ describe('ipRateLimitKeyGenerator', () => {
6
+ test('uses request ip address', () => {
7
+ const req = {
8
+ ip: '198.51.100.42',
9
+ socket: { remoteAddress: '10.0.0.1' },
10
+ };
11
+ expect(ipRateLimitKeyGenerator(req)).toBe('ip:198.51.100.42');
12
+ });
13
+ });
14
+ describe('rateLimitKeyGenerator', () => {
15
+ test('uses authenticated user id when available', () => {
16
+ const req = {
17
+ authContext: { userId: 'alice' },
18
+ ip: '203.0.113.10',
19
+ socket: { remoteAddress: '10.0.0.1' },
20
+ };
21
+ expect(rateLimitKeyGenerator(req)).toBe('user:alice');
22
+ });
23
+ test('falls back to request ip when user is not authenticated', () => {
24
+ const req = {
25
+ ip: '198.51.100.42',
26
+ socket: { remoteAddress: '10.0.0.1' },
27
+ };
28
+ expect(rateLimitKeyGenerator(req)).toBe('ip:198.51.100.42');
29
+ });
30
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/user-api/controller.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAE,KAAK,EAAW,MAAM,mCAAmC,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAC5E,OAAO,EAEH,WAAW,EAKd,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EACH,sBAAsB,EACtB,eAAe,EAClB,MAAM,kCAAkC,CAAA;AAQzC,KAAK,uBAAuB,GAAG,OAAO,CAClC,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAClD,CAAA;AAED,eAAO,MAAM,cAAc,GACvB,YAAY,UAAU,EACtB,SAAS,MAAM,EACf,OAAO,KAAK,EACZ,qBAAqB,mBAAmB,EACxC,aAAa,WAAW,GAAG,SAAS,EACpC,SAAS,uBAAuB,EAChC,SAAS,MAAM,EACf,cAAc,MAAM;;;;;;;;;;;;;;;;;;;;;;;CA8vBvB,CAAA"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/user-api/controller.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAE,KAAK,EAAW,MAAM,mCAAmC,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAC5E,OAAO,EAEH,WAAW,EAKd,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EACH,sBAAsB,EACtB,eAAe,EAClB,MAAM,kCAAkC,CAAA;AASzC,KAAK,uBAAuB,GAAG,OAAO,CAClC,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAClD,CAAA;AAED,eAAO,MAAM,cAAc,GACvB,YAAY,UAAU,EACtB,SAAS,MAAM,EACf,OAAO,KAAK,EACZ,qBAAqB,mBAAmB,EACxC,aAAa,WAAW,GAAG,SAAS,EACpC,SAAS,uBAAuB,EAChC,SAAS,MAAM,EACf,cAAc,MAAM;;;;;;;;;;;;;;;;;;;;;;;CA+vBvB,CAAA"}