@capxul/sdk 0.1.0-alpha.8 → 0.2.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { componentsGeneric, anyApi } from 'convex/server';
2
- import { getAddress, parseUnits, createPublicClient, http, encodeFunctionData } from 'viem';
3
2
  import { toSafeSmartAccount } from 'permissionless/accounts';
3
+ import { getAddress, parseUnits, createPublicClient, http, encodeFunctionData } from 'viem';
4
4
  import { entryPoint07Address, createPaymasterClient, createBundlerClient } from 'viem/account-abstraction';
5
5
  import { baseSepolia } from 'viem/chains';
6
+ import { deriveSafeAddress as deriveSafeAddress$1, defaultSafeDeriveConfig } from '@repo/safe-derive';
7
+ import { ConvexHttpClient } from 'convex/browser';
6
8
  import { setup, fromPromise, assign } from 'xstate';
7
9
 
8
10
  // src/_generated/api.js
@@ -120,6 +122,159 @@ function identify(userId, traits) {
120
122
  debugLog(`[IDENTIFY] ${userId} ${formatDebugValue2(traits ?? "")}`);
121
123
  }
122
124
 
125
+ // ../config/src/chain.ts
126
+ var CAPXUL_PAYMENTS_ADDRESS = "0xe740ea65521edc9bef0ea75d30b5334711db99f4";
127
+ var TEST_USDC_ADDRESS = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
128
+ var CAPXUL_API_BASE_URL = "https://elated-oyster-269.convex.site";
129
+
130
+ // ../config/src/timing.ts
131
+ var FLOW_INVOKE_TIMEOUT_MS = 3e4;
132
+
133
+ // ../config/src/errors.ts
134
+ var CapxulError2 = class extends Error {
135
+ code;
136
+ details;
137
+ correlationId;
138
+ layer;
139
+ constructor(code, message, options) {
140
+ super(message, options?.cause ? { cause: options.cause } : void 0);
141
+ this.code = code;
142
+ this.details = options?.details;
143
+ this.correlationId = options?.correlationId;
144
+ this.layer = options?.layer;
145
+ }
146
+ };
147
+ var Errors = {
148
+ notAuthenticated: () => new CapxulError2("NOT_AUTHENTICATED", "Not authenticated"),
149
+ profileNotFound: (authUserId) => new CapxulError2("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`),
150
+ smartAccountMissing: (authUserId) => new CapxulError2("SMART_ACCOUNT_MISSING", `Smart account not provisioned for user ${authUserId}`),
151
+ envMissing: (name) => new CapxulError2("ENV_MISSING", `Environment variable ${name} not configured`),
152
+ openfortApi: (operation, cause) => new CapxulError2(
153
+ "PROVIDER_ERROR",
154
+ `Openfort ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
155
+ { cause, details: { provider: "openfort", operation } }
156
+ ),
157
+ shieldApi: (status, detail) => new CapxulError2(
158
+ "PROVIDER_ERROR",
159
+ `Shield API error (${status}): ${detail}`,
160
+ { details: { provider: "shield", status } }
161
+ ),
162
+ providerError: (provider, operation, cause) => (
163
+ // Public `message` is redacted to a fixed shape so provider-side
164
+ // exception text never leaks to the client. The original `cause`
165
+ // is preserved on `Error.cause` for server-side debugging via
166
+ // observability sinks (Sentry, console traces).
167
+ new CapxulError2(
168
+ "PROVIDER_ERROR",
169
+ `Provider error: ${provider} ${operation}`,
170
+ { cause, details: { provider, operation } }
171
+ )
172
+ ),
173
+ invalidInput: (field, reason) => new CapxulError2(
174
+ "INVALID_INPUT",
175
+ `Invalid ${field}: ${reason}`,
176
+ { details: { field, reason } }
177
+ ),
178
+ playerNotFound: (playerId) => new CapxulError2(
179
+ "PLAYER_NOT_FOUND",
180
+ playerId ? `Openfort player ${playerId} not found` : "Openfort player not found"
181
+ ),
182
+ accountNotFound: (accountId) => new CapxulError2(
183
+ "ACCOUNT_NOT_FOUND",
184
+ accountId ? `Openfort account ${accountId} not found` : "Openfort smart account not found"
185
+ ),
186
+ invalidRecipient: (reason) => new CapxulError2("INVALID_RECIPIENT", reason),
187
+ permissionDenied: (reason = "Permission denied") => new CapxulError2("PERMISSION_DENIED", reason),
188
+ notFound: (resource, id) => new CapxulError2(
189
+ "NOT_FOUND",
190
+ id ? `${resource} ${id} not found` : `${resource} not found`
191
+ ),
192
+ idempotencyConflict: (details) => new CapxulError2(
193
+ "IDEMPOTENCY_CONFLICT",
194
+ "Idempotency key was already used for a different request",
195
+ { details }
196
+ ),
197
+ emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
198
+ details
199
+ }),
200
+ rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
201
+ details: { ...details }
202
+ }),
203
+ internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
204
+ /**
205
+ * Verification gate. Surfaced when a request hits a verification
206
+ * boundary the actor cannot cross under their current state. Two
207
+ * variants share this code:
208
+ *
209
+ * - Rail gate (Withdrawals v1 W2, #465): the resolved
210
+ * `external_account.kind` routes to a withdrawal rail (e.g.
211
+ * `fiat_offramp`, `card_payout`) that is not yet supported. Carries
212
+ * `details.rail` + `details.currentKind`.
213
+ * - KYC tier gate (legacy / future): the actor's KYC tier is below
214
+ * the required tier. Carries `details.requiredTier`.
215
+ *
216
+ * Code is shared because both expose the same UX shape ("you cannot
217
+ * proceed until verification advances"); the `details.*` keys
218
+ * differentiate the route.
219
+ */
220
+ verificationRequired: (details) => {
221
+ const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
222
+ return new CapxulError2("VERIFICATION_REQUIRED", message, {
223
+ details: { ...details }
224
+ });
225
+ }
226
+ };
227
+
228
+ // ../config/src/safe.ts
229
+ var SAFE_PROXY_FACTORY = "0x4e1dcf7ad4e460cfd30791ccc4f9c8a4f820ec67";
230
+ var SAFE_L2_SINGLETON = "0x29fcb43b46531bca003ddc8fcb67ffe91900c762";
231
+ var SAFE_MODULE_SETUP = "0x2dd68b007b46fbe91b9a7c3eda5a7a1063cb5b47";
232
+ var SAFE_4337_MODULE = "0x75cf11467937ce3f2f357ce24ffc3dbf8fd5c226";
233
+
234
+ // ../config/src/org-roles.ts
235
+ function roleKeyFromLabel(label) {
236
+ const bytes = new TextEncoder().encode(label);
237
+ const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
238
+ return "0x" + hex.padEnd(64, "0");
239
+ }
240
+ roleKeyFromLabel("OWNER");
241
+ roleKeyFromLabel("FINANCE_MANAGER");
242
+ roleKeyFromLabel("PAYMENTS_OPERATOR");
243
+ async function buildSafeAccount(signer, chain) {
244
+ try {
245
+ const publicClient = createPublicClient({
246
+ chain: baseSepolia,
247
+ transport: http(chain.rpcUrl)
248
+ });
249
+ return await toSafeSmartAccount({
250
+ client: publicClient,
251
+ entryPoint: { address: entryPoint07Address, version: "0.7" },
252
+ version: "1.4.1",
253
+ owners: [signer],
254
+ saltNonce: computeSaltNonce(signer.address),
255
+ safeSingletonAddress: SAFE_L2_SINGLETON,
256
+ safeProxyFactoryAddress: SAFE_PROXY_FACTORY,
257
+ safeModuleSetupAddress: SAFE_MODULE_SETUP,
258
+ safe4337ModuleAddress: SAFE_4337_MODULE,
259
+ safeModules: [],
260
+ setupTransactions: []
261
+ });
262
+ } catch (cause) {
263
+ throw new CapxulError({
264
+ code: "NETWORK_ERROR",
265
+ message: cause instanceof Error ? cause.message : String(cause),
266
+ cause,
267
+ details: { chainId: chain.chainId }
268
+ });
269
+ }
270
+ }
271
+ function computeSaltNonce(ownerAddress) {
272
+ return BigInt(`0x${ownerAddress.toLowerCase().slice(2).padEnd(64, "0")}`);
273
+ }
274
+ function deriveSafeAddress(signerAddress) {
275
+ return deriveSafeAddress$1(signerAddress, defaultSafeDeriveConfig);
276
+ }
277
+
123
278
  // ../platform-kernel/src/ids.ts
124
279
  function makePrefixedIdConstructor(prefix, fieldName) {
125
280
  const re = new RegExp(`^${prefix}_[A-Za-z0-9_\\-]+$`);
@@ -132,12 +287,24 @@ function makePrefixedIdConstructor(prefix, fieldName) {
132
287
  return raw;
133
288
  };
134
289
  }
290
+ var toSafeId = makePrefixedIdConstructor(
291
+ "safe",
292
+ "safeId"
293
+ );
294
+ var toTreasuryId = makePrefixedIdConstructor(
295
+ "try",
296
+ "treasuryId"
297
+ );
135
298
  var toOperationId = makePrefixedIdConstructor(
136
299
  "op",
137
300
  "operationId"
138
301
  );
139
302
  var toCorrelationId = makePrefixedIdConstructor("ctx", "correlationId");
140
303
  var toExternalAccountId = makePrefixedIdConstructor("ext", "externalAccountId");
304
+ var toSubAccountId = makePrefixedIdConstructor(
305
+ "sub",
306
+ "subAccountId"
307
+ );
141
308
 
142
309
  // src/core/external-accounts.ts
143
310
  function brandExternalAccount(raw) {
@@ -154,13 +321,13 @@ function brandExternalAccount(raw) {
154
321
  function createExternalAccountsClient(config = {}) {
155
322
  return {
156
323
  retrieve: async (externalAccountId) => {
157
- if (!config.data) {
324
+ if (!config._data) {
158
325
  return stub(
159
326
  "externalAccounts.retrieve"
160
327
  );
161
328
  }
162
329
  const [err, raw] = await tryCatch(
163
- config.data.query(api.externalAccounts.queries.retrievePersonal, {
330
+ config._data.query(api.externalAccounts.queries.retrievePersonal, {
164
331
  externalAccountId
165
332
  })
166
333
  );
@@ -182,11 +349,11 @@ function createExternalAccountsClient(config = {}) {
182
349
  return [null, brandExternalAccount(raw)];
183
350
  },
184
351
  remove: async (externalAccountId) => {
185
- if (!config.data) {
352
+ if (!config._data) {
186
353
  return stub("externalAccounts.remove");
187
354
  }
188
355
  const [err] = await tryCatch(
189
- config.data.mutation(api.externalAccounts.mutations.removePersonal, {
356
+ config._data.mutation(api.externalAccounts.mutations.removePersonal, {
190
357
  externalAccountId
191
358
  })
192
359
  );
@@ -201,17 +368,203 @@ function createExternalAccountsClient(config = {}) {
201
368
  };
202
369
  }
203
370
 
371
+ // src/core/sub-accounts.ts
372
+ function malformedWireError(reason, raw) {
373
+ return new CapxulError({
374
+ code: "PROVIDER_ERROR",
375
+ message: `convex brandSubAccount failed: ${reason}`,
376
+ details: {
377
+ provider: "convex",
378
+ operation: "brandSubAccount",
379
+ reason,
380
+ // PII discipline (`.claude/rules/posthog.md`): user-authored
381
+ // strings on the wire (`name`, `purpose`) are customer-confidential
382
+ // — sub-account names like "Q3 Acquisition Reserve" or
383
+ // "Vendor ABC payments" must not flow into telemetry. We emit a
384
+ // structural keys-only sample via a strict ALLOWLIST so any future
385
+ // wire-shape addition (e.g. `recipientEmail`, `tags`) is dropped
386
+ // by construction rather than leaked through a denylist gap.
387
+ sample: safeSampleShape(raw)
388
+ }
389
+ });
390
+ }
391
+ function safeSampleShape(raw) {
392
+ if (raw === null || typeof raw !== "object") {
393
+ return { type: typeof raw };
394
+ }
395
+ const r = raw;
396
+ const balance = r.balance;
397
+ return {
398
+ object: typeof r.object === "string" ? r.object : typeof r.object,
399
+ idPresent: typeof r.id === "string" && r.id.length > 0,
400
+ // First 4 chars only — enough to distinguish "sub_" prefixed IDs
401
+ // from accidental other resource IDs without leaking the full ID.
402
+ idPrefix: typeof r.id === "string" ? r.id.slice(0, 4) : void 0,
403
+ parentKind: r.parent !== null && typeof r.parent === "object" ? r.parent.kind : typeof r.parent,
404
+ status: r.status,
405
+ hasName: typeof r.name === "string",
406
+ hasPurpose: r.purpose !== void 0,
407
+ balanceShape: balance !== null && typeof balance === "object" ? Object.keys(balance).sort() : typeof balance,
408
+ createdAtType: typeof r.createdAt,
409
+ updatedAtType: typeof r.updatedAt
410
+ };
411
+ }
412
+ function isMoneyShape(v) {
413
+ if (typeof v !== "object" || v === null) return false;
414
+ const m = v;
415
+ return typeof m.value === "string" && typeof m.currency === "string";
416
+ }
417
+ function isParentShape(v) {
418
+ if (typeof v !== "object" || v === null) return false;
419
+ const p = v;
420
+ return (p.kind === "account" || p.kind === "organization") && typeof p.id === "string";
421
+ }
422
+ function isFiniteNonNegativeInteger(v) {
423
+ return typeof v === "number" && Number.isFinite(v) && Number.isInteger(v) && v >= 0;
424
+ }
425
+ function validateWireSubAccount(raw) {
426
+ if (typeof raw !== "object" || raw === null) {
427
+ return { ok: false, reason: "not an object" };
428
+ }
429
+ const r = raw;
430
+ if (r.object !== "sub_account") {
431
+ return { ok: false, reason: `object must be "sub_account" (got ${String(r.object)})` };
432
+ }
433
+ if (typeof r.id !== "string" || r.id.length === 0) {
434
+ return { ok: false, reason: "id must be a non-empty string" };
435
+ }
436
+ if (!isParentShape(r.parent)) {
437
+ return { ok: false, reason: "parent must be { kind: 'account' | 'organization', id: string }" };
438
+ }
439
+ if (typeof r.name !== "string") {
440
+ return { ok: false, reason: "name must be a string" };
441
+ }
442
+ if (r.purpose !== void 0 && typeof r.purpose !== "string") {
443
+ return { ok: false, reason: "purpose must be a string when present" };
444
+ }
445
+ if (r.status !== "active" && r.status !== "archived") {
446
+ return { ok: false, reason: `status must be "active" | "archived" (got ${String(r.status)})` };
447
+ }
448
+ if (!isMoneyShape(r.balance)) {
449
+ return { ok: false, reason: "balance must be { value: string, currency: string }" };
450
+ }
451
+ if (!isFiniteNonNegativeInteger(r.createdAt)) {
452
+ return { ok: false, reason: "createdAt must be a finite non-negative integer (epoch ms)" };
453
+ }
454
+ if (r.updatedAt !== void 0 && !isFiniteNonNegativeInteger(r.updatedAt)) {
455
+ return { ok: false, reason: "updatedAt must be a finite non-negative integer when present" };
456
+ }
457
+ return { ok: true, value: r };
458
+ }
459
+ function brandSubAccount(raw) {
460
+ const result = validateWireSubAccount(raw);
461
+ if (!result.ok) {
462
+ throw malformedWireError(result.reason, raw);
463
+ }
464
+ const wire = result.value;
465
+ return {
466
+ object: wire.object,
467
+ id: toSubAccountId(wire.id),
468
+ parent: wire.parent,
469
+ name: wire.name,
470
+ ...wire.purpose !== void 0 ? { purpose: wire.purpose } : {},
471
+ status: wire.status,
472
+ balance: wire.balance,
473
+ createdAt: new Date(wire.createdAt).toISOString()
474
+ };
475
+ }
476
+ function tryBrandSubAccount(raw) {
477
+ try {
478
+ return [null, brandSubAccount(raw)];
479
+ } catch (err) {
480
+ if (err instanceof CapxulError && err.code === "PROVIDER_ERROR") {
481
+ return [err, null];
482
+ }
483
+ return [
484
+ malformedWireError(
485
+ err instanceof Error ? err.message : String(err),
486
+ raw
487
+ ),
488
+ null
489
+ ];
490
+ }
491
+ }
492
+ function createSubAccountsClient(config = {}) {
493
+ return {
494
+ retrieve: async (subAccountId) => {
495
+ if (!config._data) {
496
+ return stub("subAccounts.retrieve");
497
+ }
498
+ const [err, raw] = await tryCatch(
499
+ config._data.query(api.subAccounts.queries.retrieve, {
500
+ subAccountId
501
+ })
502
+ );
503
+ if (err) {
504
+ return [
505
+ fromConvexError(err),
506
+ null
507
+ ];
508
+ }
509
+ if (!raw) {
510
+ return [
511
+ new CapxulError({
512
+ code: "NOT_FOUND",
513
+ message: `sub_account ${subAccountId} not found`
514
+ }),
515
+ null
516
+ ];
517
+ }
518
+ const [brandErr, branded] = tryBrandSubAccount(raw);
519
+ if (brandErr) {
520
+ return [brandErr, null];
521
+ }
522
+ return [null, branded];
523
+ },
524
+ remove: async (subAccountId) => {
525
+ if (!config._data) {
526
+ return stub("subAccounts.remove");
527
+ }
528
+ const [err, raw] = await tryCatch(
529
+ config._data.mutation(api.subAccounts.mutations.archive, {
530
+ subAccountId
531
+ })
532
+ );
533
+ if (err) {
534
+ return [
535
+ fromConvexError(err),
536
+ null
537
+ ];
538
+ }
539
+ if (!raw) {
540
+ return [
541
+ new CapxulError({
542
+ code: "NOT_FOUND",
543
+ message: `sub_account ${subAccountId} not found`
544
+ }),
545
+ null
546
+ ];
547
+ }
548
+ const [brandErr, branded] = tryBrandSubAccount(raw);
549
+ if (brandErr) {
550
+ return [brandErr, null];
551
+ }
552
+ return [null, branded];
553
+ }
554
+ };
555
+ }
556
+
204
557
  // src/core/accounts.ts
205
558
  function createAccountExternalAccountsClient(config) {
206
559
  return {
207
560
  create: async (input) => {
208
- if (!config.data) {
561
+ if (!config._data) {
209
562
  return stub(
210
563
  "accounts.externalAccounts.create"
211
564
  );
212
565
  }
213
566
  const [err, raw] = await tryCatch(
214
- config.data.mutation(
567
+ config._data.mutation(
215
568
  api.externalAccounts.mutations.createPersonal,
216
569
  {
217
570
  kind: input.kind,
@@ -249,13 +602,13 @@ function createAccountExternalAccountsClient(config) {
249
602
  ];
250
603
  },
251
604
  list: async (input) => {
252
- if (!config.data) {
605
+ if (!config._data) {
253
606
  return stub(
254
607
  "accounts.externalAccounts.list"
255
608
  );
256
609
  }
257
610
  const [err, result] = await tryCatch(
258
- config.data.query(api.externalAccounts.queries.listPersonal, {
611
+ config._data.query(api.externalAccounts.queries.listPersonal, {
259
612
  limit: input.limit,
260
613
  cursor: input.cursor
261
614
  })
@@ -278,13 +631,13 @@ function createAccountExternalAccountsClient(config) {
278
631
  ];
279
632
  },
280
633
  retrieve: async (externalAccountId) => {
281
- if (!config.data) {
634
+ if (!config._data) {
282
635
  return stub(
283
636
  "accounts.externalAccounts.retrieve"
284
637
  );
285
638
  }
286
639
  const [err, raw] = await tryCatch(
287
- config.data.query(api.externalAccounts.queries.retrievePersonal, {
640
+ config._data.query(api.externalAccounts.queries.retrievePersonal, {
288
641
  externalAccountId
289
642
  })
290
643
  );
@@ -311,11 +664,11 @@ function createAccountExternalAccountsClient(config) {
311
664
  ];
312
665
  },
313
666
  remove: async (externalAccountId) => {
314
- if (!config.data) {
667
+ if (!config._data) {
315
668
  return stub("accounts.externalAccounts.remove");
316
669
  }
317
670
  const [err] = await tryCatch(
318
- config.data.mutation(api.externalAccounts.mutations.removePersonal, {
671
+ config._data.mutation(api.externalAccounts.mutations.removePersonal, {
319
672
  externalAccountId
320
673
  })
321
674
  );
@@ -329,41 +682,189 @@ function createAccountExternalAccountsClient(config) {
329
682
  }
330
683
  };
331
684
  }
332
- function createAccountsClient(config = {}) {
685
+ function createAccountSubAccountsClient(config) {
333
686
  return {
334
- retrieve: async (accountId) => {
335
- if (!config.data) {
336
- return stub("accounts.retrieve");
337
- }
338
- try {
339
- const account = await config.data.query(
340
- api.openfort.queries.getMyAccount,
341
- {}
687
+ create: async (input) => {
688
+ if (!config._data) {
689
+ return stub(
690
+ "accounts.subAccounts.create"
342
691
  );
343
- if (account.id !== accountId) {
344
- return [
345
- new CapxulError({
346
- code: "PERMISSION_DENIED",
347
- message: "accounts.retrieve currently supports the authenticated caller's own account only.",
348
- details: {
349
- requestedAccountId: accountId,
350
- authenticatedAccountId: account.id
351
- }
352
- }),
353
- null
354
- ];
355
- }
356
- return [null, account];
357
- } catch (cause) {
358
- return [fromConvexError(cause), null];
359
- }
360
- },
361
- lookup: async () => stub("accounts.lookup"),
362
- update: async (input) => {
363
- if (!config.data) {
364
- return stub("accounts.update");
365
692
  }
366
- if (input.countryCode !== void 0) {
693
+ const [err, raw] = await tryCatch(
694
+ config._data.mutation(api.subAccounts.mutations.create, {
695
+ parent: { kind: "account", id: input.accountId },
696
+ name: input.name,
697
+ purpose: input.purpose
698
+ })
699
+ );
700
+ if (err) {
701
+ return [
702
+ fromConvexError(err),
703
+ null
704
+ ];
705
+ }
706
+ if (!raw) {
707
+ return [
708
+ new CapxulError({
709
+ code: "NOT_FOUND",
710
+ message: "sub_account creation returned no resource"
711
+ }),
712
+ null
713
+ ];
714
+ }
715
+ const [brandErr, branded] = tryBrandSubAccount(raw);
716
+ if (brandErr) {
717
+ return [
718
+ brandErr,
719
+ null
720
+ ];
721
+ }
722
+ return [null, branded];
723
+ },
724
+ list: async (input) => {
725
+ if (!config._data) {
726
+ return stub(
727
+ "accounts.subAccounts.list"
728
+ );
729
+ }
730
+ const [err, rows] = await tryCatch(
731
+ config._data.query(api.subAccounts.queries.listByAccount, {
732
+ accountId: input.accountId
733
+ })
734
+ );
735
+ if (err) {
736
+ return [
737
+ fromConvexError(err),
738
+ null
739
+ ];
740
+ }
741
+ const branded = [];
742
+ for (const row of rows) {
743
+ const [brandErr, value] = tryBrandSubAccount(row);
744
+ if (brandErr) {
745
+ return [
746
+ brandErr,
747
+ null
748
+ ];
749
+ }
750
+ branded.push(value);
751
+ }
752
+ return [
753
+ null,
754
+ {
755
+ object: "list",
756
+ data: branded,
757
+ page: { hasMore: false }
758
+ }
759
+ ];
760
+ },
761
+ retrieve: async (subAccountId) => {
762
+ if (!config._data) {
763
+ return stub(
764
+ "accounts.subAccounts.retrieve"
765
+ );
766
+ }
767
+ const [err, raw] = await tryCatch(
768
+ config._data.query(api.subAccounts.queries.retrieve, {
769
+ subAccountId
770
+ })
771
+ );
772
+ if (err) {
773
+ return [
774
+ fromConvexError(err),
775
+ null
776
+ ];
777
+ }
778
+ if (!raw) {
779
+ return [
780
+ new CapxulError({
781
+ code: "NOT_FOUND",
782
+ message: `sub_account ${subAccountId} not found`
783
+ }),
784
+ null
785
+ ];
786
+ }
787
+ const [brandErr, branded] = tryBrandSubAccount(raw);
788
+ if (brandErr) {
789
+ return [
790
+ brandErr,
791
+ null
792
+ ];
793
+ }
794
+ return [null, branded];
795
+ },
796
+ remove: async (subAccountId) => {
797
+ if (!config._data) {
798
+ return stub(
799
+ "accounts.subAccounts.remove"
800
+ );
801
+ }
802
+ const [err, raw] = await tryCatch(
803
+ config._data.mutation(api.subAccounts.mutations.archive, {
804
+ subAccountId
805
+ })
806
+ );
807
+ if (err) {
808
+ return [
809
+ fromConvexError(err),
810
+ null
811
+ ];
812
+ }
813
+ if (!raw) {
814
+ return [
815
+ new CapxulError({
816
+ code: "NOT_FOUND",
817
+ message: `sub_account ${subAccountId} not found`
818
+ }),
819
+ null
820
+ ];
821
+ }
822
+ const [brandErr, branded] = tryBrandSubAccount(raw);
823
+ if (brandErr) {
824
+ return [
825
+ brandErr,
826
+ null
827
+ ];
828
+ }
829
+ return [null, branded];
830
+ }
831
+ };
832
+ }
833
+ function createAccountsClient(config = {}) {
834
+ return {
835
+ retrieve: async (accountId) => {
836
+ if (!config._data) {
837
+ return stub("accounts.retrieve");
838
+ }
839
+ try {
840
+ const account = await config._data.query(
841
+ api.openfort.queries.getMyAccount,
842
+ {}
843
+ );
844
+ if (account.id !== accountId) {
845
+ return [
846
+ new CapxulError({
847
+ code: "PERMISSION_DENIED",
848
+ message: "accounts.retrieve currently supports the authenticated caller's own account only.",
849
+ details: {
850
+ requestedAccountId: accountId,
851
+ authenticatedAccountId: account.id
852
+ }
853
+ }),
854
+ null
855
+ ];
856
+ }
857
+ return [null, account];
858
+ } catch (cause) {
859
+ return [fromConvexError(cause), null];
860
+ }
861
+ },
862
+ lookup: async () => stub("accounts.lookup"),
863
+ update: async (input) => {
864
+ if (!config._data) {
865
+ return stub("accounts.update");
866
+ }
867
+ if (input.countryCode !== void 0) {
367
868
  return [
368
869
  new CapxulError({
369
870
  code: "INVALID_INPUT",
@@ -374,7 +875,7 @@ function createAccountsClient(config = {}) {
374
875
  ];
375
876
  }
376
877
  try {
377
- const current = await config.data.query(
878
+ const current = await config._data.query(
378
879
  api.openfort.queries.getMyAccount,
379
880
  {}
380
881
  );
@@ -391,11 +892,11 @@ function createAccountsClient(config = {}) {
391
892
  null
392
893
  ];
393
894
  }
394
- await config.data.mutation(api.openfort.mutations.updateProfile, {
895
+ await config._data.mutation(api.openfort.mutations.updateProfile, {
395
896
  displayName: input.name,
396
897
  username: input.username
397
898
  });
398
- const updated = await config.data.query(
899
+ const updated = await config._data.query(
399
900
  api.openfort.queries.getMyAccount,
400
901
  {}
401
902
  );
@@ -405,7 +906,7 @@ function createAccountsClient(config = {}) {
405
906
  }
406
907
  },
407
908
  provisionPersonal: async (input) => {
408
- if (!config.data) {
909
+ if (!config._data) {
409
910
  return stub(
410
911
  "accounts.provisionPersonal"
411
912
  );
@@ -420,17 +921,17 @@ function createAccountsClient(config = {}) {
420
921
  ];
421
922
  }
422
923
  try {
423
- await config.data.mutation(
924
+ await config._data.mutation(
424
925
  api.safe.mutations.provisionLocalPersonalAccount,
425
926
  {
426
927
  displayName: input.displayName,
427
928
  username: input.username,
428
929
  countryCode: input.countryCode,
429
930
  eoaAddress: input.signerProvider.signerAddress,
430
- safeAddress: input.signerProvider.safeAddress
931
+ safeAddress: deriveSafeAddress(input.signerProvider.signerAddress)
431
932
  }
432
933
  );
433
- const account = await config.data.query(
934
+ const account = await config._data.query(
434
935
  api.openfort.queries.getMyAccount,
435
936
  {}
436
937
  );
@@ -453,11 +954,11 @@ function createAccountsClient(config = {}) {
453
954
  },
454
955
  safes: {
455
956
  retrieve: async (safeId) => {
456
- if (!config.data) {
957
+ if (!config._data) {
457
958
  return stub("accounts.safes.retrieve");
458
959
  }
459
960
  try {
460
- const safe = await config.data.query(
961
+ const safe = await config._data.query(
461
962
  api.safe.queries.retrieveAccountSafe,
462
963
  { safeId }
463
964
  );
@@ -484,19 +985,50 @@ function createAccountsClient(config = {}) {
484
985
  retrieve: async () => stub("accounts.kycProfiles.retrieve")
485
986
  },
486
987
  externalAccounts: createAccountExternalAccountsClient(config),
487
- subAccounts: {
488
- create: async () => stub("accounts.subAccounts.create"),
489
- list: async () => stub("accounts.subAccounts.list"),
490
- retrieve: async () => stub("accounts.subAccounts.retrieve"),
491
- remove: async () => stub("accounts.subAccounts.remove")
492
- },
988
+ subAccounts: createAccountSubAccountsClient(config),
493
989
  balanceLedger: {
494
- list: async () => stub(
495
- "accounts.balanceLedger.list"
496
- ),
497
- retrieve: async () => stub(
498
- "accounts.balanceLedger.retrieve"
499
- )
990
+ list: async (input) => {
991
+ if (!config._data) {
992
+ return stub(
993
+ "accounts.balanceLedger.list"
994
+ );
995
+ }
996
+ try {
997
+ const accountId = input.accountId.replace(/^acct_/, "");
998
+ const page = await config._data.query(
999
+ api.balanceLedger.queries.listForAccount,
1000
+ { accountId, limit: input.limit, cursor: input.cursor }
1001
+ );
1002
+ return [null, page];
1003
+ } catch (cause) {
1004
+ return [fromConvexError(cause), null];
1005
+ }
1006
+ },
1007
+ retrieve: async (entryId) => {
1008
+ if (!config._data) {
1009
+ return stub(
1010
+ "accounts.balanceLedger.retrieve"
1011
+ );
1012
+ }
1013
+ try {
1014
+ const entry = await config._data.query(
1015
+ api.balanceLedger.queries.retrieve,
1016
+ { entryId }
1017
+ );
1018
+ if (!entry) {
1019
+ return [
1020
+ new CapxulError({
1021
+ code: "NOT_FOUND",
1022
+ message: `balance_ledger_entry ${entryId} not found`
1023
+ }),
1024
+ null
1025
+ ];
1026
+ }
1027
+ return [null, entry];
1028
+ } catch (cause) {
1029
+ return [fromConvexError(cause), null];
1030
+ }
1031
+ }
500
1032
  }
501
1033
  };
502
1034
  }
@@ -510,129 +1042,21 @@ function createApiKeysClient() {
510
1042
  revoke: async () => stub("apiKeys.revoke")
511
1043
  };
512
1044
  }
1045
+ function createDefaultDataClient(convexUrl, jwt) {
1046
+ const client = new ConvexHttpClient(convexUrl);
1047
+ client.setAuth(jwt);
1048
+ return client;
1049
+ }
513
1050
 
514
- // ../config/src/chain.ts
515
- var CAPXUL_PAYMENTS_ADDRESS = "0xe740ea65521edc9bef0ea75d30b5334711db99f4";
516
- var TEST_USDC_ADDRESS = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
517
- var CAPXUL_API_BASE_URL = "https://elated-oyster-269.convex.site";
518
-
519
- // ../config/src/timing.ts
520
- var FLOW_INVOKE_TIMEOUT_MS = 3e4;
521
-
522
- // ../config/src/errors.ts
523
- var CapxulError2 = class extends Error {
524
- code;
525
- details;
526
- correlationId;
527
- layer;
528
- constructor(code, message, options) {
529
- super(message, options?.cause ? { cause: options.cause } : void 0);
530
- this.code = code;
531
- this.details = options?.details;
532
- this.correlationId = options?.correlationId;
533
- this.layer = options?.layer;
534
- }
535
- };
536
- var Errors = {
537
- notAuthenticated: () => new CapxulError2("NOT_AUTHENTICATED", "Not authenticated"),
538
- profileNotFound: (authUserId) => new CapxulError2("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`),
539
- smartAccountMissing: (authUserId) => new CapxulError2("SMART_ACCOUNT_MISSING", `Smart account not provisioned for user ${authUserId}`),
540
- envMissing: (name) => new CapxulError2("ENV_MISSING", `Environment variable ${name} not configured`),
541
- openfortApi: (operation, cause) => new CapxulError2(
542
- "PROVIDER_ERROR",
543
- `Openfort ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
544
- { cause, details: { provider: "openfort", operation } }
545
- ),
546
- shieldApi: (status, detail) => new CapxulError2(
547
- "PROVIDER_ERROR",
548
- `Shield API error (${status}): ${detail}`,
549
- { details: { provider: "shield", status } }
550
- ),
551
- providerError: (provider, operation, cause) => new CapxulError2(
552
- "PROVIDER_ERROR",
553
- `${provider} ${operation} failed: ${cause instanceof Error ? cause.message : String(cause)}`,
554
- { cause, details: { provider, operation } }
555
- ),
556
- invalidInput: (field, reason) => new CapxulError2(
557
- "INVALID_INPUT",
558
- `Invalid ${field}: ${reason}`,
559
- { details: { field, reason } }
560
- ),
561
- playerNotFound: (playerId) => new CapxulError2(
562
- "PLAYER_NOT_FOUND",
563
- playerId ? `Openfort player ${playerId} not found` : "Openfort player not found"
564
- ),
565
- accountNotFound: (accountId) => new CapxulError2(
566
- "ACCOUNT_NOT_FOUND",
567
- accountId ? `Openfort account ${accountId} not found` : "Openfort smart account not found"
568
- ),
569
- invalidRecipient: (reason) => new CapxulError2("INVALID_RECIPIENT", reason),
570
- permissionDenied: (reason = "Permission denied") => new CapxulError2("PERMISSION_DENIED", reason),
571
- notFound: (resource, id) => new CapxulError2(
572
- "NOT_FOUND",
573
- id ? `${resource} ${id} not found` : `${resource} not found`
574
- ),
575
- idempotencyConflict: (details) => new CapxulError2(
576
- "IDEMPOTENCY_CONFLICT",
577
- "Idempotency key was already used for a different request",
578
- { details }
579
- ),
580
- emailDeliveryFailed: (detail, details) => new CapxulError2("EMAIL_DELIVERY_FAILED", `Failed to send email: ${detail}`, {
581
- details
582
- }),
583
- rateLimited: (details) => new CapxulError2("RATE_LIMITED", "Request was rate limited", {
584
- details: { ...details }
585
- }),
586
- internalError: (reason) => new CapxulError2("INTERNAL_ERROR", `Internal error: ${reason}`),
587
- /**
588
- * Verification gate. Surfaced when a request hits a verification
589
- * boundary the actor cannot cross under their current state. Two
590
- * variants share this code:
591
- *
592
- * - Rail gate (Withdrawals v1 W2, #465): the resolved
593
- * `external_account.kind` routes to a withdrawal rail (e.g.
594
- * `fiat_offramp`, `card_payout`) that is not yet supported. Carries
595
- * `details.rail` + `details.currentKind`.
596
- * - KYC tier gate (legacy / future): the actor's KYC tier is below
597
- * the required tier. Carries `details.requiredTier`.
598
- *
599
- * Code is shared because both expose the same UX shape ("you cannot
600
- * proceed until verification advances"); the `details.*` keys
601
- * differentiate the route.
602
- */
603
- verificationRequired: (details) => {
604
- const message = "rail" in details ? `Withdrawal rail "${details.rail}" (kind=${details.currentKind}) is not yet supported.` : `Verification tier ${details.requiredTier} is required.`;
605
- return new CapxulError2("VERIFICATION_REQUIRED", message, {
606
- details: { ...details }
607
- });
608
- }
609
- };
610
-
611
- // ../config/src/safe.ts
612
- var SAFE_PROXY_FACTORY = "0x4e1dcf7ad4e460cfd30791ccc4f9c8a4f820ec67";
613
- var SAFE_L2_SINGLETON = "0x29fcb43b46531bca003ddc8fcb67ffe91900c762";
614
- var SAFE_MODULE_SETUP = "0x2dd68b007b46fbe91b9a7c3eda5a7a1063cb5b47";
615
- var SAFE_4337_MODULE = "0x75cf11467937ce3f2f357ce24ffc3dbf8fd5c226";
616
-
617
- // ../config/src/org-roles.ts
618
- function roleKeyFromLabel(label) {
619
- const bytes = new TextEncoder().encode(label);
620
- const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
621
- return "0x" + hex.padEnd(64, "0");
622
- }
623
- roleKeyFromLabel("OWNER");
624
- roleKeyFromLabel("FINANCE_MANAGER");
625
- roleKeyFromLabel("TEAM_LEAD");
626
-
627
- // src/transport.ts
628
- function makeHttpTransport(config) {
629
- switch (config.mode) {
630
- case "build-time-urls":
631
- return makeBuildTimeUrlsTransport(config);
632
- case "publishable-key":
633
- return makePublishableKeyTransport(config);
634
- default:
635
- return assertNever(config);
1051
+ // src/transport.ts
1052
+ function makeHttpTransport(config) {
1053
+ switch (config.mode) {
1054
+ case "build-time-urls":
1055
+ return makeBuildTimeUrlsTransport(config);
1056
+ case "publishable-key":
1057
+ return makePublishableKeyTransport(config);
1058
+ default:
1059
+ return assertNever(config);
636
1060
  }
637
1061
  }
638
1062
  function createLifecycle(initial) {
@@ -953,7 +1377,7 @@ function readNonEmptyString(value) {
953
1377
 
954
1378
  // src/core/auth.ts
955
1379
  function createAuthClient(config = {}) {
956
- let dataClient = config.data ?? null;
1380
+ let dataClient = config._data ?? null;
957
1381
  const sessionStore = config.auth?.sessionStore ?? createMemorySessionStore();
958
1382
  const getTransport = createTransportProvider(config);
959
1383
  return {
@@ -1009,11 +1433,20 @@ function createAuthClient(config = {}) {
1009
1433
  ).toISOString()
1010
1434
  };
1011
1435
  sessionStore.set(session);
1012
- if (config.auth?.createDataClient) {
1436
+ if (!dataClient) {
1013
1437
  try {
1014
- dataClient = await config.auth.createDataClient(session);
1015
- mutableConfig(config).data = dataClient;
1016
- transport.markAuthenticated({ dataClient });
1438
+ const convexUrl = transport.convexUrl;
1439
+ if (!convexUrl || !session.convexJwt) {
1440
+ return [
1441
+ new CapxulError({
1442
+ code: "NETWORK_ERROR",
1443
+ message: "Cannot create data client: missing convex URL or JWT."
1444
+ }),
1445
+ null
1446
+ ];
1447
+ }
1448
+ dataClient = createDefaultDataClient(convexUrl, session.convexJwt);
1449
+ mutableConfig(config)._data = dataClient;
1017
1450
  } catch (cause) {
1018
1451
  return [
1019
1452
  new CapxulError({
@@ -1024,14 +1457,89 @@ function createAuthClient(config = {}) {
1024
1457
  null
1025
1458
  ];
1026
1459
  }
1460
+ } else {
1461
+ const injected = dataClient;
1462
+ if (typeof injected.refreshAuth === "function") {
1463
+ injected.refreshAuth();
1464
+ } else if (typeof injected.setAuth === "function" && session.convexJwt) {
1465
+ injected.setAuth(session.convexJwt);
1466
+ }
1467
+ }
1468
+ transport.markAuthenticated({ dataClient });
1469
+ if (!dataClient) {
1470
+ return [
1471
+ new CapxulError({
1472
+ code: "NOT_AUTHENTICATED",
1473
+ message: "Auth bootstrap requires an authenticated Convex data client."
1474
+ }),
1475
+ null
1476
+ ];
1477
+ }
1478
+ try {
1479
+ const resolution = await dataClient.mutation(
1480
+ api.authBootstrap.resolveAfterOtp,
1481
+ {
1482
+ email: session.email,
1483
+ sessionToken: session.token
1484
+ }
1485
+ );
1486
+ if (resolution.kind === "existing_member") {
1487
+ return [null, { ...resolution, session }];
1488
+ }
1489
+ return [null, { ...resolution, session }];
1490
+ } catch (cause) {
1491
+ return [fromConvexError(cause), null];
1492
+ }
1493
+ },
1494
+ completeBootstrap: async (input) => {
1495
+ const session = sessionStore.get();
1496
+ const data = dataClient ?? config._data;
1497
+ if (!session || !data) {
1498
+ return [
1499
+ new CapxulError({
1500
+ code: "INVALID_INPUT",
1501
+ message: "completeBootstrap requires the OTP-verified session that issued the bootstrap token."
1502
+ }),
1503
+ null
1504
+ ];
1505
+ }
1506
+ const signerAddress = config.signer?.address;
1507
+ if (!signerAddress) {
1508
+ return [
1509
+ new CapxulError({
1510
+ code: "INVALID_INPUT",
1511
+ message: "completeBootstrap requires a signer to be configured on the client."
1512
+ }),
1513
+ null
1514
+ ];
1515
+ }
1516
+ try {
1517
+ const safeAddress = deriveSafeAddress(signerAddress);
1518
+ const result = await data.mutation(api.authBootstrap.completeBootstrap, {
1519
+ bootstrapToken: input.bootstrapToken,
1520
+ sessionToken: session.token,
1521
+ username: input.username,
1522
+ displayName: input.displayName,
1523
+ countryCode: input.countryCode,
1524
+ signerProvider: {
1525
+ kind: "local-private-key",
1526
+ signerAddress,
1527
+ safeAddress
1528
+ }
1529
+ });
1530
+ return [null, { kind: "authenticated", session, ...result }];
1531
+ } catch (cause) {
1532
+ return [
1533
+ fromConvexError(cause),
1534
+ null
1535
+ ];
1027
1536
  }
1028
- return [null, session];
1029
1537
  },
1030
1538
  getSession: async () => [null, sessionStore.get()],
1031
1539
  signOut: async () => {
1032
1540
  sessionStore.clear();
1033
1541
  dataClient = null;
1034
- mutableConfig(config).data = void 0;
1542
+ mutableConfig(config)._data = void 0;
1035
1543
  const transport = getTransport();
1036
1544
  transport?.clearAuth();
1037
1545
  return [null, void 0];
@@ -1102,6 +1610,9 @@ async function postBetterAuth(transport, path, body, code, signal) {
1102
1610
  }
1103
1611
  return [null, text ? JSON.parse(text) : void 0];
1104
1612
  } catch (cause) {
1613
+ if (cause instanceof CapxulError) {
1614
+ return [cause, null];
1615
+ }
1105
1616
  return [
1106
1617
  new CapxulError({
1107
1618
  code: "NETWORK_ERROR",
@@ -1140,7 +1651,7 @@ function parseBetterAuthError(text) {
1140
1651
  }
1141
1652
  }
1142
1653
  function isCapxulErrorCode2(code) {
1143
- return code === "NOT_AUTHENTICATED" || code === "EMAIL_DELIVERY_FAILED" || code === "INVALID_INPUT" || code === "RATE_LIMITED" || code === "NETWORK_ERROR" || code === "API_KEY_INVALID" || code === "API_KEY_EXPIRED";
1654
+ return code === "NOT_AUTHENTICATED" || code === "EMAIL_DELIVERY_FAILED" || code === "INVALID_INPUT" || code === "RATE_LIMITED" || code === "NETWORK_ERROR" || code === "API_KEY_INVALID" || code === "API_KEY_EXPIRED" || code === "INTERNAL_ERROR" || code === "ENV_MISSING" || code === "UNKNOWN" || code === "PROVIDER_ERROR";
1144
1655
  }
1145
1656
  async function exchangeConvexToken(transport, config, token, signal) {
1146
1657
  const path = config.auth?.convexTokenUrl ?? "/convex/token";
@@ -1170,6 +1681,9 @@ async function exchangeConvexToken(transport, config, token, signal) {
1170
1681
  }
1171
1682
  return [null, body.token];
1172
1683
  } catch (cause) {
1684
+ if (cause instanceof CapxulError) {
1685
+ return [cause, null];
1686
+ }
1173
1687
  return [
1174
1688
  new CapxulError({
1175
1689
  code: "NETWORK_ERROR",
@@ -1206,11 +1720,11 @@ function createOrgDocumentsClient() {
1206
1720
  function createMeClient(config = {}) {
1207
1721
  return {
1208
1722
  get: async () => {
1209
- if (!config.data) {
1723
+ if (!config._data) {
1210
1724
  return stub("me.get");
1211
1725
  }
1212
1726
  try {
1213
- const account = await config.data.query(
1727
+ const account = await config._data.query(
1214
1728
  api.openfort.queries.getMyAccount,
1215
1729
  {}
1216
1730
  );
@@ -1220,7 +1734,7 @@ function createMeClient(config = {}) {
1220
1734
  }
1221
1735
  },
1222
1736
  update: async (input) => {
1223
- if (!config.data) {
1737
+ if (!config._data) {
1224
1738
  return stub("me.update");
1225
1739
  }
1226
1740
  if (input.countryCode !== void 0) {
@@ -1234,11 +1748,11 @@ function createMeClient(config = {}) {
1234
1748
  ];
1235
1749
  }
1236
1750
  try {
1237
- await config.data.mutation(api.openfort.mutations.updateProfile, {
1751
+ await config._data.mutation(api.openfort.mutations.updateProfile, {
1238
1752
  displayName: input.name,
1239
1753
  username: input.username
1240
1754
  });
1241
- const account = await config.data.query(
1755
+ const account = await config._data.query(
1242
1756
  api.openfort.queries.getMyAccount,
1243
1757
  {}
1244
1758
  );
@@ -1253,11 +1767,11 @@ function createMeClient(config = {}) {
1253
1767
  // src/core/operations.ts
1254
1768
  function createOperationsClient(config = {}) {
1255
1769
  const retrieve = async (operationId) => {
1256
- if (!config.data) {
1770
+ if (!config._data) {
1257
1771
  return stub("operations.retrieve");
1258
1772
  }
1259
1773
  try {
1260
- const operation = await config.data.query(api.operations.queries.retrieve, {
1774
+ const operation = await config._data.query(api.operations.queries.retrieve, {
1261
1775
  operationId
1262
1776
  });
1263
1777
  if (!operation) {
@@ -1274,7 +1788,7 @@ function createOperationsClient(config = {}) {
1274
1788
  return {
1275
1789
  retrieve,
1276
1790
  wait: async (operationId, input = {}) => {
1277
- if (!config.data) {
1791
+ if (!config._data) {
1278
1792
  return stub("operations.wait");
1279
1793
  }
1280
1794
  const until = new Set(
@@ -1309,49 +1823,22 @@ function toTokenUnits(value, decimals = 6) {
1309
1823
  return parseUnits(value, decimals);
1310
1824
  }
1311
1825
 
1312
- // src/internal/payment-token.ts
1313
- function resolvePaymentTokenAddress(currency) {
1826
+ // src/core/token-registry.ts
1827
+ function resolvePaymentToken(currency) {
1314
1828
  const normalized = currency.trim().toUpperCase();
1315
1829
  if (normalized === "USD" || normalized === "USDC") {
1316
- return TEST_USDC_ADDRESS.toLowerCase();
1830
+ return {
1831
+ address: TEST_USDC_ADDRESS.toLowerCase(),
1832
+ decimals: 6,
1833
+ symbol: "USDC"
1834
+ };
1317
1835
  }
1318
1836
  throw new CapxulError({
1319
- code: "NETWORK_ERROR",
1320
- message: `Currency ${currency} is not configured for on-chain payment submission.`,
1837
+ code: "NOT_IMPLEMENTED",
1838
+ message: `Currency ${currency} is not yet supported by the token registry.`,
1321
1839
  details: { currency: normalized }
1322
1840
  });
1323
1841
  }
1324
- async function buildSafeAccount(signer, chain) {
1325
- try {
1326
- const publicClient = createPublicClient({
1327
- chain: baseSepolia,
1328
- transport: http(chain.rpcUrl)
1329
- });
1330
- return await toSafeSmartAccount({
1331
- client: publicClient,
1332
- entryPoint: { address: entryPoint07Address, version: "0.7" },
1333
- version: "1.4.1",
1334
- owners: [signer],
1335
- saltNonce: computeSaltNonce(signer.address),
1336
- safeSingletonAddress: SAFE_L2_SINGLETON,
1337
- safeProxyFactoryAddress: SAFE_PROXY_FACTORY,
1338
- safeModuleSetupAddress: SAFE_MODULE_SETUP,
1339
- safe4337ModuleAddress: SAFE_4337_MODULE,
1340
- safeModules: [],
1341
- setupTransactions: []
1342
- });
1343
- } catch (cause) {
1344
- throw new CapxulError({
1345
- code: "NETWORK_ERROR",
1346
- message: cause instanceof Error ? cause.message : String(cause),
1347
- cause,
1348
- details: { chainId: chain.chainId }
1349
- });
1350
- }
1351
- }
1352
- function computeSaltNonce(ownerAddress) {
1353
- return BigInt(`0x${ownerAddress.toLowerCase().slice(2).padEnd(64, "0")}`);
1354
- }
1355
1842
  function createCapxulBundler(config) {
1356
1843
  const paymaster = createPaymasterClient({
1357
1844
  transport: http(config.rpcUrl)
@@ -1458,13 +1945,13 @@ async function transferAsOwner(config, params) {
1458
1945
  function createPaymentsClient(config = {}) {
1459
1946
  return {
1460
1947
  create: async (input) => {
1461
- if (!config.data || !config.signer || !config.signing) {
1948
+ if (!config._data || !config.signer || !config.signing) {
1462
1949
  return stub("payments.create");
1463
1950
  }
1464
1951
  let created = null;
1465
1952
  let submitted = null;
1466
1953
  try {
1467
- created = await config.data.mutation(api.payments.mutations.create, {
1954
+ created = await config._data.mutation(api.payments.mutations.create, {
1468
1955
  to: input.to,
1469
1956
  amount: input.amount,
1470
1957
  reference: input.reference,
@@ -1472,15 +1959,21 @@ function createPaymentsClient(config = {}) {
1472
1959
  source: input.source
1473
1960
  });
1474
1961
  if (!created) {
1475
- return [new CapxulError({
1476
- code: "NETWORK_ERROR",
1477
- message: "payments.create returned no payment resource"
1478
- }), null];
1962
+ return [
1963
+ new CapxulError({
1964
+ code: "NETWORK_ERROR",
1965
+ message: "payments.create returned no payment resource"
1966
+ }),
1967
+ null
1968
+ ];
1479
1969
  }
1480
1970
  if (created.status !== "processing" || created.operation.status !== "processing") {
1481
1971
  return [null, created];
1482
1972
  }
1483
- const currentSigner = await config.data.query(api.safe.queries.getMySignerAddress, {});
1973
+ const currentSigner = await config._data.query(
1974
+ api.safe.queries.getMySignerAddress,
1975
+ {}
1976
+ );
1484
1977
  if (!currentSigner?.address) {
1485
1978
  throw new CapxulError({
1486
1979
  code: "PERMISSION_DENIED",
@@ -1499,9 +1992,12 @@ function createPaymentsClient(config = {}) {
1499
1992
  }
1500
1993
  });
1501
1994
  }
1502
- const submission = await config.data.query(api.payments.queries.prepareSubmission, {
1503
- paymentId: created.id
1504
- });
1995
+ const submission = await config._data.query(
1996
+ api.payments.queries.prepareSubmission,
1997
+ {
1998
+ paymentId: created.id
1999
+ }
2000
+ );
1505
2001
  if (!submission?.recipientAddress) {
1506
2002
  throw new CapxulError({
1507
2003
  code: "NETWORK_ERROR",
@@ -1509,15 +2005,16 @@ function createPaymentsClient(config = {}) {
1509
2005
  details: { paymentId: created.id }
1510
2006
  });
1511
2007
  }
2008
+ const token = resolvePaymentToken(submission.amount.currency);
1512
2009
  const transfer = await transferAsOwner(
1513
2010
  {
1514
2011
  signer: config.signer,
1515
2012
  signing: config.signing
1516
2013
  },
1517
2014
  {
1518
- tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
2015
+ tokenAddress: token.address,
1519
2016
  recipientAddress: submission.recipientAddress,
1520
- amount: toTokenUnits(submission.amount.value, 6)
2017
+ amount: toTokenUnits(submission.amount.value, token.decimals)
1521
2018
  }
1522
2019
  );
1523
2020
  if (!transfer.success) {
@@ -1535,7 +2032,7 @@ function createPaymentsClient(config = {}) {
1535
2032
  txHash: transfer.txHash,
1536
2033
  userOpHash: transfer.userOpHash
1537
2034
  };
1538
- await config.data.mutation(api.payments.mutations.recordSubmitted, {
2035
+ await config._data.mutation(api.payments.mutations.recordSubmitted, {
1539
2036
  paymentId: created.id,
1540
2037
  txHash: transfer.txHash,
1541
2038
  userOpHash: transfer.userOpHash,
@@ -1545,43 +2042,65 @@ function createPaymentsClient(config = {}) {
1545
2042
  } catch (cause) {
1546
2043
  const error = mapCreateError(fromConvexError(cause));
1547
2044
  if (created?.id && created.status === "processing" && !submitted) {
1548
- await bestEffortMarkFailed({ data: config.data }, created.id, error);
2045
+ await bestEffortMarkFailed({ _data: config._data }, created.id, error);
1549
2046
  }
1550
2047
  if (submitted && created?.id) {
1551
- return [new CapxulError({
1552
- code: "NETWORK_ERROR",
1553
- message: "Payment was submitted on-chain, but backend submission tracking failed.",
1554
- cause,
1555
- details: {
1556
- paymentId: created.id,
1557
- txHash: submitted.txHash,
1558
- userOpHash: submitted.userOpHash
1559
- }
1560
- }), null];
2048
+ return [
2049
+ new CapxulError({
2050
+ code: "NETWORK_ERROR",
2051
+ message: "Payment was submitted on-chain, but backend submission tracking failed.",
2052
+ cause,
2053
+ details: {
2054
+ paymentId: created.id,
2055
+ txHash: submitted.txHash,
2056
+ userOpHash: submitted.userOpHash
2057
+ }
2058
+ }),
2059
+ null
2060
+ ];
1561
2061
  }
1562
2062
  return [error, null];
1563
2063
  }
1564
2064
  },
1565
2065
  retrieve: async (paymentId) => {
1566
- if (!config.data) {
2066
+ if (!config._data) {
1567
2067
  return stub("payments.retrieve");
1568
2068
  }
1569
2069
  try {
1570
- const payment = await config.data.query(api.payments.queries.retrieve, {
1571
- paymentId
1572
- });
2070
+ const payment = await config._data.query(
2071
+ api.payments.queries.retrieve,
2072
+ {
2073
+ paymentId
2074
+ }
2075
+ );
1573
2076
  if (!payment) {
1574
- return [new CapxulError({
1575
- code: "NOT_FOUND",
1576
- message: `payment ${paymentId} not found`
1577
- }), null];
2077
+ return [
2078
+ new CapxulError({
2079
+ code: "NOT_FOUND",
2080
+ message: `payment ${paymentId} not found`
2081
+ }),
2082
+ null
2083
+ ];
1578
2084
  }
1579
2085
  return [null, payment];
1580
2086
  } catch (cause) {
1581
2087
  return [fromConvexError(cause), null];
1582
2088
  }
1583
2089
  },
1584
- list: async () => stub("payments.list")
2090
+ list: async (input) => {
2091
+ if (!config._data) {
2092
+ return stub("payments.list");
2093
+ }
2094
+ try {
2095
+ const page = await config._data.query(api.payments.queries.list, {
2096
+ limit: input?.limit,
2097
+ cursor: input?.cursor
2098
+ });
2099
+ return [null, page];
2100
+ } catch (cause) {
2101
+ return [fromConvexError(cause), null];
2102
+ }
2103
+ }
1585
2104
  };
1586
2105
  }
1587
2106
  function createOrgPaymentsClient() {
@@ -1595,7 +2114,7 @@ function createOrgPaymentsClient() {
1595
2114
  }
1596
2115
  async function bestEffortMarkFailed(config, paymentId, error) {
1597
2116
  try {
1598
- await config.data.mutation(api.payments.mutations.markFailed, {
2117
+ await config._data.mutation(api.payments.mutations.markFailed, {
1599
2118
  paymentId,
1600
2119
  errorCode: error.code,
1601
2120
  errorMessage: error.message,
@@ -1652,11 +2171,11 @@ function createOrgTransfersClient() {
1652
2171
  function createWithdrawalsClient(config = {}) {
1653
2172
  return {
1654
2173
  create: async (input) => {
1655
- if (!config.data) {
2174
+ if (!config._data) {
1656
2175
  return stub("withdrawals.create");
1657
2176
  }
1658
2177
  const [createErr, createdRaw] = await tryCatch(
1659
- config.data.mutation(api.withdrawals.mutations.create, {
2178
+ config._data.mutation(api.withdrawals.mutations.create, {
1660
2179
  amount: input.amount,
1661
2180
  destination: {
1662
2181
  externalAccountId: input.destination.externalAccountId
@@ -1686,18 +2205,18 @@ function createWithdrawalsClient(config = {}) {
1686
2205
  return [null, created];
1687
2206
  }
1688
2207
  const [signerErr, currentSigner] = await tryCatch(
1689
- config.data.query(api.safe.queries.getMySignerAddress, {})
2208
+ config._data.query(api.safe.queries.getMySignerAddress, {})
1690
2209
  );
1691
2210
  if (signerErr) {
1692
2211
  return await handleSubmissionFailure(
1693
- { data: config.data },
2212
+ { _data: config._data },
1694
2213
  created.id,
1695
2214
  mapCreateError2(fromConvexError(signerErr))
1696
2215
  );
1697
2216
  }
1698
2217
  if (!currentSigner?.address) {
1699
2218
  return await handleSubmissionFailure(
1700
- { data: config.data },
2219
+ { _data: config._data },
1701
2220
  created.id,
1702
2221
  new CapxulError({
1703
2222
  code: "PERMISSION_DENIED",
@@ -1708,7 +2227,7 @@ function createWithdrawalsClient(config = {}) {
1708
2227
  }
1709
2228
  if (getAddress(currentSigner.address) !== getAddress(config.signer.address)) {
1710
2229
  return await handleSubmissionFailure(
1711
- { data: config.data },
2230
+ { _data: config._data },
1712
2231
  created.id,
1713
2232
  new CapxulError({
1714
2233
  code: "PERMISSION_DENIED",
@@ -1722,13 +2241,13 @@ function createWithdrawalsClient(config = {}) {
1722
2241
  );
1723
2242
  }
1724
2243
  const [prepErr, submission] = await tryCatch(
1725
- config.data.query(api.withdrawals.queries.prepareSubmission, {
2244
+ config._data.query(api.withdrawals.queries.prepareSubmission, {
1726
2245
  withdrawalId: created.id
1727
2246
  })
1728
2247
  );
1729
2248
  if (prepErr) {
1730
2249
  return await handleSubmissionFailure(
1731
- { data: config.data },
2250
+ { _data: config._data },
1732
2251
  created.id,
1733
2252
  mapCreateError2(fromConvexError(prepErr))
1734
2253
  );
@@ -1736,7 +2255,7 @@ function createWithdrawalsClient(config = {}) {
1736
2255
  const destinationAddress = submission?.destinationAddress;
1737
2256
  if (!submission || !destinationAddress) {
1738
2257
  return await handleSubmissionFailure(
1739
- { data: config.data },
2258
+ { _data: config._data },
1740
2259
  created.id,
1741
2260
  new CapxulError({
1742
2261
  code: "NETWORK_ERROR",
@@ -1745,6 +2264,7 @@ function createWithdrawalsClient(config = {}) {
1745
2264
  })
1746
2265
  );
1747
2266
  }
2267
+ const token = resolvePaymentToken(submission.amount.currency);
1748
2268
  const [transferErr, transferOk] = await tryCatch(
1749
2269
  transferAsOwner(
1750
2270
  {
@@ -1752,22 +2272,22 @@ function createWithdrawalsClient(config = {}) {
1752
2272
  signing: config.signing
1753
2273
  },
1754
2274
  {
1755
- tokenAddress: resolvePaymentTokenAddress(submission.amount.currency),
2275
+ tokenAddress: token.address,
1756
2276
  recipientAddress: destinationAddress,
1757
- amount: toTokenUnits(submission.amount.value, 6)
2277
+ amount: toTokenUnits(submission.amount.value, token.decimals)
1758
2278
  }
1759
2279
  )
1760
2280
  );
1761
2281
  if (transferErr) {
1762
2282
  return await handleSubmissionFailure(
1763
- { data: config.data },
2283
+ { _data: config._data },
1764
2284
  created.id,
1765
2285
  mapCreateError2(fromConvexError(transferErr))
1766
2286
  );
1767
2287
  }
1768
2288
  if (!transferOk.success) {
1769
2289
  return await handleSubmissionFailure(
1770
- { data: config.data },
2290
+ { _data: config._data },
1771
2291
  created.id,
1772
2292
  new CapxulError({
1773
2293
  code: "NETWORK_ERROR",
@@ -1781,7 +2301,7 @@ function createWithdrawalsClient(config = {}) {
1781
2301
  );
1782
2302
  }
1783
2303
  const [recordErr] = await tryCatch(
1784
- config.data.mutation(api.withdrawals.mutations.recordSubmitted, {
2304
+ config._data.mutation(api.withdrawals.mutations.recordSubmitted, {
1785
2305
  withdrawalId: created.id,
1786
2306
  txHash: transferOk.txHash,
1787
2307
  userOpHash: transferOk.userOpHash
@@ -1805,11 +2325,11 @@ function createWithdrawalsClient(config = {}) {
1805
2325
  return [null, created];
1806
2326
  },
1807
2327
  retrieve: async (withdrawalId) => {
1808
- if (!config.data) {
2328
+ if (!config._data) {
1809
2329
  return stub("withdrawals.retrieve");
1810
2330
  }
1811
2331
  const [err, raw] = await tryCatch(
1812
- config.data.query(api.withdrawals.queries.retrieve, { withdrawalId })
2332
+ config._data.query(api.withdrawals.queries.retrieve, { withdrawalId })
1813
2333
  );
1814
2334
  if (err) {
1815
2335
  return [fromConvexError(err), null];
@@ -1827,11 +2347,11 @@ function createWithdrawalsClient(config = {}) {
1827
2347
  return [null, withdrawal];
1828
2348
  },
1829
2349
  list: async (input) => {
1830
- if (!config.data) {
2350
+ if (!config._data) {
1831
2351
  return stub("withdrawals.list");
1832
2352
  }
1833
2353
  const [err, raw] = await tryCatch(
1834
- config.data.query(api.withdrawals.queries.list, {
2354
+ config._data.query(api.withdrawals.queries.list, {
1835
2355
  limit: input?.limit,
1836
2356
  cursor: input?.cursor
1837
2357
  })
@@ -1842,13 +2362,13 @@ function createWithdrawalsClient(config = {}) {
1842
2362
  return [null, raw];
1843
2363
  },
1844
2364
  recordCompleted: async (input) => {
1845
- if (!config.data) {
2365
+ if (!config._data) {
1846
2366
  return stub(
1847
2367
  "withdrawals.recordCompleted"
1848
2368
  );
1849
2369
  }
1850
2370
  const [err] = await tryCatch(
1851
- config.data.mutation(api.withdrawals.mutations.recordCompleted, {
2371
+ config._data.mutation(api.withdrawals.mutations.recordCompleted, {
1852
2372
  withdrawalId: input.withdrawalId,
1853
2373
  txHash: input.txHash
1854
2374
  })
@@ -1873,13 +2393,13 @@ function createOrgWithdrawalsClient(config = {}) {
1873
2393
  * orchestration ships in W3+.
1874
2394
  */
1875
2395
  create: async (input) => {
1876
- if (!config.data) {
2396
+ if (!config._data) {
1877
2397
  return stub(
1878
2398
  "organizations.withdrawals.create"
1879
2399
  );
1880
2400
  }
1881
2401
  const [err, raw] = await tryCatch(
1882
- config.data.mutation(api.withdrawals.mutations.createOrg, {
2402
+ config._data.mutation(api.withdrawals.mutations.createOrg, {
1883
2403
  organizationId: input.organizationId,
1884
2404
  amount: input.amount,
1885
2405
  destination: {
@@ -1906,13 +2426,13 @@ function createOrgWithdrawalsClient(config = {}) {
1906
2426
  return [null, created];
1907
2427
  },
1908
2428
  retrieve: async (input) => {
1909
- if (!config.data) {
2429
+ if (!config._data) {
1910
2430
  return stub(
1911
2431
  "organizations.withdrawals.retrieve"
1912
2432
  );
1913
2433
  }
1914
2434
  const [err, raw] = await tryCatch(
1915
- config.data.query(api.withdrawals.queries.retrieve, {
2435
+ config._data.query(api.withdrawals.queries.retrieve, {
1916
2436
  withdrawalId: input.withdrawalId
1917
2437
  })
1918
2438
  );
@@ -1942,13 +2462,13 @@ function createOrgWithdrawalsClient(config = {}) {
1942
2462
  return [null, withdrawal];
1943
2463
  },
1944
2464
  list: async (input) => {
1945
- if (!config.data) {
2465
+ if (!config._data) {
1946
2466
  return stub(
1947
2467
  "organizations.withdrawals.list"
1948
2468
  );
1949
2469
  }
1950
2470
  const [err, raw] = await tryCatch(
1951
- config.data.query(api.withdrawals.queries.listOrg, {
2471
+ config._data.query(api.withdrawals.queries.listOrg, {
1952
2472
  organizationId: input.organizationId,
1953
2473
  limit: input.limit,
1954
2474
  cursor: input.cursor
@@ -1967,7 +2487,7 @@ async function handleSubmissionFailure(config, withdrawalId, error) {
1967
2487
  }
1968
2488
  async function bestEffortMarkFailed2(config, withdrawalId, error) {
1969
2489
  await tryCatch(
1970
- config.data.mutation(api.withdrawals.mutations.markFailed, {
2490
+ config._data.mutation(api.withdrawals.mutations.markFailed, {
1971
2491
  withdrawalId,
1972
2492
  errorCode: error.code,
1973
2493
  errorMessage: error.message
@@ -2046,13 +2566,13 @@ function createWebhookEventsClient() {
2046
2566
  function createOrgExternalAccountsClient(config) {
2047
2567
  return {
2048
2568
  create: async (input) => {
2049
- if (!config.data) {
2569
+ if (!config._data) {
2050
2570
  return stub(
2051
2571
  "organizations.externalAccounts.create"
2052
2572
  );
2053
2573
  }
2054
2574
  const [err, raw] = await tryCatch(
2055
- config.data.mutation(api.externalAccounts.mutations.createOrg, {
2575
+ config._data.mutation(api.externalAccounts.mutations.createOrg, {
2056
2576
  organizationId: input.organizationId,
2057
2577
  kind: input.kind,
2058
2578
  label: input.label,
@@ -2066,10 +2586,7 @@ function createOrgExternalAccountsClient(config) {
2066
2586
  })
2067
2587
  );
2068
2588
  if (err) {
2069
- return [
2070
- fromConvexError(err),
2071
- null
2072
- ];
2589
+ return [fromConvexError(err), null];
2073
2590
  }
2074
2591
  if (!raw) {
2075
2592
  return [
@@ -2080,21 +2597,16 @@ function createOrgExternalAccountsClient(config) {
2080
2597
  null
2081
2598
  ];
2082
2599
  }
2083
- return [
2084
- null,
2085
- brandExternalAccount(
2086
- raw
2087
- )
2088
- ];
2600
+ return [null, brandExternalAccount(raw)];
2089
2601
  },
2090
2602
  list: async (input) => {
2091
- if (!config.data) {
2603
+ if (!config._data) {
2092
2604
  return stub(
2093
2605
  "organizations.externalAccounts.list"
2094
2606
  );
2095
2607
  }
2096
2608
  const [err, result] = await tryCatch(
2097
- config.data.query(api.externalAccounts.queries.listOrg, {
2609
+ config._data.query(api.externalAccounts.queries.listOrg, {
2098
2610
  organizationId: input.organizationId,
2099
2611
  limit: input.limit,
2100
2612
  cursor: input.cursor
@@ -2104,9 +2616,7 @@ function createOrgExternalAccountsClient(config) {
2104
2616
  return [fromConvexError(err), null];
2105
2617
  }
2106
2618
  const branded = result.data.map(
2107
- (row) => brandExternalAccount(
2108
- row
2109
- )
2619
+ (row) => brandExternalAccount(row)
2110
2620
  );
2111
2621
  return [
2112
2622
  null,
@@ -2118,17 +2628,66 @@ function createOrgExternalAccountsClient(config) {
2118
2628
  ];
2119
2629
  },
2120
2630
  retrieve: async (input) => {
2121
- if (!config.data) {
2631
+ if (!config._data) {
2122
2632
  return stub(
2123
2633
  "organizations.externalAccounts.retrieve"
2124
2634
  );
2125
2635
  }
2126
2636
  const [err, raw] = await tryCatch(
2127
- config.data.query(api.externalAccounts.queries.retrieveOrg, {
2637
+ config._data.query(api.externalAccounts.queries.retrieveOrg, {
2638
+ organizationId: input.organizationId,
2639
+ externalAccountId: input.externalAccountId
2640
+ })
2641
+ );
2642
+ if (err) {
2643
+ return [fromConvexError(err), null];
2644
+ }
2645
+ if (!raw) {
2646
+ return [
2647
+ new CapxulError({
2648
+ code: "NOT_FOUND",
2649
+ message: `external_account ${input.externalAccountId} not found`
2650
+ }),
2651
+ null
2652
+ ];
2653
+ }
2654
+ return [null, brandExternalAccount(raw)];
2655
+ },
2656
+ remove: async (input) => {
2657
+ if (!config._data) {
2658
+ return stub("organizations.externalAccounts.remove");
2659
+ }
2660
+ const [err] = await tryCatch(
2661
+ config._data.mutation(api.externalAccounts.mutations.removeOrg, {
2128
2662
  organizationId: input.organizationId,
2129
2663
  externalAccountId: input.externalAccountId
2130
2664
  })
2131
2665
  );
2666
+ if (err) {
2667
+ return [fromConvexError(err), null];
2668
+ }
2669
+ return [null, void 0];
2670
+ }
2671
+ };
2672
+ }
2673
+ function createOrgSubAccountsClient(config) {
2674
+ return {
2675
+ create: async (input) => {
2676
+ if (!config._data) {
2677
+ return stub(
2678
+ "organizations.subAccounts.create"
2679
+ );
2680
+ }
2681
+ const [err, raw] = await tryCatch(
2682
+ config._data.mutation(api.subAccounts.mutations.create, {
2683
+ parent: {
2684
+ kind: "organization",
2685
+ id: input.organizationId
2686
+ },
2687
+ name: input.name,
2688
+ purpose: input.purpose
2689
+ })
2690
+ );
2132
2691
  if (err) {
2133
2692
  return [
2134
2693
  fromConvexError(err),
@@ -2139,28 +2698,95 @@ function createOrgExternalAccountsClient(config) {
2139
2698
  return [
2140
2699
  new CapxulError({
2141
2700
  code: "NOT_FOUND",
2142
- message: `external_account ${input.externalAccountId} not found`
2701
+ message: "sub_account creation returned no resource"
2143
2702
  }),
2144
2703
  null
2145
2704
  ];
2146
2705
  }
2706
+ const [brandErr, branded] = tryBrandSubAccount(raw);
2707
+ if (brandErr) {
2708
+ return [brandErr, null];
2709
+ }
2710
+ return [null, branded];
2711
+ },
2712
+ list: async (input) => {
2713
+ if (!config._data) {
2714
+ return stub(
2715
+ "organizations.subAccounts.list"
2716
+ );
2717
+ }
2718
+ const [err, rows] = await tryCatch(
2719
+ config._data.query(api.subAccounts.queries.listByOrganization, {
2720
+ organizationId: input.organizationId
2721
+ })
2722
+ );
2723
+ if (err) {
2724
+ return [
2725
+ fromConvexError(err),
2726
+ null
2727
+ ];
2728
+ }
2729
+ const branded = [];
2730
+ for (const row of rows) {
2731
+ const [brandErr, value] = tryBrandSubAccount(row);
2732
+ if (brandErr) {
2733
+ return [brandErr, null];
2734
+ }
2735
+ branded.push(value);
2736
+ }
2147
2737
  return [
2148
2738
  null,
2149
- brandExternalAccount(
2150
- raw
2151
- )
2739
+ {
2740
+ object: "list",
2741
+ data: branded,
2742
+ page: { hasMore: false }
2743
+ }
2152
2744
  ];
2153
2745
  },
2746
+ retrieve: async (input) => {
2747
+ if (!config._data) {
2748
+ return stub(
2749
+ "organizations.subAccounts.retrieve"
2750
+ );
2751
+ }
2752
+ const [err, raw] = await tryCatch(
2753
+ config._data.query(api.subAccounts.queries.retrieve, {
2754
+ subAccountId: input.subAccountId
2755
+ })
2756
+ );
2757
+ if (err) {
2758
+ return [
2759
+ fromConvexError(err),
2760
+ null
2761
+ ];
2762
+ }
2763
+ if (!raw) {
2764
+ return [
2765
+ new CapxulError({
2766
+ code: "NOT_FOUND",
2767
+ message: `sub_account ${input.subAccountId} not found`
2768
+ }),
2769
+ null
2770
+ ];
2771
+ }
2772
+ const [brandErr, branded] = tryBrandSubAccount(raw);
2773
+ if (brandErr) {
2774
+ return [
2775
+ brandErr,
2776
+ null
2777
+ ];
2778
+ }
2779
+ return [null, branded];
2780
+ },
2154
2781
  remove: async (input) => {
2155
- if (!config.data) {
2782
+ if (!config._data) {
2156
2783
  return stub(
2157
- "organizations.externalAccounts.remove"
2784
+ "organizations.subAccounts.remove"
2158
2785
  );
2159
2786
  }
2160
- const [err] = await tryCatch(
2161
- config.data.mutation(api.externalAccounts.mutations.removeOrg, {
2162
- organizationId: input.organizationId,
2163
- externalAccountId: input.externalAccountId
2787
+ const [err, raw] = await tryCatch(
2788
+ config._data.mutation(api.subAccounts.mutations.archive, {
2789
+ subAccountId: input.subAccountId
2164
2790
  })
2165
2791
  );
2166
2792
  if (err) {
@@ -2169,74 +2795,399 @@ function createOrgExternalAccountsClient(config) {
2169
2795
  null
2170
2796
  ];
2171
2797
  }
2172
- return [null, void 0];
2173
- }
2174
- };
2175
- }
2176
- function createOrganizationsClient(config = {}) {
2177
- return {
2178
- create: async () => stub("organizations.create"),
2179
- retrieve: async () => stub("organizations.retrieve"),
2180
- list: async () => stub("organizations.list"),
2181
- update: async () => stub("organizations.update"),
2182
- safes: {
2798
+ if (!raw) {
2799
+ return [
2800
+ new CapxulError({
2801
+ code: "NOT_FOUND",
2802
+ message: `sub_account ${input.subAccountId} not found`
2803
+ }),
2804
+ null
2805
+ ];
2806
+ }
2807
+ const [brandErr, branded] = tryBrandSubAccount(raw);
2808
+ if (brandErr) {
2809
+ return [
2810
+ brandErr,
2811
+ null
2812
+ ];
2813
+ }
2814
+ return [null, branded];
2815
+ }
2816
+ };
2817
+ }
2818
+ function createOrganizationsClient(config = {}) {
2819
+ return {
2820
+ create: async (input) => {
2821
+ if (!config._data) {
2822
+ return stub("organizations.create");
2823
+ }
2824
+ if (input.country !== void 0) {
2825
+ return [
2826
+ new CapxulError({
2827
+ code: "INVALID_INPUT",
2828
+ message: "organizations.create does not support country yet \u2014 backend mutation only accepts name.",
2829
+ details: { field: "country" }
2830
+ }),
2831
+ null
2832
+ ];
2833
+ }
2834
+ try {
2835
+ const orgId = await config._data.mutation(api.org.mutations.create, {
2836
+ name: input.name
2837
+ });
2838
+ const org = await config._data.query(api.org.queries.retrieve, {
2839
+ orgId
2840
+ });
2841
+ if (!org) {
2842
+ return [
2843
+ new CapxulError({
2844
+ code: "NETWORK_ERROR",
2845
+ message: "organization created but could not be retrieved"
2846
+ }),
2847
+ null
2848
+ ];
2849
+ }
2850
+ return [null, org];
2851
+ } catch (cause) {
2852
+ return [fromConvexError(cause), null];
2853
+ }
2854
+ },
2855
+ retrieve: async (organizationId) => {
2856
+ if (!config._data) {
2857
+ return stub("organizations.retrieve");
2858
+ }
2859
+ try {
2860
+ const orgId = organizationId.replace(/^org_/, "");
2861
+ const org = await config._data.query(api.org.queries.retrieve, {
2862
+ orgId
2863
+ });
2864
+ if (!org) {
2865
+ return [
2866
+ new CapxulError({
2867
+ code: "NOT_FOUND",
2868
+ message: `organization ${organizationId} not found`
2869
+ }),
2870
+ null
2871
+ ];
2872
+ }
2873
+ return [null, org];
2874
+ } catch (cause) {
2875
+ return [fromConvexError(cause), null];
2876
+ }
2877
+ },
2878
+ list: async (input) => {
2879
+ if (!config._data) {
2880
+ return stub("organizations.list");
2881
+ }
2882
+ try {
2883
+ const page = await config._data.query(api.org.queries.list, {
2884
+ limit: input?.limit,
2885
+ cursor: input?.cursor
2886
+ });
2887
+ const result = {
2888
+ object: "list",
2889
+ data: page.data,
2890
+ page: {
2891
+ hasMore: page.hasMore,
2892
+ cursor: page.nextCursor
2893
+ }
2894
+ };
2895
+ return [null, result];
2896
+ } catch (cause) {
2897
+ return [fromConvexError(cause), null];
2898
+ }
2899
+ },
2900
+ update: async (input) => {
2901
+ if (!config._data) {
2902
+ return stub("organizations.update");
2903
+ }
2904
+ try {
2905
+ const orgId = input.organizationId.replace(/^org_/, "");
2906
+ const org = await config._data.mutation(api.org.mutations.update, {
2907
+ orgId,
2908
+ name: input.name
2909
+ });
2910
+ if (!org) {
2911
+ return [
2912
+ new CapxulError({
2913
+ code: "NOT_FOUND",
2914
+ message: `organization ${input.organizationId} not found`
2915
+ }),
2916
+ null
2917
+ ];
2918
+ }
2919
+ return [null, org];
2920
+ } catch (cause) {
2921
+ return [fromConvexError(cause), null];
2922
+ }
2923
+ },
2924
+ safes: {
2925
+ retrieve: async (input) => {
2926
+ if (!config._data) {
2927
+ return stub("organizations.safes.retrieve");
2928
+ }
2929
+ try {
2930
+ const safe = await config._data.query(
2931
+ api.safe.queries.retrieveOrganizationSafe,
2932
+ input
2933
+ );
2934
+ if (!safe) {
2935
+ return [
2936
+ new CapxulError({
2937
+ code: "NOT_FOUND",
2938
+ message: `safe ${input.safeId} not found`
2939
+ }),
2940
+ null
2941
+ ];
2942
+ }
2943
+ return [null, safe];
2944
+ } catch (cause) {
2945
+ return [
2946
+ fromConvexError(cause),
2947
+ null
2948
+ ];
2949
+ }
2950
+ }
2951
+ },
2952
+ treasury: {
2953
+ retrieve: async (organizationId) => {
2954
+ if (!config._data) {
2955
+ return stub(
2956
+ "organizations.treasury.retrieve"
2957
+ );
2958
+ }
2959
+ try {
2960
+ const orgId = organizationId.replace(/^org_/, "");
2961
+ const raw = await config._data.query(
2962
+ api.safe.queries.getOrgTreasuryBalance,
2963
+ { orgId }
2964
+ );
2965
+ if (!raw) {
2966
+ return [
2967
+ new CapxulError({
2968
+ code: "NOT_FOUND",
2969
+ message: `treasury for organization ${organizationId} not found`
2970
+ }),
2971
+ null
2972
+ ];
2973
+ }
2974
+ const treasury = {
2975
+ object: "treasury",
2976
+ id: toTreasuryId(`try_${orgId}`),
2977
+ organizationId,
2978
+ status: "active",
2979
+ safeId: toSafeId(
2980
+ `safe_${raw.safeAddress.replace(/^0x/, "").toLowerCase()}`
2981
+ ),
2982
+ totalBalance: { value: "0", currency: "USD" },
2983
+ positions: raw.tokens.map((t) => ({
2984
+ symbol: t.symbol,
2985
+ contractAddress: t.tokenAddress,
2986
+ amount: t.balance
2987
+ })),
2988
+ asOf: raw.lastUpdatedAt ? new Date(raw.lastUpdatedAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString()
2989
+ };
2990
+ return [null, treasury];
2991
+ } catch (cause) {
2992
+ return [
2993
+ fromConvexError(cause),
2994
+ null
2995
+ ];
2996
+ }
2997
+ }
2998
+ },
2999
+ members: {
3000
+ list: async (input) => {
3001
+ if (!config._data?.action) {
3002
+ return stub("organizations.members.list");
3003
+ }
3004
+ try {
3005
+ const orgId = input.organizationId.replace(/^org_/, "");
3006
+ const page = await config._data.action(api.org.actions.membersList, {
3007
+ organizationId: orgId,
3008
+ status: input.status,
3009
+ limit: input.limit,
3010
+ cursor: input.cursor
3011
+ });
3012
+ return [null, page];
3013
+ } catch (cause) {
3014
+ return [fromConvexError(cause), null];
3015
+ }
3016
+ },
3017
+ retrieve: async (input) => {
3018
+ if (!config._data?.action) {
3019
+ return stub("organizations.members.retrieve");
3020
+ }
3021
+ try {
3022
+ const orgId = input.organizationId.replace(/^org_/, "");
3023
+ const memberId = input.memberId.replace(/^mb_/, "");
3024
+ const member = await config._data.action(
3025
+ api.org.actions.retrieveMember,
3026
+ {
3027
+ organizationId: orgId,
3028
+ memberId
3029
+ }
3030
+ );
3031
+ return [null, member];
3032
+ } catch (cause) {
3033
+ return [fromConvexError(cause), null];
3034
+ }
3035
+ },
3036
+ invite: async (input) => {
3037
+ if (!config._data?.action) {
3038
+ return stub("organizations.members.invite");
3039
+ }
3040
+ try {
3041
+ const orgId = input.organizationId.replace(/^org_/, "");
3042
+ const result = await config._data.action(
3043
+ api.org.actions.inviteMember,
3044
+ {
3045
+ organizationId: orgId,
3046
+ email: input.email,
3047
+ role: input.role
3048
+ }
3049
+ );
3050
+ return [null, result];
3051
+ } catch (cause) {
3052
+ return [fromConvexError(cause), null];
3053
+ }
3054
+ },
3055
+ accept: async (input) => {
3056
+ if (!config._data?.action) {
3057
+ return stub("organizations.members.accept");
3058
+ }
3059
+ try {
3060
+ const member = await config._data.action(
3061
+ api.org.actions.acceptInvitation,
3062
+ { token: input.token }
3063
+ );
3064
+ return [null, member];
3065
+ } catch (cause) {
3066
+ return [fromConvexError(cause), null];
3067
+ }
3068
+ },
3069
+ updateRole: async (input) => {
3070
+ if (!config._data?.action) {
3071
+ return stub("organizations.members.updateRole");
3072
+ }
3073
+ try {
3074
+ const orgId = input.organizationId.replace(/^org_/, "");
3075
+ const memberId = input.memberId.replace(/^mb_/, "");
3076
+ const member = await config._data.action(
3077
+ api.org.actions.updateMemberRole,
3078
+ {
3079
+ organizationId: orgId,
3080
+ memberId,
3081
+ role: input.role
3082
+ }
3083
+ );
3084
+ return [null, member];
3085
+ } catch (cause) {
3086
+ return [fromConvexError(cause), null];
3087
+ }
3088
+ },
3089
+ revoke: async (input) => {
3090
+ if (!config._data?.action) {
3091
+ return stub("organizations.members.revoke");
3092
+ }
3093
+ try {
3094
+ const orgId = input.organizationId.replace(/^org_/, "");
3095
+ const memberId = input.memberId.replace(/^mb_/, "");
3096
+ const member = await config._data.action(
3097
+ api.org.actions.revokeMember,
3098
+ {
3099
+ organizationId: orgId,
3100
+ memberId
3101
+ }
3102
+ );
3103
+ return [null, member];
3104
+ } catch (cause) {
3105
+ return [fromConvexError(cause), null];
3106
+ }
3107
+ },
3108
+ remove: async (input) => {
3109
+ if (!config._data?.action) {
3110
+ return stub("organizations.members.remove");
3111
+ }
3112
+ try {
3113
+ const orgId = input.organizationId.replace(/^org_/, "");
3114
+ const memberId = input.memberId.replace(/^mb_/, "");
3115
+ await config._data.action(api.org.actions.removeMember, {
3116
+ organizationId: orgId,
3117
+ memberId
3118
+ });
3119
+ return [null, void 0];
3120
+ } catch (cause) {
3121
+ return [fromConvexError(cause), null];
3122
+ }
3123
+ },
3124
+ resend: async (input) => {
3125
+ if (!config._data?.action) {
3126
+ return stub("organizations.members.resend");
3127
+ }
3128
+ try {
3129
+ const orgId = input.organizationId.replace(/^org_/, "");
3130
+ const memberId = input.memberId.replace(/^mb_/, "");
3131
+ const result = await config._data.action(
3132
+ api.org.actions.resendInvitation,
3133
+ {
3134
+ organizationId: orgId,
3135
+ memberId
3136
+ }
3137
+ );
3138
+ return [null, result];
3139
+ } catch (cause) {
3140
+ return [fromConvexError(cause), null];
3141
+ }
3142
+ }
3143
+ },
3144
+ apiKeys: createApiKeysClient(),
3145
+ subAccounts: createOrgSubAccountsClient(config),
3146
+ externalAccounts: createOrgExternalAccountsClient(config),
3147
+ balanceLedger: {
3148
+ list: async (input) => {
3149
+ if (!config._data) {
3150
+ return stub(
3151
+ "organizations.balanceLedger.list"
3152
+ );
3153
+ }
3154
+ try {
3155
+ const orgId = input.organizationId.replace(/^org_/, "");
3156
+ const page = await config._data.query(
3157
+ api.balanceLedger.queries.listForOrg,
3158
+ { orgId, limit: input.limit, cursor: input.cursor }
3159
+ );
3160
+ return [null, page];
3161
+ } catch (cause) {
3162
+ return [fromConvexError(cause), null];
3163
+ }
3164
+ },
2183
3165
  retrieve: async (input) => {
2184
- if (!config.data) {
2185
- return stub("organizations.safes.retrieve");
3166
+ if (!config._data) {
3167
+ return stub(
3168
+ "organizations.balanceLedger.retrieve"
3169
+ );
2186
3170
  }
2187
3171
  try {
2188
- const safe = await config.data.query(
2189
- api.safe.queries.retrieveOrganizationSafe,
2190
- input
3172
+ const entry = await config._data.query(
3173
+ api.balanceLedger.queries.retrieve,
3174
+ { entryId: input.entryId }
2191
3175
  );
2192
- if (!safe) {
3176
+ if (!entry) {
2193
3177
  return [
2194
3178
  new CapxulError({
2195
3179
  code: "NOT_FOUND",
2196
- message: `safe ${input.safeId} not found`
3180
+ message: `balance_ledger_entry ${input.entryId} not found`
2197
3181
  }),
2198
3182
  null
2199
3183
  ];
2200
3184
  }
2201
- return [null, safe];
3185
+ return [null, entry];
2202
3186
  } catch (cause) {
2203
- return [
2204
- fromConvexError(cause),
2205
- null
2206
- ];
3187
+ return [fromConvexError(cause), null];
2207
3188
  }
2208
3189
  }
2209
3190
  },
2210
- treasury: {
2211
- retrieve: async () => stub("organizations.treasury.retrieve")
2212
- },
2213
- members: {
2214
- list: async () => stub("organizations.members.list"),
2215
- retrieve: async () => stub("organizations.members.retrieve"),
2216
- invite: async () => stub("organizations.members.invite"),
2217
- updateRole: async () => stub("organizations.members.updateRole"),
2218
- remove: async () => stub("organizations.members.remove")
2219
- },
2220
- apiKeys: createApiKeysClient(),
2221
- kybProfile: {
2222
- start: async () => stub("organizations.kybProfile.start"),
2223
- retrieve: async () => stub("organizations.kybProfile.retrieve")
2224
- },
2225
- subAccounts: {
2226
- create: async () => stub("organizations.subAccounts.create"),
2227
- list: async () => stub("organizations.subAccounts.list"),
2228
- retrieve: async () => stub("organizations.subAccounts.retrieve"),
2229
- remove: async () => stub("organizations.subAccounts.remove")
2230
- },
2231
- externalAccounts: createOrgExternalAccountsClient(config),
2232
- balanceLedger: {
2233
- list: async () => stub(
2234
- "organizations.balanceLedger.list"
2235
- ),
2236
- retrieve: async () => stub(
2237
- "organizations.balanceLedger.retrieve"
2238
- )
2239
- },
2240
3191
  payments: createOrgPaymentsClient(),
2241
3192
  transfers: createOrgTransfersClient(),
2242
3193
  withdrawals: createOrgWithdrawalsClient(config),
@@ -2246,14 +3197,6 @@ function createOrganizationsClient(config = {}) {
2246
3197
  };
2247
3198
  }
2248
3199
 
2249
- // src/core/sub-accounts.ts
2250
- function createSubAccountsClient() {
2251
- return {
2252
- retrieve: async () => stub("subAccounts.retrieve"),
2253
- remove: async () => stub("subAccounts.remove")
2254
- };
2255
- }
2256
-
2257
3200
  // src/core/token-transfers.ts
2258
3201
  var toTokenTransferId = (raw) => {
2259
3202
  if (typeof raw !== "string" || raw.length === 0) {
@@ -2272,11 +3215,11 @@ function brandRow(row) {
2272
3215
  function createTokenTransfersClient(config = {}) {
2273
3216
  return {
2274
3217
  list: async (input) => {
2275
- if (!config.data) {
3218
+ if (!config._data) {
2276
3219
  return stub("tokenTransfers.list");
2277
3220
  }
2278
3221
  try {
2279
- const raw = await config.data.query(
3222
+ const raw = await config._data.query(
2280
3223
  api.tokenTransfers.queries.list,
2281
3224
  {
2282
3225
  limit: input?.limit,
@@ -2310,11 +3253,11 @@ function createTokenTransfersClient(config = {}) {
2310
3253
  }
2311
3254
  },
2312
3255
  retrieve: async (input) => {
2313
- if (!config.data) {
3256
+ if (!config._data) {
2314
3257
  return stub("tokenTransfers.retrieve");
2315
3258
  }
2316
3259
  try {
2317
- const raw = await config.data.query(
3260
+ const raw = await config._data.query(
2318
3261
  api.tokenTransfers.queries.getByTxLogIndex,
2319
3262
  {
2320
3263
  txHash: input.txHash,
@@ -2384,7 +3327,7 @@ function createAuthFlowMachine(client) {
2384
3327
  }),
2385
3328
  verifyOtp: fromPromise(
2386
3329
  async ({ input, signal }) => {
2387
- const [error, session] = await client.auth.verifyOtp(
3330
+ const [error, result] = await client.auth.verifyOtp(
2388
3331
  {
2389
3332
  email: input.email,
2390
3333
  otp: input.code
@@ -2392,7 +3335,14 @@ function createAuthFlowMachine(client) {
2392
3335
  { signal }
2393
3336
  );
2394
3337
  if (error) throw error;
2395
- return session;
3338
+ if (result.kind === "bootstrap_required") {
3339
+ throw new CapxulError({
3340
+ code: "ACTION_REQUIRED",
3341
+ message: "OTP verified but auth bootstrap is required. Use createAuthBootstrapFlowMachine for product sign-up.",
3342
+ details: { reason: result.reason }
3343
+ });
3344
+ }
3345
+ return result.session;
2396
3346
  }
2397
3347
  ),
2398
3348
  signOut: fromPromise(async () => {
@@ -2640,6 +3590,327 @@ function emailDomain(email) {
2640
3590
  const domain = email.split("@")[1]?.trim().toLowerCase();
2641
3591
  return domain || "unknown";
2642
3592
  }
3593
+ var initialContext = {
3594
+ email: null,
3595
+ code: null,
3596
+ username: null,
3597
+ bootstrapToken: null,
3598
+ bootstrapReason: null,
3599
+ session: null,
3600
+ account: null,
3601
+ safe: null,
3602
+ error: null
3603
+ };
3604
+ function createAuthBootstrapFlowMachine(client) {
3605
+ return setup({
3606
+ types: {},
3607
+ actors: {
3608
+ sendOtp: fromPromise(async ({ input, signal }) => {
3609
+ const [error] = await client.auth.sendOtp(
3610
+ { email: input.email },
3611
+ { signal }
3612
+ );
3613
+ if (error) throw error;
3614
+ }),
3615
+ verifyOtp: fromPromise(
3616
+ async ({ input, signal }) => {
3617
+ const [error, result] = await client.auth.verifyOtp(
3618
+ { email: input.email, otp: input.code },
3619
+ { signal }
3620
+ );
3621
+ if (error) throw error;
3622
+ return result;
3623
+ }
3624
+ ),
3625
+ completeBootstrap: fromPromise(async ({ input }) => {
3626
+ const [error, result] = await client.auth.completeBootstrap(input);
3627
+ if (error) throw error;
3628
+ return result;
3629
+ }),
3630
+ signOut: fromPromise(async () => {
3631
+ const [error] = await client.auth.signOut();
3632
+ if (error) throw error;
3633
+ })
3634
+ },
3635
+ actions: {
3636
+ trackOtpRequested: ({ context }) => {
3637
+ if (!context.email) return;
3638
+ track("auth_otp_requested", {
3639
+ email_domain: emailDomain2(context.email)
3640
+ });
3641
+ },
3642
+ trackFailed: ({ event }) => {
3643
+ track("auth_failed", {
3644
+ auth_type: "email_otp",
3645
+ reason: errorFromEvent2(event).code
3646
+ });
3647
+ },
3648
+ trackTimeoutFailed: () => {
3649
+ track("auth_failed", {
3650
+ auth_type: "email_otp",
3651
+ reason: "timeout"
3652
+ });
3653
+ },
3654
+ trackVerified: () => {
3655
+ track("auth_verified", { auth_type: "email_otp" });
3656
+ },
3657
+ trackBootstrapRequired: ({ context }) => {
3658
+ track("auth_verified", {
3659
+ auth_type: "email_otp",
3660
+ auth_mode: context.bootstrapReason ?? "bootstrap_required"
3661
+ });
3662
+ },
3663
+ identifyAndTrack: ({ context }) => {
3664
+ if (!context.session) return;
3665
+ identify(context.session.authUserId, {
3666
+ email_domain: emailDomain2(context.session.email)
3667
+ });
3668
+ track("auth_identified", {
3669
+ email_domain: emailDomain2(context.session.email)
3670
+ });
3671
+ },
3672
+ trackSignedOut: () => {
3673
+ track("auth_signed_out");
3674
+ }
3675
+ }
3676
+ }).createMachine({
3677
+ id: "authBootstrap",
3678
+ initial: "email",
3679
+ context: initialContext,
3680
+ states: {
3681
+ email: {
3682
+ on: {
3683
+ ENTER_EMAIL: {
3684
+ actions: assign({
3685
+ email: ({ event }) => event.email,
3686
+ error: () => null
3687
+ })
3688
+ },
3689
+ REQUEST_OTP: { target: "sending_otp" }
3690
+ }
3691
+ },
3692
+ sending_otp: {
3693
+ invoke: {
3694
+ src: "sendOtp",
3695
+ input: ({ context }) => ({ email: requireEmail2(context) }),
3696
+ onDone: { target: "otp_requested", actions: "trackOtpRequested" },
3697
+ onError: {
3698
+ target: "otp_requested",
3699
+ actions: [
3700
+ assign({ error: ({ event }) => errorFromEvent2(event) }),
3701
+ "trackFailed"
3702
+ ]
3703
+ }
3704
+ },
3705
+ after: {
3706
+ [FLOW_INVOKE_TIMEOUT_MS]: {
3707
+ target: "otp_requested",
3708
+ actions: [
3709
+ assign({ error: () => timeoutError2("sending_otp") }),
3710
+ "trackTimeoutFailed"
3711
+ ]
3712
+ }
3713
+ }
3714
+ },
3715
+ otp_requested: {
3716
+ on: {
3717
+ ENTER_OTP: {
3718
+ actions: assign({
3719
+ code: ({ event }) => event.code,
3720
+ error: () => null
3721
+ })
3722
+ },
3723
+ VERIFY_OTP: { target: "verifying_otp" },
3724
+ BACK: { target: "email" },
3725
+ RESET: { target: "email", actions: assign(() => initialContext) }
3726
+ }
3727
+ },
3728
+ verifying_otp: {
3729
+ invoke: {
3730
+ src: "verifyOtp",
3731
+ input: ({ context }) => ({
3732
+ email: requireEmail2(context),
3733
+ code: requireCode(context)
3734
+ }),
3735
+ onDone: [
3736
+ {
3737
+ guard: ({ event }) => event.output.kind === "existing_member",
3738
+ target: "authenticated",
3739
+ actions: [
3740
+ assign({
3741
+ session: ({ event }) => event.output.session,
3742
+ account: ({ event }) => event.output.kind === "existing_member" ? event.output.account : null,
3743
+ username: ({ event }) => event.output.kind === "existing_member" ? event.output.username : null,
3744
+ safe: ({ event }) => event.output.kind === "existing_member" ? event.output.safe : null,
3745
+ email: () => null,
3746
+ error: () => null
3747
+ }),
3748
+ "trackVerified",
3749
+ "identifyAndTrack"
3750
+ ]
3751
+ },
3752
+ {
3753
+ target: "bootstrap_required",
3754
+ actions: [
3755
+ assign({
3756
+ session: ({ event }) => event.output.session,
3757
+ bootstrapToken: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.bootstrapToken : null,
3758
+ bootstrapReason: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.reason : null,
3759
+ username: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.username ?? null : null,
3760
+ email: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.email : null,
3761
+ error: () => null
3762
+ }),
3763
+ "trackVerified",
3764
+ "trackBootstrapRequired"
3765
+ ]
3766
+ }
3767
+ ],
3768
+ onError: {
3769
+ target: "otp_requested",
3770
+ actions: [
3771
+ assign({ error: ({ event }) => errorFromEvent2(event) }),
3772
+ "trackFailed"
3773
+ ]
3774
+ }
3775
+ },
3776
+ after: {
3777
+ [FLOW_INVOKE_TIMEOUT_MS]: {
3778
+ target: "otp_requested",
3779
+ actions: [
3780
+ assign({ error: () => timeoutError2("verifying_otp") }),
3781
+ "trackTimeoutFailed"
3782
+ ]
3783
+ }
3784
+ }
3785
+ },
3786
+ bootstrap_required: {
3787
+ on: {
3788
+ ENTER_USERNAME: {
3789
+ actions: assign({
3790
+ username: ({ event }) => event.username,
3791
+ error: () => null
3792
+ })
3793
+ },
3794
+ COMPLETE_BOOTSTRAP: { target: "completing_bootstrap" },
3795
+ BACK: { target: "otp_requested" },
3796
+ RESET: { target: "email", actions: assign(() => initialContext) }
3797
+ }
3798
+ },
3799
+ completing_bootstrap: {
3800
+ invoke: {
3801
+ src: "completeBootstrap",
3802
+ input: ({ context }) => ({
3803
+ bootstrapToken: requireBootstrapToken(context),
3804
+ username: requireUsername(context)
3805
+ }),
3806
+ onDone: {
3807
+ target: "authenticated",
3808
+ actions: [
3809
+ assign({
3810
+ session: ({ event }) => event.output.session,
3811
+ account: ({ event }) => event.output.account,
3812
+ username: ({ event }) => event.output.username,
3813
+ safe: ({ event }) => event.output.safe,
3814
+ bootstrapToken: () => null,
3815
+ bootstrapReason: () => null,
3816
+ email: () => null,
3817
+ error: () => null
3818
+ }),
3819
+ "identifyAndTrack"
3820
+ ]
3821
+ },
3822
+ onError: {
3823
+ target: "bootstrap_required",
3824
+ actions: [
3825
+ assign({ error: ({ event }) => errorFromEvent2(event) }),
3826
+ "trackFailed"
3827
+ ]
3828
+ }
3829
+ },
3830
+ after: {
3831
+ [FLOW_INVOKE_TIMEOUT_MS]: {
3832
+ target: "bootstrap_required",
3833
+ actions: [
3834
+ assign({ error: () => timeoutError2("completing_bootstrap") }),
3835
+ "trackTimeoutFailed"
3836
+ ]
3837
+ }
3838
+ }
3839
+ },
3840
+ authenticated: {
3841
+ on: {
3842
+ SIGN_OUT: { target: "signing_out" }
3843
+ }
3844
+ },
3845
+ signing_out: {
3846
+ invoke: {
3847
+ src: "signOut",
3848
+ onDone: {
3849
+ target: "email",
3850
+ actions: [
3851
+ assign(() => initialContext),
3852
+ "trackSignedOut"
3853
+ ]
3854
+ },
3855
+ onError: {
3856
+ target: "error",
3857
+ actions: assign({ error: ({ event }) => errorFromEvent2(event) })
3858
+ }
3859
+ }
3860
+ },
3861
+ error: {
3862
+ on: {
3863
+ RESET: { target: "email", actions: assign(() => initialContext) }
3864
+ }
3865
+ }
3866
+ }
3867
+ });
3868
+ }
3869
+ function requireEmail2(context) {
3870
+ if (!context.email) {
3871
+ throw Errors.invalidInput("email", "Auth bootstrap requires an email.");
3872
+ }
3873
+ return context.email;
3874
+ }
3875
+ function requireCode(context) {
3876
+ if (!context.code) {
3877
+ throw Errors.invalidInput("code", "Auth bootstrap requires an OTP code.");
3878
+ }
3879
+ return context.code;
3880
+ }
3881
+ function requireBootstrapToken(context) {
3882
+ if (!context.bootstrapToken) {
3883
+ throw Errors.invalidInput(
3884
+ "bootstrapToken",
3885
+ "Auth bootstrap requires a continuation token."
3886
+ );
3887
+ }
3888
+ return context.bootstrapToken;
3889
+ }
3890
+ function requireUsername(context) {
3891
+ if (!context.username) {
3892
+ throw Errors.invalidInput("username", "Auth bootstrap requires a username.");
3893
+ }
3894
+ return context.username;
3895
+ }
3896
+ function errorFromEvent2(event) {
3897
+ const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3898
+ if (cause instanceof CapxulError || cause instanceof CapxulError2) {
3899
+ return cause;
3900
+ }
3901
+ return Errors.providerError("auth", "bootstrap", cause);
3902
+ }
3903
+ function timeoutError2(state) {
3904
+ return Errors.providerError(
3905
+ "auth",
3906
+ "bootstrap",
3907
+ new Error(`timeout: ${state} exceeded ${FLOW_INVOKE_TIMEOUT_MS}ms`)
3908
+ );
3909
+ }
3910
+ function emailDomain2(email) {
3911
+ const domain = email.split("@")[1]?.trim().toLowerCase();
3912
+ return domain || "unknown";
3913
+ }
2643
3914
  function createProvisioningMachine(client) {
2644
3915
  return setup({
2645
3916
  types: {},
@@ -2667,7 +3938,7 @@ function createProvisioningMachine(client) {
2667
3938
  const provider = context.input?.signerProvider;
2668
3939
  if (!provider) return;
2669
3940
  track("provisioning_safe_created", {
2670
- safe_address: provider.safeAddress
3941
+ safe_address: deriveSafeAddress(provider.signerAddress)
2671
3942
  });
2672
3943
  }
2673
3944
  }
@@ -2712,13 +3983,13 @@ function createProvisioningMachine(client) {
2712
3983
  },
2713
3984
  onError: {
2714
3985
  target: "error",
2715
- actions: assign({ error: ({ event }) => errorFromEvent2(event) })
3986
+ actions: assign({ error: ({ event }) => errorFromEvent3(event) })
2716
3987
  }
2717
3988
  },
2718
3989
  after: {
2719
3990
  [FLOW_INVOKE_TIMEOUT_MS]: {
2720
3991
  target: "error",
2721
- actions: assign({ error: () => timeoutError2() })
3992
+ actions: assign({ error: () => timeoutError3() })
2722
3993
  }
2723
3994
  }
2724
3995
  },
@@ -2736,7 +4007,7 @@ function createProvisioningMachine(client) {
2736
4007
  * this payload on its `onDone` transition and branches via guards
2737
4008
  * on `event.output.error`.
2738
4009
  */
2739
- output: ({ context }) => context.error ? { error: context.error } : context.account ? { account: context.account } : { error: timeoutError2() }
4010
+ output: ({ context }) => context.error ? { error: context.error } : context.account ? { account: context.account } : { error: timeoutError3() }
2740
4011
  });
2741
4012
  }
2742
4013
  function requireProvisionInput(context) {
@@ -2748,13 +4019,13 @@ function requireProvisionInput(context) {
2748
4019
  }
2749
4020
  return context.input;
2750
4021
  }
2751
- function errorFromEvent2(event) {
4022
+ function errorFromEvent3(event) {
2752
4023
  const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
2753
4024
  if (cause instanceof CapxulError) return cause;
2754
4025
  if (cause instanceof CapxulError2) return cause;
2755
4026
  return Errors.providerError("provisioning", "flow", cause);
2756
4027
  }
2757
- function timeoutError2() {
4028
+ function timeoutError3() {
2758
4029
  return Errors.providerError(
2759
4030
  "provisioning",
2760
4031
  "flow",
@@ -2847,7 +4118,7 @@ function createOnboardingFlowMachine(client) {
2847
4118
  error: ({ event }) => extractChildErrorOrFallback(event)
2848
4119
  }),
2849
4120
  assignChildThrown: assign({
2850
- error: ({ event }) => errorFromEvent3(event)
4121
+ error: ({ event }) => errorFromEvent4(event)
2851
4122
  }),
2852
4123
  assignAccountFromChild: assign({
2853
4124
  account: ({ event }) => extractChildAccountOrNull(event)
@@ -3009,7 +4280,7 @@ function extractChildAccountOrNull(event) {
3009
4280
  if (output && "account" in output && output.account) return output.account;
3010
4281
  return null;
3011
4282
  }
3012
- function errorFromEvent3(event) {
4283
+ function errorFromEvent4(event) {
3013
4284
  const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3014
4285
  if (cause instanceof CapxulError) return cause;
3015
4286
  if (cause instanceof CapxulError2) return cause;
@@ -3029,7 +4300,7 @@ function createCapxulClient(config = {}) {
3029
4300
  tokenTransfers: createTokenTransfersClient(config),
3030
4301
  withdrawals: createWithdrawalsClient(config),
3031
4302
  documents: createDocumentsClient(),
3032
- subAccounts: createSubAccountsClient(),
4303
+ subAccounts: createSubAccountsClient(config),
3033
4304
  virtualAccounts: createVirtualAccountsClient(),
3034
4305
  virtualCards: createVirtualCardsClient(),
3035
4306
  externalAccounts: createExternalAccountsClient(config),
@@ -3041,6 +4312,7 @@ function createCapxulClient(config = {}) {
3041
4312
  const client = clientWithoutFlows;
3042
4313
  client.flows = {
3043
4314
  auth: () => createAuthFlowMachine(client),
4315
+ authBootstrap: () => createAuthBootstrapFlowMachine(client),
3044
4316
  onboarding: () => createOnboardingFlowMachine(client),
3045
4317
  provisioning: () => createProvisioningMachine(client)
3046
4318
  };