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