@capxul/sdk 0.1.0-alpha.3 → 0.1.0-alpha.6

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.
package/dist/index.cjs CHANGED
@@ -70,12 +70,441 @@ function fromConvexError(error) {
70
70
  });
71
71
  }
72
72
 
73
+ // ../observability/src/try-catch.ts
74
+ async function tryCatch(promise) {
75
+ try {
76
+ return [null, await promise];
77
+ } catch (e) {
78
+ return [e instanceof Error ? e : new Error(String(e)), null];
79
+ }
80
+ }
81
+
82
+ // ../observability/src/debug-log.ts
83
+ function isDevelopmentBuild() {
84
+ if (typeof process === "undefined") {
85
+ return false;
86
+ }
87
+ return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
88
+ }
89
+ function debugLog(line) {
90
+ if (!isDevelopmentBuild()) return;
91
+ if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
92
+ console.info(line);
93
+ return;
94
+ }
95
+ if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
96
+ process.stderr.write(`${line}
97
+ `);
98
+ }
99
+ }
100
+ function formatDebugValue(value) {
101
+ if (value === void 0 || value === "") return "";
102
+ if (typeof value === "string") return value;
103
+ try {
104
+ return JSON.stringify(value);
105
+ } catch {
106
+ return String(value);
107
+ }
108
+ }
109
+ function track(...args) {
110
+ const [name, props] = args;
111
+ debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
112
+ }
113
+ function formatDebugValue2(value) {
114
+ if (value === void 0 || value === "") return "";
115
+ if (typeof value === "string") return value;
116
+ try {
117
+ return JSON.stringify(value);
118
+ } catch {
119
+ return String(value);
120
+ }
121
+ }
122
+ function identify(userId, traits) {
123
+ debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
124
+ }
125
+
126
+ // ../platform-kernel/src/ids.ts
127
+ function makePrefixedIdConstructor(prefix, fieldName) {
128
+ const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
129
+ return (raw) => {
130
+ if (typeof raw !== "string" || !re.test(raw)) {
131
+ throw new Error(
132
+ `Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
133
+ );
134
+ }
135
+ return raw;
136
+ };
137
+ }
138
+ var toAccountId = makePrefixedIdConstructor(
139
+ "acct",
140
+ "accountId"
141
+ );
142
+ var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
143
+ var toMemberId = makePrefixedIdConstructor(
144
+ "mem",
145
+ "memberId"
146
+ );
147
+ var toSafeId = makePrefixedIdConstructor(
148
+ "safe",
149
+ "safeId"
150
+ );
151
+ var toTreasuryId = makePrefixedIdConstructor(
152
+ "try",
153
+ "treasuryId"
154
+ );
155
+ var toApiKeyId = makePrefixedIdConstructor(
156
+ "ak",
157
+ "apiKeyId"
158
+ );
159
+ var toOperationId = makePrefixedIdConstructor(
160
+ "op",
161
+ "operationId"
162
+ );
163
+ var toCorrelationId = makePrefixedIdConstructor("ctx", "correlationId");
164
+ var toKycProfileId = makePrefixedIdConstructor(
165
+ "kyc",
166
+ "kycProfileId"
167
+ );
168
+ var toKybProfileId = makePrefixedIdConstructor(
169
+ "kyb",
170
+ "kybProfileId"
171
+ );
172
+ var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
173
+ var toPaymentId = makePrefixedIdConstructor(
174
+ "pay",
175
+ "paymentId"
176
+ );
177
+ var toWithdrawalId = makePrefixedIdConstructor(
178
+ "wd",
179
+ "withdrawalId"
180
+ );
181
+ var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
182
+ var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
183
+ var toSubAccountId = makePrefixedIdConstructor(
184
+ "sub",
185
+ "subAccountId"
186
+ );
187
+ var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
188
+ var toVirtualCardId = makePrefixedIdConstructor(
189
+ "vc",
190
+ "virtualCardId"
191
+ );
192
+ var toTransferId = makePrefixedIdConstructor(
193
+ "txfr",
194
+ "transferId"
195
+ );
196
+ var toDocumentId = makePrefixedIdConstructor(
197
+ "doc",
198
+ "documentId"
199
+ );
200
+ var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
201
+
202
+ // ../platform-kernel/src/value-objects.ts
203
+ var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
204
+ function toPhoneNumber(raw) {
205
+ if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
206
+ throw new Error(
207
+ `Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
208
+ );
209
+ }
210
+ return raw;
211
+ }
212
+ var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
213
+ function toEmail(raw) {
214
+ if (typeof raw !== "string") {
215
+ throw new Error(
216
+ `Invalid email: expected string, got ${String(raw)}`
217
+ );
218
+ }
219
+ const lowered = raw.trim().toLowerCase();
220
+ if (!EMAIL_RE.test(lowered)) {
221
+ throw new Error(
222
+ `Invalid email: expected local@domain.tld, got ${String(raw)}`
223
+ );
224
+ }
225
+ return lowered;
226
+ }
227
+ var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
228
+ function toUsername(raw) {
229
+ if (typeof raw !== "string") {
230
+ throw new Error(
231
+ `Invalid username: expected string, got ${String(raw)}`
232
+ );
233
+ }
234
+ const lowered = raw.toLowerCase();
235
+ if (!USERNAME_RE.test(lowered)) {
236
+ throw new Error(
237
+ `Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
238
+ );
239
+ }
240
+ return lowered;
241
+ }
242
+
243
+ // src/core/external-accounts.ts
244
+ function brandExternalAccount(raw) {
245
+ return {
246
+ ...raw,
247
+ id: toExternalAccountId(raw.id),
248
+ operation: {
249
+ id: toOperationId(raw.operation.id),
250
+ status: raw.operation.status,
251
+ correlationId: toCorrelationId(raw.operation.correlationId)
252
+ }
253
+ };
254
+ }
255
+ function createExternalAccountsClient(config = {}) {
256
+ return {
257
+ retrieve: async (externalAccountId) => {
258
+ if (!config.data) {
259
+ return stub(
260
+ "externalAccounts.retrieve"
261
+ );
262
+ }
263
+ const [err, raw] = await tryCatch(
264
+ config.data.query(api.externalAccounts.queries.retrievePersonal, {
265
+ externalAccountId
266
+ })
267
+ );
268
+ if (err) {
269
+ return [
270
+ fromConvexError(err),
271
+ null
272
+ ];
273
+ }
274
+ if (!raw) {
275
+ return [
276
+ new CapxulError({
277
+ code: "NOT_FOUND",
278
+ message: `external_account ${externalAccountId} not found`
279
+ }),
280
+ null
281
+ ];
282
+ }
283
+ return [null, brandExternalAccount(raw)];
284
+ },
285
+ remove: async (externalAccountId) => {
286
+ if (!config.data) {
287
+ return stub("externalAccounts.remove");
288
+ }
289
+ const [err] = await tryCatch(
290
+ config.data.mutation(api.externalAccounts.mutations.removePersonal, {
291
+ externalAccountId
292
+ })
293
+ );
294
+ if (err) {
295
+ return [
296
+ fromConvexError(err),
297
+ null
298
+ ];
299
+ }
300
+ return [null, void 0];
301
+ }
302
+ };
303
+ }
304
+
73
305
  // src/core/accounts.ts
306
+ function createAccountExternalAccountsClient(config) {
307
+ return {
308
+ create: async (input) => {
309
+ if (!config.data) {
310
+ return stub(
311
+ "accounts.externalAccounts.create"
312
+ );
313
+ }
314
+ const [err, raw] = await tryCatch(
315
+ config.data.mutation(
316
+ api.externalAccounts.mutations.createPersonal,
317
+ {
318
+ kind: input.kind,
319
+ label: input.label,
320
+ address: input.address,
321
+ iban: input.iban,
322
+ bic: input.bic,
323
+ accountHolder: input.accountHolder,
324
+ network: input.network,
325
+ panToken: input.panToken,
326
+ last4: input.last4
327
+ }
328
+ )
329
+ );
330
+ if (err) {
331
+ return [
332
+ fromConvexError(err),
333
+ null
334
+ ];
335
+ }
336
+ if (!raw) {
337
+ return [
338
+ new CapxulError({
339
+ code: "NOT_FOUND",
340
+ message: "external_account creation returned no resource"
341
+ }),
342
+ null
343
+ ];
344
+ }
345
+ return [
346
+ null,
347
+ brandExternalAccount(
348
+ raw
349
+ )
350
+ ];
351
+ },
352
+ list: async (input) => {
353
+ if (!config.data) {
354
+ return stub(
355
+ "accounts.externalAccounts.list"
356
+ );
357
+ }
358
+ const [err, result] = await tryCatch(
359
+ config.data.query(api.externalAccounts.queries.listPersonal, {
360
+ limit: input.limit,
361
+ cursor: input.cursor
362
+ })
363
+ );
364
+ if (err) {
365
+ return [fromConvexError(err), null];
366
+ }
367
+ const branded = result.data.map(
368
+ (row) => brandExternalAccount(
369
+ row
370
+ )
371
+ );
372
+ return [
373
+ null,
374
+ {
375
+ object: "list",
376
+ data: branded,
377
+ page: result.page
378
+ }
379
+ ];
380
+ },
381
+ retrieve: async (externalAccountId) => {
382
+ if (!config.data) {
383
+ return stub(
384
+ "accounts.externalAccounts.retrieve"
385
+ );
386
+ }
387
+ const [err, raw] = await tryCatch(
388
+ config.data.query(api.externalAccounts.queries.retrievePersonal, {
389
+ externalAccountId
390
+ })
391
+ );
392
+ if (err) {
393
+ return [
394
+ fromConvexError(err),
395
+ null
396
+ ];
397
+ }
398
+ if (!raw) {
399
+ return [
400
+ new CapxulError({
401
+ code: "NOT_FOUND",
402
+ message: `external_account ${externalAccountId} not found`
403
+ }),
404
+ null
405
+ ];
406
+ }
407
+ return [
408
+ null,
409
+ brandExternalAccount(
410
+ raw
411
+ )
412
+ ];
413
+ },
414
+ remove: async (externalAccountId) => {
415
+ if (!config.data) {
416
+ return stub("accounts.externalAccounts.remove");
417
+ }
418
+ const [err] = await tryCatch(
419
+ config.data.mutation(api.externalAccounts.mutations.removePersonal, {
420
+ externalAccountId
421
+ })
422
+ );
423
+ if (err) {
424
+ return [
425
+ fromConvexError(err),
426
+ null
427
+ ];
428
+ }
429
+ return [null, void 0];
430
+ }
431
+ };
432
+ }
74
433
  function createAccountsClient(config = {}) {
75
434
  return {
76
- retrieve: async () => stub("accounts.retrieve"),
435
+ retrieve: async (accountId) => {
436
+ if (!config.data) {
437
+ return stub("accounts.retrieve");
438
+ }
439
+ try {
440
+ const account = await config.data.query(
441
+ api.openfort.queries.getMyAccount,
442
+ {}
443
+ );
444
+ if (account.id !== accountId) {
445
+ return [
446
+ new CapxulError({
447
+ code: "PERMISSION_DENIED",
448
+ message: "accounts.retrieve currently supports the authenticated caller's own account only.",
449
+ details: {
450
+ requestedAccountId: accountId,
451
+ authenticatedAccountId: account.id
452
+ }
453
+ }),
454
+ null
455
+ ];
456
+ }
457
+ return [null, account];
458
+ } catch (cause) {
459
+ return [fromConvexError(cause), null];
460
+ }
461
+ },
77
462
  lookup: async () => stub("accounts.lookup"),
78
- update: async () => stub("accounts.update"),
463
+ update: async (input) => {
464
+ if (!config.data) {
465
+ return stub("accounts.update");
466
+ }
467
+ if (input.countryCode !== void 0) {
468
+ return [
469
+ new CapxulError({
470
+ code: "INVALID_INPUT",
471
+ message: "accounts.update does not support countryCode yet \u2014 backend mutation only patches displayName and username.",
472
+ details: { field: "countryCode" }
473
+ }),
474
+ null
475
+ ];
476
+ }
477
+ try {
478
+ const current = await config.data.query(
479
+ api.openfort.queries.getMyAccount,
480
+ {}
481
+ );
482
+ if (current.id !== input.accountId) {
483
+ return [
484
+ new CapxulError({
485
+ code: "PERMISSION_DENIED",
486
+ message: "accounts.update currently supports the authenticated caller's own account only.",
487
+ details: {
488
+ requestedAccountId: input.accountId,
489
+ authenticatedAccountId: current.id
490
+ }
491
+ }),
492
+ null
493
+ ];
494
+ }
495
+ await config.data.mutation(api.openfort.mutations.updateProfile, {
496
+ displayName: input.name,
497
+ username: input.username
498
+ });
499
+ const updated = await config.data.query(
500
+ api.openfort.queries.getMyAccount,
501
+ {}
502
+ );
503
+ return [null, updated];
504
+ } catch (cause) {
505
+ return [fromConvexError(cause), null];
506
+ }
507
+ },
79
508
  provisionPersonal: async (input) => {
80
509
  if (!config.data) {
81
510
  return stub(
@@ -155,18 +584,7 @@ function createAccountsClient(config = {}) {
155
584
  create: async () => stub("accounts.kycProfiles.create"),
156
585
  retrieve: async () => stub("accounts.kycProfiles.retrieve")
157
586
  },
158
- externalAccounts: {
159
- create: async () => stub(
160
- "accounts.externalAccounts.create"
161
- ),
162
- list: async () => stub(
163
- "accounts.externalAccounts.list"
164
- ),
165
- retrieve: async () => stub(
166
- "accounts.externalAccounts.retrieve"
167
- ),
168
- remove: async () => stub("accounts.externalAccounts.remove")
169
- },
587
+ externalAccounts: createAccountExternalAccountsClient(config),
170
588
  subAccounts: {
171
589
  create: async () => stub("accounts.subAccounts.create"),
172
590
  list: async () => stub("accounts.subAccounts.list"),
@@ -262,7 +680,29 @@ var Errors = {
262
680
  { details }
263
681
  ),
264
682
  emailDeliveryFailed: (detail) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`),
265
- internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`)
683
+ internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
684
+ /**
685
+ * Verification gate. Surfaced when a request hits a verification
686
+ * boundary the actor cannot cross under their current state. Two
687
+ * variants share this code:
688
+ *
689
+ * - Rail gate (Withdrawals v1 W2, #465): the resolved
690
+ * `external_account.kind` routes to a withdrawal rail (e.g.
691
+ * `fiat_offramp`, `card_payout`) that is not yet supported. Carries
692
+ * `details.rail` + `details.currentKind`.
693
+ * - KYC tier gate (legacy / future): the actor's KYC tier is below
694
+ * the required tier. Carries `details.requiredTier`.
695
+ *
696
+ * Code is shared because both expose the same UX shape ("you cannot
697
+ * proceed until verification advances"); the `details.*` keys
698
+ * differentiate the route.
699
+ */
700
+ verificationRequired: (details) => {
701
+ const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
702
+ return new CapxulError2("VERIFICATION_REQUIRED", message, {
703
+ details: { ...details }
704
+ });
705
+ }
266
706
  };
267
707
 
268
708
  // ../config/src/safe.ts
@@ -674,22 +1114,42 @@ function createOrgDocumentsClient() {
674
1114
  };
675
1115
  }
676
1116
 
677
- // src/core/external-accounts.ts
678
- function createExternalAccountsClient() {
679
- return {
680
- retrieve: async () => stub("externalAccounts.retrieve"),
681
- remove: async () => stub("externalAccounts.remove")
682
- };
683
- }
684
-
685
1117
  // src/core/me.ts
686
1118
  function createMeClient(config = {}) {
687
1119
  return {
688
1120
  get: async () => {
689
1121
  if (!config.data) {
690
- return stub("me.get");
1122
+ return stub("me.get");
1123
+ }
1124
+ try {
1125
+ const account = await config.data.query(
1126
+ api.openfort.queries.getMyAccount,
1127
+ {}
1128
+ );
1129
+ return [null, account];
1130
+ } catch (cause) {
1131
+ return [fromConvexError(cause), null];
1132
+ }
1133
+ },
1134
+ update: async (input) => {
1135
+ if (!config.data) {
1136
+ return stub("me.update");
1137
+ }
1138
+ if (input.countryCode !== void 0) {
1139
+ return [
1140
+ new CapxulError({
1141
+ code: "INVALID_INPUT",
1142
+ message: "me.update does not support countryCode yet \u2014 backend mutation only patches displayName and username.",
1143
+ details: { field: "countryCode" }
1144
+ }),
1145
+ null
1146
+ ];
691
1147
  }
692
1148
  try {
1149
+ await config.data.mutation(api.openfort.mutations.updateProfile, {
1150
+ displayName: input.name,
1151
+ username: input.username
1152
+ });
693
1153
  const account = await config.data.query(
694
1154
  api.openfort.queries.getMyAccount,
695
1155
  {}
@@ -698,8 +1158,7 @@ function createMeClient(config = {}) {
698
1158
  } catch (cause) {
699
1159
  return [fromConvexError(cause), null];
700
1160
  }
701
- },
702
- update: async () => stub("me.update")
1161
+ }
703
1162
  };
704
1163
  }
705
1164
 
@@ -1102,24 +1561,378 @@ function createOrgTransfersClient() {
1102
1561
  cancel: async () => stub("organizations.transfers.cancel")
1103
1562
  };
1104
1563
  }
1105
-
1106
- // src/core/withdrawals.ts
1107
- function createWithdrawalsClient() {
1564
+ function createWithdrawalsClient(config = {}) {
1108
1565
  return {
1109
- create: async () => stub("withdrawals.create"),
1110
- retrieve: async () => stub("withdrawals.retrieve"),
1111
- list: async () => stub("withdrawals.list")
1566
+ create: async (input) => {
1567
+ if (!config.data) {
1568
+ return stub("withdrawals.create");
1569
+ }
1570
+ const [createErr, createdRaw] = await tryCatch(
1571
+ config.data.mutation(api.withdrawals.mutations.create, {
1572
+ amount: input.amount,
1573
+ destination: {
1574
+ externalAccountId: input.destination.externalAccountId
1575
+ },
1576
+ source: input.source,
1577
+ reference: input.reference,
1578
+ idempotencyKey: input.idempotencyKey
1579
+ })
1580
+ );
1581
+ if (createErr) {
1582
+ return [mapCreateError2(fromConvexError(createErr)), null];
1583
+ }
1584
+ const created = createdRaw;
1585
+ if (!created) {
1586
+ return [
1587
+ new CapxulError({
1588
+ code: "NETWORK_ERROR",
1589
+ message: "withdrawals.create returned no withdrawal resource"
1590
+ }),
1591
+ null
1592
+ ];
1593
+ }
1594
+ if (created.status !== "processing" || created.operation.status !== "processing") {
1595
+ return [null, created];
1596
+ }
1597
+ if (!config.signer || !config.signing) {
1598
+ return [null, created];
1599
+ }
1600
+ const [signerErr, currentSigner] = await tryCatch(
1601
+ config.data.query(api.safe.queries.getMySignerAddress, {})
1602
+ );
1603
+ if (signerErr) {
1604
+ return await handleSubmissionFailure(
1605
+ { data: config.data },
1606
+ created.id,
1607
+ mapCreateError2(fromConvexError(signerErr))
1608
+ );
1609
+ }
1610
+ if (!currentSigner?.address) {
1611
+ return await handleSubmissionFailure(
1612
+ { data: config.data },
1613
+ created.id,
1614
+ new CapxulError({
1615
+ code: "PERMISSION_DENIED",
1616
+ message: "No signer is registered for the authenticated account.",
1617
+ details: { withdrawalId: created.id }
1618
+ })
1619
+ );
1620
+ }
1621
+ if (viem.getAddress(currentSigner.address) !== viem.getAddress(config.signer.address)) {
1622
+ return await handleSubmissionFailure(
1623
+ { data: config.data },
1624
+ created.id,
1625
+ new CapxulError({
1626
+ code: "PERMISSION_DENIED",
1627
+ message: "Configured signer does not match the authenticated account signer.",
1628
+ details: {
1629
+ withdrawalId: created.id,
1630
+ expectedSignerAddress: currentSigner.address,
1631
+ actualSignerAddress: config.signer.address
1632
+ }
1633
+ })
1634
+ );
1635
+ }
1636
+ const [prepErr, submission] = await tryCatch(
1637
+ config.data.query(api.withdrawals.queries.prepareSubmission, {
1638
+ withdrawalId: created.id
1639
+ })
1640
+ );
1641
+ if (prepErr) {
1642
+ return await handleSubmissionFailure(
1643
+ { data: config.data },
1644
+ created.id,
1645
+ mapCreateError2(fromConvexError(prepErr))
1646
+ );
1647
+ }
1648
+ const destinationAddress = submission?.destinationAddress;
1649
+ if (!submission || !destinationAddress) {
1650
+ return await handleSubmissionFailure(
1651
+ { data: config.data },
1652
+ created.id,
1653
+ new CapxulError({
1654
+ code: "NETWORK_ERROR",
1655
+ message: "withdrawals.prepareSubmission returned no destination.",
1656
+ details: { withdrawalId: created.id }
1657
+ })
1658
+ );
1659
+ }
1660
+ const [transferErr, transferOk] = await tryCatch(
1661
+ transferAsOwner(
1662
+ {
1663
+ signer: config.signer,
1664
+ signing: config.signing
1665
+ },
1666
+ {
1667
+ tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
1668
+ recipientAddress: destinationAddress,
1669
+ amount: toTokenUnits(submission.amount.value, 6)
1670
+ }
1671
+ )
1672
+ );
1673
+ if (transferErr) {
1674
+ return await handleSubmissionFailure(
1675
+ { data: config.data },
1676
+ created.id,
1677
+ mapCreateError2(fromConvexError(transferErr))
1678
+ );
1679
+ }
1680
+ if (!transferOk.success) {
1681
+ return await handleSubmissionFailure(
1682
+ { data: config.data },
1683
+ created.id,
1684
+ new CapxulError({
1685
+ code: "NETWORK_ERROR",
1686
+ message: "Bundler submission did not succeed.",
1687
+ details: {
1688
+ withdrawalId: created.id,
1689
+ txHash: transferOk.txHash,
1690
+ userOpHash: transferOk.userOpHash
1691
+ }
1692
+ })
1693
+ );
1694
+ }
1695
+ const [recordErr] = await tryCatch(
1696
+ config.data.mutation(api.withdrawals.mutations.recordSubmitted, {
1697
+ withdrawalId: created.id,
1698
+ txHash: transferOk.txHash,
1699
+ userOpHash: transferOk.userOpHash
1700
+ })
1701
+ );
1702
+ if (recordErr) {
1703
+ return [
1704
+ new CapxulError({
1705
+ code: "NETWORK_ERROR",
1706
+ message: "Withdrawal was submitted on-chain, but backend submission tracking failed.",
1707
+ cause: recordErr,
1708
+ details: {
1709
+ withdrawalId: created.id,
1710
+ txHash: transferOk.txHash,
1711
+ userOpHash: transferOk.userOpHash
1712
+ }
1713
+ }),
1714
+ null
1715
+ ];
1716
+ }
1717
+ return [null, created];
1718
+ },
1719
+ retrieve: async (withdrawalId) => {
1720
+ if (!config.data) {
1721
+ return stub("withdrawals.retrieve");
1722
+ }
1723
+ const [err, raw] = await tryCatch(
1724
+ config.data.query(api.withdrawals.queries.retrieve, { withdrawalId })
1725
+ );
1726
+ if (err) {
1727
+ return [fromConvexError(err), null];
1728
+ }
1729
+ const withdrawal = raw;
1730
+ if (!withdrawal) {
1731
+ return [
1732
+ new CapxulError({
1733
+ code: "NOT_FOUND",
1734
+ message: `withdrawal ${withdrawalId} not found`
1735
+ }),
1736
+ null
1737
+ ];
1738
+ }
1739
+ return [null, withdrawal];
1740
+ },
1741
+ list: async (input) => {
1742
+ if (!config.data) {
1743
+ return stub("withdrawals.list");
1744
+ }
1745
+ const [err, raw] = await tryCatch(
1746
+ config.data.query(api.withdrawals.queries.list, {
1747
+ limit: input?.limit,
1748
+ cursor: input?.cursor
1749
+ })
1750
+ );
1751
+ if (err) {
1752
+ return [fromConvexError(err), null];
1753
+ }
1754
+ return [null, raw];
1755
+ },
1756
+ recordCompleted: async (input) => {
1757
+ if (!config.data) {
1758
+ return stub(
1759
+ "withdrawals.recordCompleted"
1760
+ );
1761
+ }
1762
+ const [err] = await tryCatch(
1763
+ config.data.mutation(api.withdrawals.mutations.recordCompleted, {
1764
+ withdrawalId: input.withdrawalId,
1765
+ txHash: input.txHash
1766
+ })
1767
+ );
1768
+ if (err) {
1769
+ return [
1770
+ mapRecordCompletedError(fromConvexError(err)),
1771
+ null
1772
+ ];
1773
+ }
1774
+ return [null, null];
1775
+ }
1112
1776
  };
1113
1777
  }
1114
- function createOrgWithdrawalsClient() {
1778
+ function createOrgWithdrawalsClient(config = {}) {
1115
1779
  return {
1116
- create: async () => stub(
1117
- "organizations.withdrawals.create"
1118
- ),
1119
- retrieve: async () => stub("organizations.withdrawals.retrieve"),
1120
- list: async () => stub("organizations.withdrawals.list")
1780
+ /**
1781
+ * Org-scope create (Withdrawals v1 W2, #465).
1782
+ *
1783
+ * D6 returns the `processing` row only. No `transferAsOwner`
1784
+ * tail, no `recordSubmitted` call. Org Safe + Zodiac submission
1785
+ * orchestration ships in W3+.
1786
+ */
1787
+ create: async (input) => {
1788
+ if (!config.data) {
1789
+ return stub(
1790
+ "organizations.withdrawals.create"
1791
+ );
1792
+ }
1793
+ const [err, raw] = await tryCatch(
1794
+ config.data.mutation(api.withdrawals.mutations.createOrg, {
1795
+ organizationId: input.organizationId,
1796
+ amount: input.amount,
1797
+ destination: {
1798
+ externalAccountId: input.destination.externalAccountId
1799
+ },
1800
+ source: input.source,
1801
+ reference: input.reference,
1802
+ idempotencyKey: input.idempotencyKey
1803
+ })
1804
+ );
1805
+ if (err) {
1806
+ return [mapCreateError2(fromConvexError(err)), null];
1807
+ }
1808
+ const created = raw;
1809
+ if (!created) {
1810
+ return [
1811
+ new CapxulError({
1812
+ code: "NETWORK_ERROR",
1813
+ message: "organizations.withdrawals.create returned no withdrawal resource"
1814
+ }),
1815
+ null
1816
+ ];
1817
+ }
1818
+ return [null, created];
1819
+ },
1820
+ retrieve: async (input) => {
1821
+ if (!config.data) {
1822
+ return stub(
1823
+ "organizations.withdrawals.retrieve"
1824
+ );
1825
+ }
1826
+ const [err, raw] = await tryCatch(
1827
+ config.data.query(api.withdrawals.queries.retrieve, {
1828
+ withdrawalId: input.withdrawalId
1829
+ })
1830
+ );
1831
+ if (err) {
1832
+ return [fromConvexError(err), null];
1833
+ }
1834
+ const withdrawal = raw;
1835
+ if (!withdrawal) {
1836
+ return [
1837
+ new CapxulError({
1838
+ code: "NOT_FOUND",
1839
+ message: `withdrawal ${input.withdrawalId} not found`
1840
+ }),
1841
+ null
1842
+ ];
1843
+ }
1844
+ const ownerCheck = withdrawal.owner;
1845
+ if (ownerCheck?.type !== "organization" || ownerCheck.id !== input.organizationId) {
1846
+ return [
1847
+ new CapxulError({
1848
+ code: "NOT_FOUND",
1849
+ message: `withdrawal ${input.withdrawalId} does not belong to organization ${input.organizationId}`
1850
+ }),
1851
+ null
1852
+ ];
1853
+ }
1854
+ return [null, withdrawal];
1855
+ },
1856
+ list: async (input) => {
1857
+ if (!config.data) {
1858
+ return stub(
1859
+ "organizations.withdrawals.list"
1860
+ );
1861
+ }
1862
+ const [err, raw] = await tryCatch(
1863
+ config.data.query(api.withdrawals.queries.listOrg, {
1864
+ organizationId: input.organizationId,
1865
+ limit: input.limit,
1866
+ cursor: input.cursor
1867
+ })
1868
+ );
1869
+ if (err) {
1870
+ return [fromConvexError(err), null];
1871
+ }
1872
+ return [null, raw];
1873
+ }
1121
1874
  };
1122
1875
  }
1876
+ async function handleSubmissionFailure(config, withdrawalId, error) {
1877
+ await bestEffortMarkFailed2(config, withdrawalId, error);
1878
+ return [error, null];
1879
+ }
1880
+ async function bestEffortMarkFailed2(config, withdrawalId, error) {
1881
+ await tryCatch(
1882
+ config.data.mutation(api.withdrawals.mutations.markFailed, {
1883
+ withdrawalId,
1884
+ errorCode: error.code,
1885
+ errorMessage: error.message
1886
+ })
1887
+ );
1888
+ }
1889
+ function mapCreateError2(error) {
1890
+ switch (error.code) {
1891
+ case "NOT_AUTHENTICATED":
1892
+ case "PERMISSION_DENIED":
1893
+ case "INVALID_INPUT":
1894
+ case "INSUFFICIENT_BALANCE":
1895
+ case "IDEMPOTENCY_CONFLICT":
1896
+ case "KYC_REQUIRED":
1897
+ case "POLICY_DENIED":
1898
+ case "RATE_LIMITED":
1899
+ case "NETWORK_ERROR":
1900
+ case "NOT_FOUND":
1901
+ case "VERIFICATION_REQUIRED":
1902
+ return error;
1903
+ default:
1904
+ return new CapxulError({
1905
+ code: "NETWORK_ERROR",
1906
+ message: error.message,
1907
+ cause: error,
1908
+ details: error.details,
1909
+ operationId: error.operationId,
1910
+ correlationId: error.correlationId,
1911
+ retryable: error.retryable
1912
+ });
1913
+ }
1914
+ }
1915
+ function mapRecordCompletedError(error) {
1916
+ switch (error.code) {
1917
+ case "NOT_AUTHENTICATED":
1918
+ case "PERMISSION_DENIED":
1919
+ case "INVALID_INPUT":
1920
+ case "NOT_FOUND":
1921
+ case "NETWORK_ERROR":
1922
+ case "INTERNAL_ERROR":
1923
+ return error;
1924
+ default:
1925
+ return new CapxulError({
1926
+ code: "NETWORK_ERROR",
1927
+ message: error.message,
1928
+ cause: error,
1929
+ details: error.details,
1930
+ operationId: error.operationId,
1931
+ correlationId: error.correlationId,
1932
+ retryable: error.retryable
1933
+ });
1934
+ }
1935
+ }
1123
1936
 
1124
1937
  // src/core/webhook-endpoints.ts
1125
1938
  function createWebhookEndpointsClient() {
@@ -1140,8 +1953,138 @@ function createWebhookEventsClient() {
1140
1953
  list: async () => stub("webhookEvents.list")
1141
1954
  };
1142
1955
  }
1143
-
1144
- // src/core/organizations.ts
1956
+
1957
+ // src/core/organizations.ts
1958
+ function createOrgExternalAccountsClient(config) {
1959
+ return {
1960
+ create: async (input) => {
1961
+ if (!config.data) {
1962
+ return stub(
1963
+ "organizations.externalAccounts.create"
1964
+ );
1965
+ }
1966
+ const [err, raw] = await tryCatch(
1967
+ config.data.mutation(api.externalAccounts.mutations.createOrg, {
1968
+ organizationId: input.organizationId,
1969
+ kind: input.kind,
1970
+ label: input.label,
1971
+ address: input.address,
1972
+ iban: input.iban,
1973
+ bic: input.bic,
1974
+ accountHolder: input.accountHolder,
1975
+ network: input.network,
1976
+ panToken: input.panToken,
1977
+ last4: input.last4
1978
+ })
1979
+ );
1980
+ if (err) {
1981
+ return [
1982
+ fromConvexError(err),
1983
+ null
1984
+ ];
1985
+ }
1986
+ if (!raw) {
1987
+ return [
1988
+ new CapxulError({
1989
+ code: "NOT_FOUND",
1990
+ message: "external_account creation returned no resource"
1991
+ }),
1992
+ null
1993
+ ];
1994
+ }
1995
+ return [
1996
+ null,
1997
+ brandExternalAccount(
1998
+ raw
1999
+ )
2000
+ ];
2001
+ },
2002
+ list: async (input) => {
2003
+ if (!config.data) {
2004
+ return stub(
2005
+ "organizations.externalAccounts.list"
2006
+ );
2007
+ }
2008
+ const [err, result] = await tryCatch(
2009
+ config.data.query(api.externalAccounts.queries.listOrg, {
2010
+ organizationId: input.organizationId,
2011
+ limit: input.limit,
2012
+ cursor: input.cursor
2013
+ })
2014
+ );
2015
+ if (err) {
2016
+ return [fromConvexError(err), null];
2017
+ }
2018
+ const branded = result.data.map(
2019
+ (row) => brandExternalAccount(
2020
+ row
2021
+ )
2022
+ );
2023
+ return [
2024
+ null,
2025
+ {
2026
+ object: "list",
2027
+ data: branded,
2028
+ page: result.page
2029
+ }
2030
+ ];
2031
+ },
2032
+ retrieve: async (input) => {
2033
+ if (!config.data) {
2034
+ return stub(
2035
+ "organizations.externalAccounts.retrieve"
2036
+ );
2037
+ }
2038
+ const [err, raw] = await tryCatch(
2039
+ config.data.query(api.externalAccounts.queries.retrieveOrg, {
2040
+ organizationId: input.organizationId,
2041
+ externalAccountId: input.externalAccountId
2042
+ })
2043
+ );
2044
+ if (err) {
2045
+ return [
2046
+ fromConvexError(err),
2047
+ null
2048
+ ];
2049
+ }
2050
+ if (!raw) {
2051
+ return [
2052
+ new CapxulError({
2053
+ code: "NOT_FOUND",
2054
+ message: `external_account ${input.externalAccountId} not found`
2055
+ }),
2056
+ null
2057
+ ];
2058
+ }
2059
+ return [
2060
+ null,
2061
+ brandExternalAccount(
2062
+ raw
2063
+ )
2064
+ ];
2065
+ },
2066
+ remove: async (input) => {
2067
+ if (!config.data) {
2068
+ return stub(
2069
+ "organizations.externalAccounts.remove"
2070
+ );
2071
+ }
2072
+ const [err] = await tryCatch(
2073
+ config.data.mutation(api.externalAccounts.mutations.removeOrg, {
2074
+ organizationId: input.organizationId,
2075
+ externalAccountId: input.externalAccountId
2076
+ })
2077
+ );
2078
+ if (err) {
2079
+ return [
2080
+ fromConvexError(err),
2081
+ null
2082
+ ];
2083
+ }
2084
+ return [null, void 0];
2085
+ }
2086
+ };
2087
+ }
1145
2088
  function createOrganizationsClient(config = {}) {
1146
2089
  return {
1147
2090
  create: async () => stub("organizations.create"),
@@ -1197,18 +2140,7 @@ function createOrganizationsClient(config = {}) {
1197
2140
  retrieve: async () => stub("organizations.subAccounts.retrieve"),
1198
2141
  remove: async () => stub("organizations.subAccounts.remove")
1199
2142
  },
1200
- externalAccounts: {
1201
- create: async () => stub(
1202
- "organizations.externalAccounts.create"
1203
- ),
1204
- list: async () => stub(
1205
- "organizations.externalAccounts.list"
1206
- ),
1207
- retrieve: async () => stub(
1208
- "organizations.externalAccounts.retrieve"
1209
- ),
1210
- remove: async () => stub("organizations.externalAccounts.remove")
1211
- },
2143
+ externalAccounts: createOrgExternalAccountsClient(config),
1212
2144
  balanceLedger: {
1213
2145
  list: async () => stub(
1214
2146
  "organizations.balanceLedger.list"
@@ -1219,7 +2151,7 @@ function createOrganizationsClient(config = {}) {
1219
2151
  },
1220
2152
  payments: createOrgPaymentsClient(),
1221
2153
  transfers: createOrgTransfersClient(),
1222
- withdrawals: createOrgWithdrawalsClient(),
2154
+ withdrawals: createOrgWithdrawalsClient(config),
1223
2155
  documents: createOrgDocumentsClient(),
1224
2156
  webhookEndpoints: createWebhookEndpointsClient(),
1225
2157
  webhookEvents: createWebhookEventsClient()
@@ -1234,6 +2166,96 @@ function createSubAccountsClient() {
1234
2166
  };
1235
2167
  }
1236
2168
 
2169
+ // src/core/token-transfers.ts
2170
+ var toTokenTransferId = (raw) => {
2171
+ if (typeof raw !== "string" || raw.length === 0) {
2172
+ throw new Error(
2173
+ `Invalid tokenTransferId: expected non-empty string, got ${String(raw)}`
2174
+ );
2175
+ }
2176
+ return raw;
2177
+ };
2178
+ function brandRow(row) {
2179
+ return {
2180
+ ...row,
2181
+ id: toTokenTransferId(row.id)
2182
+ };
2183
+ }
2184
+ function createTokenTransfersClient(config = {}) {
2185
+ return {
2186
+ list: async (input) => {
2187
+ if (!config.data) {
2188
+ return stub("tokenTransfers.list");
2189
+ }
2190
+ try {
2191
+ const raw = await config.data.query(
2192
+ api.tokenTransfers.queries.list,
2193
+ {
2194
+ limit: input?.limit,
2195
+ cursor: input?.cursor,
2196
+ direction: input?.direction
2197
+ }
2198
+ );
2199
+ if (!raw) {
2200
+ return [
2201
+ new CapxulError({
2202
+ code: "NOT_AUTHENTICATED",
2203
+ message: "tokenTransfers.list requires an authenticated session."
2204
+ }),
2205
+ null
2206
+ ];
2207
+ }
2208
+ return [
2209
+ null,
2210
+ {
2211
+ object: "list",
2212
+ data: raw.items.map(brandRow),
2213
+ page: {
2214
+ hasMore: raw.hasMore,
2215
+ nextCursor: raw.nextCursor
2216
+ },
2217
+ displayCurrency: raw.displayCurrency
2218
+ }
2219
+ ];
2220
+ } catch (cause) {
2221
+ return [fromConvexError(cause), null];
2222
+ }
2223
+ },
2224
+ retrieve: async (input) => {
2225
+ if (!config.data) {
2226
+ return stub("tokenTransfers.retrieve");
2227
+ }
2228
+ try {
2229
+ const raw = await config.data.query(
2230
+ api.tokenTransfers.queries.getByTxLogIndex,
2231
+ {
2232
+ txHash: input.txHash,
2233
+ logIndex: input.logIndex,
2234
+ chainId: input.chainId
2235
+ }
2236
+ );
2237
+ if (!raw) {
2238
+ return [
2239
+ new CapxulError({
2240
+ code: "NOT_FOUND",
2241
+ message: `tokenTransfer (txHash=${input.txHash}, logIndex=${input.logIndex}) not found or not visible to caller.`,
2242
+ details: {
2243
+ txHash: input.txHash,
2244
+ logIndex: input.logIndex,
2245
+ chainId: input.chainId
2246
+ }
2247
+ }),
2248
+ null
2249
+ ];
2250
+ }
2251
+ return [null, brandRow(raw)];
2252
+ } catch (cause) {
2253
+ return [fromConvexError(cause), null];
2254
+ }
2255
+ }
2256
+ };
2257
+ }
2258
+
1237
2259
  // src/core/virtual-accounts.ts
1238
2260
  function createVirtualAccountsClient() {
1239
2261
  return {
@@ -1255,59 +2277,6 @@ function createVirtualCardsClient() {
1255
2277
  cancel: async () => stub("virtualCards.cancel")
1256
2278
  };
1257
2279
  }
1258
-
1259
- // ../observability/src/try-catch.ts
1260
- async function tryCatch(promise) {
1261
- try {
1262
- return [null, await promise];
1263
- } catch (e) {
1264
- return [e instanceof Error ? e : new Error(String(e)), null];
1265
- }
1266
- }
1267
-
1268
- // ../observability/src/debug-log.ts
1269
- function isDevelopmentBuild() {
1270
- if (typeof process === "undefined") {
1271
- return false;
1272
- }
1273
- return process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test";
1274
- }
1275
- function debugLog(line) {
1276
- if (!isDevelopmentBuild()) return;
1277
- if (typeof globalThis !== "undefined" && "window" in globalThis && typeof console?.info === "function") {
1278
- console.info(line);
1279
- return;
1280
- }
1281
- if (typeof process !== "undefined" && typeof process.stderr?.write === "function") {
1282
- process.stderr.write(`${line}
1283
- `);
1284
- }
1285
- }
1286
- function formatDebugValue(value) {
1287
- if (value === void 0 || value === "") return "";
1288
- if (typeof value === "string") return value;
1289
- try {
1290
- return JSON.stringify(value);
1291
- } catch {
1292
- return String(value);
1293
- }
1294
- }
1295
- function track(...args) {
1296
- const [name, props] = args;
1297
- debugLog(`[TRACK] ${name} ${formatDebugValue(props ?? "")}`);
1298
- }
1299
- function formatDebugValue2(value) {
1300
- if (value === void 0 || value === "") return "";
1301
- if (typeof value === "string") return value;
1302
- try {
1303
- return JSON.stringify(value);
1304
- } catch {
1305
- return String(value);
1306
- }
1307
- }
1308
- function identify(userId, traits) {
1309
- debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
1310
- }
1311
2280
  function createAuthFlowMachine(client) {
1312
2281
  return xstate.setup({
1313
2282
  types: {},
@@ -1583,122 +2552,6 @@ function emailDomain(email) {
1583
2552
  const domain = email.split("@")[1]?.trim().toLowerCase();
1584
2553
  return domain || "unknown";
1585
2554
  }
1586
-
1587
- // ../platform-kernel/src/ids.ts
1588
- function makePrefixedIdConstructor(prefix, fieldName) {
1589
- const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
1590
- return (raw) => {
1591
- if (typeof raw !== "string" || !re.test(raw)) {
1592
- throw new Error(
1593
- `Invalid ${fieldName}: expected string matching ^${prefix}_[A-Za-z0-9_\\-]+$, got ${String(raw)}`
1594
- );
1595
- }
1596
- return raw;
1597
- };
1598
- }
1599
- var toAccountId = makePrefixedIdConstructor(
1600
- "acct",
1601
- "accountId"
1602
- );
1603
- var toOrganizationId = makePrefixedIdConstructor("org", "organizationId");
1604
- var toMemberId = makePrefixedIdConstructor(
1605
- "mem",
1606
- "memberId"
1607
- );
1608
- var toSafeId = makePrefixedIdConstructor(
1609
- "safe",
1610
- "safeId"
1611
- );
1612
- var toTreasuryId = makePrefixedIdConstructor(
1613
- "try",
1614
- "treasuryId"
1615
- );
1616
- var toApiKeyId = makePrefixedIdConstructor(
1617
- "ak",
1618
- "apiKeyId"
1619
- );
1620
- var toOperationId = makePrefixedIdConstructor(
1621
- "op",
1622
- "operationId"
1623
- );
1624
- var toKycProfileId = makePrefixedIdConstructor(
1625
- "kyc",
1626
- "kycProfileId"
1627
- );
1628
- var toKybProfileId = makePrefixedIdConstructor(
1629
- "kyb",
1630
- "kybProfileId"
1631
- );
1632
- var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
1633
- var toPaymentId = makePrefixedIdConstructor(
1634
- "pay",
1635
- "paymentId"
1636
- );
1637
- var toWithdrawalId = makePrefixedIdConstructor(
1638
- "wd",
1639
- "withdrawalId"
1640
- );
1641
- var toWebhookEndpointId = makePrefixedIdConstructor("we", "webhookEndpointId");
1642
- var toWebhookEventId = makePrefixedIdConstructor("evt", "webhookEventId");
1643
- var toSubAccountId = makePrefixedIdConstructor(
1644
- "sub",
1645
- "subAccountId"
1646
- );
1647
- var toVirtualAccountId = makePrefixedIdConstructor("va", "virtualAccountId");
1648
- var toVirtualCardId = makePrefixedIdConstructor(
1649
- "vc",
1650
- "virtualCardId"
1651
- );
1652
- var toTransferId = makePrefixedIdConstructor(
1653
- "txfr",
1654
- "transferId"
1655
- );
1656
- var toDocumentId = makePrefixedIdConstructor(
1657
- "doc",
1658
- "documentId"
1659
- );
1660
- var toBalanceLedgerEntryId = makePrefixedIdConstructor("bal", "balanceLedgerEntryId");
1661
-
1662
- // ../platform-kernel/src/value-objects.ts
1663
- var PHONE_NUMBER_RE = /^\+[1-9]\d{1,14}$/;
1664
- function toPhoneNumber(raw) {
1665
- if (typeof raw !== "string" || !PHONE_NUMBER_RE.test(raw)) {
1666
- throw new Error(
1667
- `Invalid phoneNumber: expected E.164 string matching ^\\+[1-9]\\d{1,14}$, got ${String(raw)}`
1668
- );
1669
- }
1670
- return raw;
1671
- }
1672
- var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1673
- function toEmail(raw) {
1674
- if (typeof raw !== "string") {
1675
- throw new Error(
1676
- `Invalid email: expected string, got ${String(raw)}`
1677
- );
1678
- }
1679
- const lowered = raw.trim().toLowerCase();
1680
- if (!EMAIL_RE.test(lowered)) {
1681
- throw new Error(
1682
- `Invalid email: expected local@domain.tld, got ${String(raw)}`
1683
- );
1684
- }
1685
- return lowered;
1686
- }
1687
- var USERNAME_RE = /^[a-z][a-z0-9_-]{2,29}$/;
1688
- function toUsername(raw) {
1689
- if (typeof raw !== "string") {
1690
- throw new Error(
1691
- `Invalid username: expected string, got ${String(raw)}`
1692
- );
1693
- }
1694
- const lowered = raw.toLowerCase();
1695
- if (!USERNAME_RE.test(lowered)) {
1696
- throw new Error(
1697
- `Invalid username: expected 3-30 chars, letter-first, [a-z0-9_-], got ${String(raw)}`
1698
- );
1699
- }
1700
- return lowered;
1701
- }
1702
2555
  function createProvisioningMachine(client) {
1703
2556
  return xstate.setup({
1704
2557
  types: {},
@@ -2085,12 +2938,13 @@ function createCapxulClient(config = {}) {
2085
2938
  organizations: createOrganizationsClient(config),
2086
2939
  payments: createPaymentsClient(config),
2087
2940
  transfers: createTransfersClient(),
2088
- withdrawals: createWithdrawalsClient(),
2941
+ tokenTransfers: createTokenTransfersClient(config),
2942
+ withdrawals: createWithdrawalsClient(config),
2089
2943
  documents: createDocumentsClient(),
2090
2944
  subAccounts: createSubAccountsClient(),
2091
2945
  virtualAccounts: createVirtualAccountsClient(),
2092
2946
  virtualCards: createVirtualCardsClient(),
2093
- externalAccounts: createExternalAccountsClient(),
2947
+ externalAccounts: createExternalAccountsClient(config),
2094
2948
  operations: createOperationsClient(config),
2095
2949
  webhookEndpoints: createWebhookEndpointsClient(),
2096
2950
  webhookEvents: createWebhookEventsClient(),
@@ -2228,6 +3082,7 @@ exports.toPaymentId = toPaymentId;
2228
3082
  exports.toPhoneNumber = toPhoneNumber;
2229
3083
  exports.toSafeId = toSafeId;
2230
3084
  exports.toSubAccountId = toSubAccountId;
3085
+ exports.toTokenTransferId = toTokenTransferId;
2231
3086
  exports.toTransferId = toTransferId;
2232
3087
  exports.toTreasuryId = toTreasuryId;
2233
3088
  exports.toUsername = toUsername;