@swype-org/react-sdk 0.1.132 → 0.1.142

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -156,261 +156,6 @@ function useSwypeDepositAmount() {
156
156
  };
157
157
  }
158
158
 
159
- // src/api.ts
160
- var api_exports = {};
161
- __export(api_exports, {
162
- createAccount: () => createAccount,
163
- createAccountAuthorizationSession: () => createAccountAuthorizationSession,
164
- createTransfer: () => createTransfer,
165
- fetchAccount: () => fetchAccount,
166
- fetchAccounts: () => fetchAccounts,
167
- fetchAuthorizationSession: () => fetchAuthorizationSession,
168
- fetchChains: () => fetchChains,
169
- fetchMerchantPublicKey: () => fetchMerchantPublicKey,
170
- fetchProviders: () => fetchProviders,
171
- fetchTransfer: () => fetchTransfer,
172
- fetchUserConfig: () => fetchUserConfig,
173
- registerPasskey: () => registerPasskey,
174
- reportActionCompletion: () => reportActionCompletion,
175
- reportPasskeyActivity: () => reportPasskeyActivity,
176
- signTransfer: () => signTransfer,
177
- updateUserConfig: () => updateUserConfig,
178
- updateUserConfigBySession: () => updateUserConfigBySession
179
- });
180
- async function throwApiError(res) {
181
- const body = await res.json().catch(() => null);
182
- const detail = body?.error ?? body;
183
- const msg = detail?.message ?? res.statusText;
184
- const code = detail?.code ?? String(res.status);
185
- throw new Error(`${res.status} \u2014 ${code}: ${msg}`);
186
- }
187
- async function fetchProviders(apiBaseUrl, token) {
188
- const res = await fetch(`${apiBaseUrl}/v1/providers`, {
189
- headers: { Authorization: `Bearer ${token}` }
190
- });
191
- if (!res.ok) await throwApiError(res);
192
- const data = await res.json();
193
- return data.items;
194
- }
195
- async function fetchChains(apiBaseUrl, token) {
196
- const res = await fetch(`${apiBaseUrl}/v1/chains`, {
197
- headers: { Authorization: `Bearer ${token}` }
198
- });
199
- if (!res.ok) await throwApiError(res);
200
- const data = await res.json();
201
- return data.items;
202
- }
203
- async function fetchAccounts(apiBaseUrl, token, credentialId) {
204
- const params = new URLSearchParams({ credentialId });
205
- const res = await fetch(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
206
- headers: { Authorization: `Bearer ${token}` }
207
- });
208
- if (!res.ok) await throwApiError(res);
209
- const data = await res.json();
210
- return data.items;
211
- }
212
- async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
213
- const params = new URLSearchParams({ credentialId });
214
- const res = await fetch(`${apiBaseUrl}/v1/accounts/${accountId}?${params.toString()}`, {
215
- headers: { Authorization: `Bearer ${token}` }
216
- });
217
- if (!res.ok) await throwApiError(res);
218
- return await res.json();
219
- }
220
- async function createAccount(apiBaseUrl, token, params) {
221
- const body = {
222
- id: params.id ?? crypto.randomUUID(),
223
- name: params.name,
224
- credentialId: params.credentialId,
225
- providerId: params.providerId
226
- };
227
- if (params.nickname) {
228
- body.nickname = params.nickname;
229
- }
230
- const res = await fetch(`${apiBaseUrl}/v1/accounts`, {
231
- method: "POST",
232
- headers: {
233
- "Content-Type": "application/json",
234
- Authorization: `Bearer ${token}`
235
- },
236
- body: JSON.stringify(body)
237
- });
238
- if (!res.ok) await throwApiError(res);
239
- return await res.json();
240
- }
241
- async function createAccountAuthorizationSession(apiBaseUrl, token, accountId, credentialId, options) {
242
- const body = { credentialId };
243
- if (options?.tokenAddress) body.tokenAddress = options.tokenAddress;
244
- if (options?.chainId != null) body.chainId = options.chainId;
245
- const res = await fetch(
246
- `${apiBaseUrl}/v1/accounts/${accountId}/authorization-sessions`,
247
- {
248
- method: "POST",
249
- headers: {
250
- "Content-Type": "application/json",
251
- Authorization: `Bearer ${token}`
252
- },
253
- body: JSON.stringify(body)
254
- }
255
- );
256
- if (!res.ok) await throwApiError(res);
257
- return await res.json();
258
- }
259
- async function createTransfer(apiBaseUrl, token, params) {
260
- if (!params.merchantAuthorization) {
261
- throw new Error("merchantAuthorization is required for transfer creation.");
262
- }
263
- const body = {
264
- id: params.id ?? crypto.randomUUID(),
265
- credentialId: params.credentialId,
266
- merchantAuthorization: params.merchantAuthorization,
267
- sources: [{ [params.sourceType]: params.sourceId }],
268
- destinations: [
269
- {
270
- chainId: params.destination.chainId,
271
- token: { address: params.destination.token.address },
272
- address: params.destination.address
273
- }
274
- ],
275
- amount: {
276
- amount: params.amount,
277
- currency: params.currency ?? "USD"
278
- }
279
- };
280
- const res = await fetch(`${apiBaseUrl}/v1/transfers`, {
281
- method: "POST",
282
- headers: {
283
- "Content-Type": "application/json",
284
- Authorization: `Bearer ${token}`
285
- },
286
- body: JSON.stringify(body)
287
- });
288
- if (!res.ok) await throwApiError(res);
289
- return await res.json();
290
- }
291
- async function fetchMerchantPublicKey(apiBaseUrl, merchantId) {
292
- const res = await fetch(
293
- `${apiBaseUrl}/v1/merchants/${encodeURIComponent(merchantId)}/public-key`
294
- );
295
- if (!res.ok) await throwApiError(res);
296
- return await res.json();
297
- }
298
- async function fetchTransfer(apiBaseUrl, token, transferId, authorizationSessionToken) {
299
- if (!token && !authorizationSessionToken) {
300
- throw new Error("Missing auth credentials for transfer fetch.");
301
- }
302
- const res = await fetch(`${apiBaseUrl}/v1/transfers/${transferId}`, {
303
- headers: {
304
- ...token ? { Authorization: `Bearer ${token}` } : {},
305
- ...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
306
- }
307
- });
308
- if (!res.ok) await throwApiError(res);
309
- return await res.json();
310
- }
311
- async function signTransfer(apiBaseUrl, token, transferId, signedUserOp, authorizationSessionToken) {
312
- if (!token && !authorizationSessionToken) {
313
- throw new Error("Missing auth credentials for transfer signing.");
314
- }
315
- const res = await fetch(`${apiBaseUrl}/v1/transfers/${transferId}`, {
316
- method: "PATCH",
317
- headers: {
318
- "Content-Type": "application/json",
319
- ...token ? { Authorization: `Bearer ${token}` } : {},
320
- ...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
321
- },
322
- body: JSON.stringify({ signedUserOp })
323
- });
324
- if (!res.ok) await throwApiError(res);
325
- return await res.json();
326
- }
327
- async function fetchAuthorizationSession(apiBaseUrl, sessionId) {
328
- const res = await fetch(
329
- `${apiBaseUrl}/v1/authorization-sessions/${sessionId}`
330
- );
331
- if (!res.ok) await throwApiError(res);
332
- return await res.json();
333
- }
334
- async function registerPasskey(apiBaseUrl, token, credentialId, publicKey) {
335
- const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
336
- method: "POST",
337
- headers: {
338
- "Content-Type": "application/json",
339
- Authorization: `Bearer ${token}`
340
- },
341
- body: JSON.stringify({ credentialId, publicKey })
342
- });
343
- if (!res.ok) await throwApiError(res);
344
- }
345
- async function reportPasskeyActivity(apiBaseUrl, token, credentialId) {
346
- const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
347
- method: "PATCH",
348
- headers: {
349
- "Content-Type": "application/json",
350
- Authorization: `Bearer ${token}`
351
- },
352
- body: JSON.stringify({ credentialId })
353
- });
354
- if (!res.ok) await throwApiError(res);
355
- }
356
- async function fetchUserConfig(apiBaseUrl, token) {
357
- const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
358
- headers: { Authorization: `Bearer ${token}` }
359
- });
360
- if (!res.ok) await throwApiError(res);
361
- return await res.json();
362
- }
363
- async function updateUserConfig(apiBaseUrl, token, config) {
364
- const res = await fetch(`${apiBaseUrl}/v1/users`, {
365
- method: "PATCH",
366
- headers: {
367
- "Content-Type": "application/json",
368
- Authorization: `Bearer ${token}`
369
- },
370
- body: JSON.stringify({ config })
371
- });
372
- if (!res.ok) await throwApiError(res);
373
- }
374
- async function updateUserConfigBySession(apiBaseUrl, sessionId, config) {
375
- const res = await fetch(
376
- `${apiBaseUrl}/v1/authorization-sessions/${sessionId}/user-config`,
377
- {
378
- method: "PATCH",
379
- headers: { "Content-Type": "application/json" },
380
- body: JSON.stringify({ config })
381
- }
382
- );
383
- if (!res.ok) await throwApiError(res);
384
- }
385
- async function reportActionCompletion(apiBaseUrl, actionId, result) {
386
- const res = await fetch(
387
- `${apiBaseUrl}/v1/authorization-actions/${actionId}`,
388
- {
389
- method: "PATCH",
390
- headers: { "Content-Type": "application/json" },
391
- body: JSON.stringify({ status: "COMPLETED", result })
392
- }
393
- );
394
- if (!res.ok) await throwApiError(res);
395
- return await res.json();
396
- }
397
-
398
- // src/sentry.ts
399
- var _mod;
400
- function captureException(error) {
401
- if (_mod === null) return;
402
- if (_mod) {
403
- _mod.captureException(error);
404
- return;
405
- }
406
- import('@sentry/react').then((m) => {
407
- _mod = m;
408
- m.captureException(error);
409
- }).catch(() => {
410
- _mod = null;
411
- });
412
- }
413
-
414
159
  // node_modules/@wagmi/core/dist/esm/version.js
415
160
  var version = "2.22.1";
416
161
 
@@ -671,8 +416,250 @@ async function getWalletClient(config, parameters = {}) {
671
416
  return client.extend(viem.walletActions);
672
417
  }
673
418
 
674
- // src/passkeyRpId.ts
675
- function normalizeConfiguredDomain(value) {
419
+ // src/api.ts
420
+ var api_exports = {};
421
+ __export(api_exports, {
422
+ createAccount: () => createAccount,
423
+ createAccountAuthorizationSession: () => createAccountAuthorizationSession,
424
+ createTransfer: () => createTransfer,
425
+ fetchAccount: () => fetchAccount,
426
+ fetchAccounts: () => fetchAccounts,
427
+ fetchAuthorizationSession: () => fetchAuthorizationSession,
428
+ fetchChains: () => fetchChains,
429
+ fetchMerchantPublicKey: () => fetchMerchantPublicKey,
430
+ fetchProviders: () => fetchProviders,
431
+ fetchTransfer: () => fetchTransfer,
432
+ fetchUserConfig: () => fetchUserConfig,
433
+ registerPasskey: () => registerPasskey,
434
+ reportActionCompletion: () => reportActionCompletion,
435
+ reportPasskeyActivity: () => reportPasskeyActivity,
436
+ signTransfer: () => signTransfer,
437
+ updateUserConfig: () => updateUserConfig,
438
+ updateUserConfigBySession: () => updateUserConfigBySession
439
+ });
440
+ async function throwApiError(res) {
441
+ const body = await res.json().catch(() => null);
442
+ const detail = body?.error ?? body;
443
+ const msg = detail?.message ?? res.statusText;
444
+ const code = detail?.code ?? String(res.status);
445
+ throw new Error(`${res.status} \u2014 ${code}: ${msg}`);
446
+ }
447
+ async function fetchProviders(apiBaseUrl, token) {
448
+ const res = await fetch(`${apiBaseUrl}/v1/providers`, {
449
+ headers: { Authorization: `Bearer ${token}` }
450
+ });
451
+ if (!res.ok) await throwApiError(res);
452
+ const data = await res.json();
453
+ return data.items;
454
+ }
455
+ async function fetchChains(apiBaseUrl, token) {
456
+ const res = await fetch(`${apiBaseUrl}/v1/chains`, {
457
+ headers: { Authorization: `Bearer ${token}` }
458
+ });
459
+ if (!res.ok) await throwApiError(res);
460
+ const data = await res.json();
461
+ return data.items;
462
+ }
463
+ async function fetchAccounts(apiBaseUrl, token, credentialId) {
464
+ const params = new URLSearchParams({ credentialId });
465
+ const res = await fetch(`${apiBaseUrl}/v1/accounts?${params.toString()}`, {
466
+ headers: { Authorization: `Bearer ${token}` }
467
+ });
468
+ if (!res.ok) await throwApiError(res);
469
+ const data = await res.json();
470
+ return data.items;
471
+ }
472
+ async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
473
+ const params = new URLSearchParams({ credentialId });
474
+ const res = await fetch(`${apiBaseUrl}/v1/accounts/${accountId}?${params.toString()}`, {
475
+ headers: { Authorization: `Bearer ${token}` }
476
+ });
477
+ if (!res.ok) await throwApiError(res);
478
+ return await res.json();
479
+ }
480
+ async function createAccount(apiBaseUrl, token, params) {
481
+ const body = {
482
+ id: params.id ?? crypto.randomUUID(),
483
+ name: params.name,
484
+ credentialId: params.credentialId,
485
+ providerId: params.providerId
486
+ };
487
+ if (params.nickname) {
488
+ body.nickname = params.nickname;
489
+ }
490
+ const res = await fetch(`${apiBaseUrl}/v1/accounts`, {
491
+ method: "POST",
492
+ headers: {
493
+ "Content-Type": "application/json",
494
+ Authorization: `Bearer ${token}`
495
+ },
496
+ body: JSON.stringify(body)
497
+ });
498
+ if (!res.ok) await throwApiError(res);
499
+ return await res.json();
500
+ }
501
+ async function createAccountAuthorizationSession(apiBaseUrl, token, accountId, credentialId, options) {
502
+ const body = { credentialId };
503
+ if (options?.tokenAddress) body.tokenAddress = options.tokenAddress;
504
+ if (options?.chainId != null) body.chainId = options.chainId;
505
+ const res = await fetch(
506
+ `${apiBaseUrl}/v1/accounts/${accountId}/authorization-sessions`,
507
+ {
508
+ method: "POST",
509
+ headers: {
510
+ "Content-Type": "application/json",
511
+ Authorization: `Bearer ${token}`
512
+ },
513
+ body: JSON.stringify(body)
514
+ }
515
+ );
516
+ if (!res.ok) await throwApiError(res);
517
+ return await res.json();
518
+ }
519
+ async function createTransfer(apiBaseUrl, token, params) {
520
+ if (!params.merchantAuthorization) {
521
+ throw new Error("merchantAuthorization is required for transfer creation.");
522
+ }
523
+ const body = {
524
+ id: params.id ?? crypto.randomUUID(),
525
+ credentialId: params.credentialId,
526
+ merchantAuthorization: params.merchantAuthorization,
527
+ sources: [{
528
+ [params.sourceType]: params.sourceId,
529
+ ...params.sourceTokenAddress ? { tokenAddress: params.sourceTokenAddress } : {}
530
+ }],
531
+ destinations: [
532
+ {
533
+ chainId: params.destination.chainId,
534
+ token: { address: params.destination.token.address },
535
+ address: params.destination.address
536
+ }
537
+ ],
538
+ amount: {
539
+ amount: params.amount,
540
+ currency: params.currency ?? "USD"
541
+ }
542
+ };
543
+ const res = await fetch(`${apiBaseUrl}/v1/transfers`, {
544
+ method: "POST",
545
+ headers: {
546
+ "Content-Type": "application/json",
547
+ Authorization: `Bearer ${token}`
548
+ },
549
+ body: JSON.stringify(body)
550
+ });
551
+ if (!res.ok) await throwApiError(res);
552
+ return await res.json();
553
+ }
554
+ async function fetchMerchantPublicKey(apiBaseUrl, merchantId) {
555
+ const res = await fetch(
556
+ `${apiBaseUrl}/v1/merchants/${encodeURIComponent(merchantId)}/public-key`
557
+ );
558
+ if (!res.ok) await throwApiError(res);
559
+ return await res.json();
560
+ }
561
+ async function fetchTransfer(apiBaseUrl, token, transferId, authorizationSessionToken) {
562
+ if (!token && !authorizationSessionToken) {
563
+ throw new Error("Missing auth credentials for transfer fetch.");
564
+ }
565
+ const res = await fetch(`${apiBaseUrl}/v1/transfers/${transferId}`, {
566
+ headers: {
567
+ ...token ? { Authorization: `Bearer ${token}` } : {},
568
+ ...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
569
+ }
570
+ });
571
+ if (!res.ok) await throwApiError(res);
572
+ return await res.json();
573
+ }
574
+ async function signTransfer(apiBaseUrl, token, transferId, signedUserOp, authorizationSessionToken) {
575
+ if (!token && !authorizationSessionToken) {
576
+ throw new Error("Missing auth credentials for transfer signing.");
577
+ }
578
+ const res = await fetch(`${apiBaseUrl}/v1/transfers/${transferId}`, {
579
+ method: "PATCH",
580
+ headers: {
581
+ "Content-Type": "application/json",
582
+ ...token ? { Authorization: `Bearer ${token}` } : {},
583
+ ...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
584
+ },
585
+ body: JSON.stringify({ signedUserOp })
586
+ });
587
+ if (!res.ok) await throwApiError(res);
588
+ return await res.json();
589
+ }
590
+ async function fetchAuthorizationSession(apiBaseUrl, sessionId) {
591
+ const res = await fetch(
592
+ `${apiBaseUrl}/v1/authorization-sessions/${sessionId}`
593
+ );
594
+ if (!res.ok) await throwApiError(res);
595
+ return await res.json();
596
+ }
597
+ async function registerPasskey(apiBaseUrl, token, credentialId, publicKey) {
598
+ const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
599
+ method: "POST",
600
+ headers: {
601
+ "Content-Type": "application/json",
602
+ Authorization: `Bearer ${token}`
603
+ },
604
+ body: JSON.stringify({ credentialId, publicKey })
605
+ });
606
+ if (!res.ok) await throwApiError(res);
607
+ }
608
+ async function reportPasskeyActivity(apiBaseUrl, token, credentialId) {
609
+ const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
610
+ method: "PATCH",
611
+ headers: {
612
+ "Content-Type": "application/json",
613
+ Authorization: `Bearer ${token}`
614
+ },
615
+ body: JSON.stringify({ credentialId })
616
+ });
617
+ if (!res.ok) await throwApiError(res);
618
+ }
619
+ async function fetchUserConfig(apiBaseUrl, token) {
620
+ const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
621
+ headers: { Authorization: `Bearer ${token}` }
622
+ });
623
+ if (!res.ok) await throwApiError(res);
624
+ return await res.json();
625
+ }
626
+ async function updateUserConfig(apiBaseUrl, token, config) {
627
+ const res = await fetch(`${apiBaseUrl}/v1/users`, {
628
+ method: "PATCH",
629
+ headers: {
630
+ "Content-Type": "application/json",
631
+ Authorization: `Bearer ${token}`
632
+ },
633
+ body: JSON.stringify({ config })
634
+ });
635
+ if (!res.ok) await throwApiError(res);
636
+ }
637
+ async function updateUserConfigBySession(apiBaseUrl, sessionId, config) {
638
+ const res = await fetch(
639
+ `${apiBaseUrl}/v1/authorization-sessions/${sessionId}/user-config`,
640
+ {
641
+ method: "PATCH",
642
+ headers: { "Content-Type": "application/json" },
643
+ body: JSON.stringify({ config })
644
+ }
645
+ );
646
+ if (!res.ok) await throwApiError(res);
647
+ }
648
+ async function reportActionCompletion(apiBaseUrl, actionId, result) {
649
+ const res = await fetch(
650
+ `${apiBaseUrl}/v1/authorization-actions/${actionId}`,
651
+ {
652
+ method: "PATCH",
653
+ headers: { "Content-Type": "application/json" },
654
+ body: JSON.stringify({ status: "COMPLETED", result })
655
+ }
656
+ );
657
+ if (!res.ok) await throwApiError(res);
658
+ return await res.json();
659
+ }
660
+
661
+ // src/passkeyRpId.ts
662
+ function normalizeConfiguredDomain(value) {
676
663
  return value.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
677
664
  }
678
665
  function resolveRootDomainFromHostname(hostname) {
@@ -1656,170 +1643,6 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
1656
1643
  return { signing, signPayload, error, signTransfer: signTransfer2 };
1657
1644
  }
1658
1645
 
1659
- // src/auth.ts
1660
- var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1661
- var PHONE_DIGIT_PATTERN = /^\d{7,15}$/;
1662
- function normalizePhoneNumber(rawValue) {
1663
- const trimmed = rawValue.trim();
1664
- if (!trimmed) return null;
1665
- const hasExplicitCountryCode = trimmed.startsWith("+");
1666
- const digits = trimmed.replace(/\D/g, "");
1667
- if (!PHONE_DIGIT_PATTERN.test(digits)) return null;
1668
- return hasExplicitCountryCode ? `+${digits}` : digits;
1669
- }
1670
- function normalizeAuthIdentifier(rawValue) {
1671
- const trimmed = rawValue.trim();
1672
- if (!trimmed) return null;
1673
- if (EMAIL_PATTERN.test(trimmed)) {
1674
- return {
1675
- kind: "email",
1676
- value: trimmed.toLowerCase()
1677
- };
1678
- }
1679
- const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
1680
- if (normalizedPhoneNumber) {
1681
- return {
1682
- kind: "phone",
1683
- value: normalizedPhoneNumber
1684
- };
1685
- }
1686
- return null;
1687
- }
1688
- function maskAuthIdentifier(identifier) {
1689
- if (identifier.kind === "email") {
1690
- const [localPart, domain = ""] = identifier.value.split("@");
1691
- const localPrefix = localPart.slice(0, 2);
1692
- return `${localPrefix}${"*".repeat(Math.max(localPart.length - 2, 0))}@${domain}`;
1693
- }
1694
- const digits = identifier.value.replace(/\D/g, "");
1695
- const visibleSuffix = digits.slice(-4);
1696
- return `***-***-${visibleSuffix}`;
1697
- }
1698
-
1699
- // src/processingStatus.ts
1700
- var PROCESSING_TIMEOUT_MS = 18e4;
1701
- function resolvePreferredTransfer(polledTransfer, localTransfer) {
1702
- return polledTransfer ?? localTransfer;
1703
- }
1704
- function getTransferStatus(polledTransfer, localTransfer) {
1705
- const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
1706
- return transfer?.status ?? "UNKNOWN";
1707
- }
1708
- function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
1709
- if (!processingStartedAtMs) return false;
1710
- return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
1711
- }
1712
- var STATUS_DISPLAY_LABELS = {
1713
- CREATED: "created",
1714
- AUTHORIZED: "authorized",
1715
- SENDING: "sending",
1716
- SENT: "confirming delivery",
1717
- COMPLETED: "completed",
1718
- FAILED: "failed"
1719
- };
1720
- function getStatusDisplayLabel(status) {
1721
- return STATUS_DISPLAY_LABELS[status] ?? status;
1722
- }
1723
- function buildProcessingTimeoutMessage(status) {
1724
- const label = getStatusDisplayLabel(status);
1725
- return `Payment is taking longer than expected (status: ${label}). Please try again.`;
1726
- }
1727
-
1728
- // src/walletFlow.ts
1729
- var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
1730
- function isMobileUserAgent(userAgent) {
1731
- if (!userAgent) {
1732
- return false;
1733
- }
1734
- return MOBILE_USER_AGENT_PATTERN.test(userAgent);
1735
- }
1736
- function shouldUseWalletConnector(options) {
1737
- return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
1738
- }
1739
-
1740
- // src/deeplink.ts
1741
- var IFRAME_CLEANUP_DELAY_MS = 3e3;
1742
- var LOCATION_FALLBACK_DELAY_MS = 100;
1743
- function triggerDeeplink(uri) {
1744
- try {
1745
- const iframe = document.createElement("iframe");
1746
- iframe.style.display = "none";
1747
- iframe.src = uri;
1748
- document.body.appendChild(iframe);
1749
- setTimeout(() => {
1750
- try {
1751
- document.body.removeChild(iframe);
1752
- } catch {
1753
- }
1754
- }, IFRAME_CLEANUP_DELAY_MS);
1755
- } catch {
1756
- }
1757
- setTimeout(() => {
1758
- window.location.href = uri;
1759
- }, LOCATION_FALLBACK_DELAY_MS);
1760
- }
1761
-
1762
- // src/mobileFlow.ts
1763
- function hasActiveWallet(accounts) {
1764
- return accounts.some((account) => account.wallets.some((wallet) => wallet.status === "ACTIVE"));
1765
- }
1766
- function resolvePostAuthStep(state) {
1767
- if (!state.hasPasskey) {
1768
- return { step: "create-passkey", clearPersistedFlow: false };
1769
- }
1770
- if (state.persistedMobileFlow) {
1771
- if (state.persistedMobileFlow.isReauthorization) {
1772
- return { step: "open-wallet", clearPersistedFlow: false };
1773
- }
1774
- if (state.persistedMobileFlow.isSetup && hasActiveWallet(state.accounts)) {
1775
- return { step: "deposit", clearPersistedFlow: true };
1776
- }
1777
- return { step: "open-wallet", clearPersistedFlow: false };
1778
- }
1779
- if (state.mobileSetupInProgress && !hasActiveWallet(state.accounts)) {
1780
- return { step: "open-wallet", clearPersistedFlow: false };
1781
- }
1782
- if ((state.accounts.length === 0 || !hasActiveWallet(state.accounts)) && !state.connectingNewAccount) {
1783
- return { step: "wallet-picker", clearPersistedFlow: false };
1784
- }
1785
- return { step: "deposit", clearPersistedFlow: false };
1786
- }
1787
- function resolveRestoredMobileFlow(transferStatus, isSetup) {
1788
- if (transferStatus === "AUTHORIZED") {
1789
- return isSetup ? { kind: "resume-setup-deposit", step: "deposit", clearPersistedFlow: true } : { kind: "resume-confirm-sign", step: "confirm-sign", clearPersistedFlow: true };
1790
- }
1791
- if (transferStatus === "COMPLETED") {
1792
- return { kind: "resume-success", step: "success", clearPersistedFlow: true };
1793
- }
1794
- if (transferStatus === "FAILED") {
1795
- return { kind: "resume-failed", step: "success", clearPersistedFlow: true };
1796
- }
1797
- if (transferStatus === "SENDING" || transferStatus === "SENT") {
1798
- return { kind: "resume-processing", step: "processing", clearPersistedFlow: true };
1799
- }
1800
- if (isSetup) {
1801
- return { kind: "resume-stale-setup", step: "wallet-picker", clearPersistedFlow: true };
1802
- }
1803
- return { kind: "resume-open-wallet", step: "open-wallet", clearPersistedFlow: false };
1804
- }
1805
-
1806
- // src/dataLoading.ts
1807
- function resolveDataLoadAction({
1808
- authenticated,
1809
- step,
1810
- accountsCount,
1811
- hasActiveCredential,
1812
- loading
1813
- }) {
1814
- if (!authenticated || step === "login" || step === "otp-verify" || accountsCount > 0 || !hasActiveCredential) {
1815
- return "reset";
1816
- }
1817
- if (loading) {
1818
- return "wait";
1819
- }
1820
- return "load";
1821
- }
1822
-
1823
1646
  // src/paymentHelpers.ts
1824
1647
  var ACTIVE_CREDENTIAL_STORAGE_KEY = "swype_active_credential_id";
1825
1648
  var MOBILE_FLOW_STORAGE_KEY = "swype_mobile_flow";
@@ -2235,6 +2058,46 @@ function paymentReducer(state, action) {
2235
2058
  return state;
2236
2059
  }
2237
2060
  }
2061
+
2062
+ // src/auth.ts
2063
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2064
+ var PHONE_DIGIT_PATTERN = /^\d{7,15}$/;
2065
+ function normalizePhoneNumber(rawValue) {
2066
+ const trimmed = rawValue.trim();
2067
+ if (!trimmed) return null;
2068
+ const hasExplicitCountryCode = trimmed.startsWith("+");
2069
+ const digits = trimmed.replace(/\D/g, "");
2070
+ if (!PHONE_DIGIT_PATTERN.test(digits)) return null;
2071
+ return hasExplicitCountryCode ? `+${digits}` : digits;
2072
+ }
2073
+ function normalizeAuthIdentifier(rawValue) {
2074
+ const trimmed = rawValue.trim();
2075
+ if (!trimmed) return null;
2076
+ if (EMAIL_PATTERN.test(trimmed)) {
2077
+ return {
2078
+ kind: "email",
2079
+ value: trimmed.toLowerCase()
2080
+ };
2081
+ }
2082
+ const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
2083
+ if (normalizedPhoneNumber) {
2084
+ return {
2085
+ kind: "phone",
2086
+ value: normalizedPhoneNumber
2087
+ };
2088
+ }
2089
+ return null;
2090
+ }
2091
+ function maskAuthIdentifier(identifier) {
2092
+ if (identifier.kind === "email") {
2093
+ const [localPart, domain = ""] = identifier.value.split("@");
2094
+ const localPrefix = localPart.slice(0, 2);
2095
+ return `${localPrefix}${"*".repeat(Math.max(localPart.length - 2, 0))}@${domain}`;
2096
+ }
2097
+ const digits = identifier.value.replace(/\D/g, "");
2098
+ const visibleSuffix = digits.slice(-4);
2099
+ return `***-***-${visibleSuffix}`;
2100
+ }
2238
2101
  var ACCENT = "#28b67a";
2239
2102
  var BG_RING = "#d2e4ea";
2240
2103
  function SwypeLoadingScreen() {
@@ -3860,7 +3723,7 @@ var dividerTextStyle = (color) => ({
3860
3723
  color,
3861
3724
  whiteSpace: "nowrap"
3862
3725
  });
3863
- var DEFAULT_MAX = 500;
3726
+ var DEFAULT_MAX = 1e7;
3864
3727
  var ABSOLUTE_MIN = 0.01;
3865
3728
  function SetupScreen({
3866
3729
  availableBalance,
@@ -3875,7 +3738,7 @@ function SetupScreen({
3875
3738
  error
3876
3739
  }) {
3877
3740
  const { tokens } = useSwypeConfig();
3878
- const effectiveMax = Math.min(DEFAULT_MAX, availableBalance);
3741
+ const effectiveMax = DEFAULT_MAX;
3879
3742
  const effectiveMin = Math.min(ABSOLUTE_MIN, effectiveMax);
3880
3743
  const [limit, setLimit] = react.useState(() => Math.min(availableBalance, effectiveMax));
3881
3744
  const [editing, setEditing] = react.useState(false);
@@ -4251,7 +4114,8 @@ function DepositScreen({
4251
4114
  onSelectAccount,
4252
4115
  onAuthorizeAccount,
4253
4116
  onAddProvider,
4254
- onSelectToken
4117
+ onSelectToken,
4118
+ selectedSourceLabel
4255
4119
  }) {
4256
4120
  const { tokens } = useSwypeConfig();
4257
4121
  const amount = initialAmount;
@@ -4296,7 +4160,7 @@ function DepositScreen({
4296
4160
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "12", fill: "#fff", fontWeight: "700", children: "$" })
4297
4161
  ] }) }),
4298
4162
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4299
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: "Available" }),
4163
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: selectedSourceLabel ?? "Available" }),
4300
4164
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...balanceAmountStyle, color: tokens.warning }, children: [
4301
4165
  "$",
4302
4166
  availableBalance.toFixed(2)
@@ -4371,10 +4235,7 @@ function DepositScreen({
4371
4235
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "12", fill: "#fff", fontWeight: "700", children: "$" })
4372
4236
  ] }) }),
4373
4237
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4374
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceLabelStyle2(tokens.textMuted), children: [
4375
- "Paying from ",
4376
- sourceName
4377
- ] }),
4238
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: selectedSourceLabel ?? `Paying from ${sourceName}` }),
4378
4239
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceAmountStyle, children: [
4379
4240
  "$",
4380
4241
  availableBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
@@ -4728,6 +4589,7 @@ function SelectSourceScreen({
4728
4589
  onChainChange,
4729
4590
  onTokenChange,
4730
4591
  onConfirm,
4592
+ onBack,
4731
4593
  onLogout
4732
4594
  }) {
4733
4595
  const { tokens } = useSwypeConfig();
@@ -4746,6 +4608,7 @@ function SelectSourceScreen({
4746
4608
  ScreenHeader,
4747
4609
  {
4748
4610
  title: "Select Source",
4611
+ onBack,
4749
4612
  right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout })
4750
4613
  }
4751
4614
  ),
@@ -5168,6 +5031,28 @@ var waitHintStyle2 = (color) => ({
5168
5031
  color,
5169
5032
  margin: 0
5170
5033
  });
5034
+
5035
+ // src/deeplink.ts
5036
+ var IFRAME_CLEANUP_DELAY_MS = 3e3;
5037
+ var LOCATION_FALLBACK_DELAY_MS = 100;
5038
+ function triggerDeeplink(uri) {
5039
+ try {
5040
+ const iframe = document.createElement("iframe");
5041
+ iframe.style.display = "none";
5042
+ iframe.src = uri;
5043
+ document.body.appendChild(iframe);
5044
+ setTimeout(() => {
5045
+ try {
5046
+ document.body.removeChild(iframe);
5047
+ } catch {
5048
+ }
5049
+ }, IFRAME_CLEANUP_DELAY_MS);
5050
+ } catch {
5051
+ }
5052
+ setTimeout(() => {
5053
+ window.location.href = uri;
5054
+ }, LOCATION_FALLBACK_DELAY_MS);
5055
+ }
5171
5056
  function OpenWalletScreen({
5172
5057
  walletName,
5173
5058
  deeplinkUri,
@@ -5613,6 +5498,7 @@ function StepRenderer({
5613
5498
  selectedSource,
5614
5499
  selectSourceChoices,
5615
5500
  selectSourceRecommended,
5501
+ selectSourceAvailableBalance,
5616
5502
  authInput,
5617
5503
  otpCode,
5618
5504
  selectSourceChainName,
@@ -5734,17 +5620,23 @@ function StepRenderer({
5734
5620
  );
5735
5621
  }
5736
5622
  if (step === "setup") {
5623
+ const selectSourceTokenCount = selectSourceChoices.reduce(
5624
+ (sum, chain) => sum + chain.tokens.length,
5625
+ 0
5626
+ );
5627
+ const effectiveTokenCount = tokenCount > 0 ? tokenCount : selectSourceTokenCount;
5628
+ const effectiveSourceLabel = selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
5737
5629
  return /* @__PURE__ */ jsxRuntime.jsx(
5738
5630
  SetupScreen,
5739
5631
  {
5740
- availableBalance: selectedSource ? selectedSource.balance.available.amount : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : maxSourceBalance,
5741
- tokenCount,
5632
+ availableBalance: selectedSource ? selectedSource.balance.available.amount : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : selectSourceAvailableBalance > 0 ? selectSourceAvailableBalance : maxSourceBalance,
5633
+ tokenCount: effectiveTokenCount,
5742
5634
  sourceName,
5743
5635
  onSetupOneTap: handlers.onSetupOneTap,
5744
5636
  onBack: () => handlers.onNavigate("deposit"),
5745
5637
  onLogout: handlers.onLogout,
5746
5638
  onAdvanced: handlers.onSelectToken,
5747
- selectedSourceLabel,
5639
+ selectedSourceLabel: effectiveSourceLabel,
5748
5640
  loading: savingOneTapLimit,
5749
5641
  error: state.error
5750
5642
  }
@@ -5792,7 +5684,8 @@ function StepRenderer({
5792
5684
  onSelectAccount: handlers.onSelectAccount,
5793
5685
  onAuthorizeAccount: handlers.onContinueConnection,
5794
5686
  onAddProvider: () => handlers.onNavigate("wallet-picker"),
5795
- onSelectToken: handlers.onSelectToken
5687
+ onSelectToken: handlers.onSelectToken,
5688
+ selectedSourceLabel
5796
5689
  }
5797
5690
  );
5798
5691
  }
@@ -5824,6 +5717,7 @@ function StepRenderer({
5824
5717
  );
5825
5718
  }
5826
5719
  if (step === "select-source") {
5720
+ const cameFromSetup = state.previousStep === "setup";
5827
5721
  return /* @__PURE__ */ jsxRuntime.jsx(
5828
5722
  SelectSourceScreen,
5829
5723
  {
@@ -5833,7 +5727,8 @@ function StepRenderer({
5833
5727
  recommended: selectSourceRecommended,
5834
5728
  onChainChange: handlers.onSelectSourceChainChange,
5835
5729
  onTokenChange: handlers.onSetSelectSourceTokenSymbol,
5836
- onConfirm: handlers.onConfirmSelectSource,
5730
+ onConfirm: cameFromSetup ? () => handlers.onNavigate("setup") : handlers.onConfirmSelectSource,
5731
+ onBack: cameFromSetup ? () => handlers.onNavigate("setup") : void 0,
5837
5732
  onLogout: handlers.onLogout
5838
5733
  }
5839
5734
  );
@@ -5883,12 +5778,29 @@ function StepRenderer({
5883
5778
  selectedAccountId: state.selectedAccountId,
5884
5779
  onSelectAccount: handlers.onSelectAccount,
5885
5780
  onAuthorizeAccount: handlers.onContinueConnection,
5886
- onAddProvider: () => handlers.onNavigate("wallet-picker")
5781
+ onAddProvider: () => handlers.onNavigate("wallet-picker"),
5782
+ selectedSourceLabel
5887
5783
  }
5888
5784
  );
5889
5785
  }
5890
5786
  return null;
5891
5787
  }
5788
+
5789
+ // src/sentry.ts
5790
+ var _mod;
5791
+ function captureException(error) {
5792
+ if (_mod === null) return;
5793
+ if (_mod) {
5794
+ _mod.captureException(error);
5795
+ return;
5796
+ }
5797
+ import('@sentry/react').then((m) => {
5798
+ _mod = m;
5799
+ m.captureException(error);
5800
+ }).catch(() => {
5801
+ _mod = null;
5802
+ });
5803
+ }
5892
5804
  var PaymentErrorBoundary = class extends react.Component {
5893
5805
  constructor(props) {
5894
5806
  super(props);
@@ -5957,78 +5869,7 @@ var buttonStyle3 = {
5957
5869
  fontFamily: "inherit",
5958
5870
  cursor: "pointer"
5959
5871
  };
5960
- function SwypePayment(props) {
5961
- const resetKey = react.useRef(0);
5962
- const handleBoundaryReset = react.useCallback(() => {
5963
- resetKey.current += 1;
5964
- }, []);
5965
- return /* @__PURE__ */ jsxRuntime.jsx(PaymentErrorBoundary, { onReset: handleBoundaryReset, children: /* @__PURE__ */ jsxRuntime.jsx(SwypePaymentInner, { ...props }) }, resetKey.current);
5966
- }
5967
- function SwypePaymentInner({
5968
- destination,
5969
- onComplete,
5970
- onError,
5971
- useWalletConnector: useWalletConnectorProp,
5972
- idempotencyKey,
5973
- merchantAuthorization,
5974
- merchantName,
5975
- onBack,
5976
- onDismiss,
5977
- autoCloseSeconds
5978
- }) {
5979
- const { apiBaseUrl, depositAmount } = useSwypeConfig();
5980
- const { ready, authenticated, user, logout, getAccessToken } = reactAuth.usePrivy();
5981
- const {
5982
- sendCode: sendEmailCode,
5983
- loginWithCode: loginWithEmailCode,
5984
- state: emailLoginState
5985
- } = reactAuth.useLoginWithEmail();
5986
- const {
5987
- sendCode: sendSmsCode,
5988
- loginWithCode: loginWithSmsCode,
5989
- state: smsLoginState
5990
- } = reactAuth.useLoginWithSms();
5991
- reactAuth.useLoginWithOAuth();
5992
- const [state, dispatch] = react.useReducer(
5993
- paymentReducer,
5994
- {
5995
- depositAmount,
5996
- passkeyPopupNeeded: isSafari() && isInCrossOriginIframe(),
5997
- activeCredentialId: typeof window === "undefined" ? null : window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY)
5998
- },
5999
- createInitialState
6000
- );
6001
- const loadingDataRef = react.useRef(false);
6002
- const pollingTransferIdRef = react.useRef(null);
6003
- const setupAccountIdRef = react.useRef(null);
6004
- const mobileSetupFlowRef = react.useRef(false);
6005
- const handlingMobileReturnRef = react.useRef(false);
6006
- const processingStartedAtRef = react.useRef(null);
6007
- const initializedSelectSourceActionRef = react.useRef(null);
6008
- const preSelectSourceStepRef = react.useRef(null);
6009
- const pendingTokenAuthRef = react.useRef(null);
6010
- const pendingTokenSelectionRef = react.useRef(null);
6011
- const pendingTokenAuthSessionRef = react.useRef(null);
6012
- const reauthSessionIdRef = react.useRef(null);
6013
- const reauthTokenRef = react.useRef(null);
6014
- const checkingPasskeyRef = react.useRef(false);
6015
- const onCompleteRef = react.useRef(onComplete);
6016
- onCompleteRef.current = onComplete;
6017
- const pollingRef = react.useRef(null);
6018
- const getAccessTokenRef = react.useRef(getAccessToken);
6019
- getAccessTokenRef.current = getAccessToken;
6020
- const handleAuthorizedMobileReturnRef = react.useRef(
6021
- null
6022
- );
6023
- const [authInput, setAuthInput] = react.useState("");
6024
- const [otpCode, setOtpCode] = react.useState("");
6025
- const [selectSourceChainName, setSelectSourceChainName] = react.useState("");
6026
- const [selectSourceTokenSymbol, setSelectSourceTokenSymbol] = react.useState("");
6027
- const [savingOneTapLimit, setSavingOneTapLimit] = react.useState(false);
6028
- const authExecutor = useAuthorizationExecutor();
6029
- const polling = useTransferPolling();
6030
- pollingRef.current = polling;
6031
- const transferSigning = useTransferSigning();
5872
+ function useDerivedState(state) {
6032
5873
  const { sourceType, sourceId } = deriveSourceTypeAndId(state);
6033
5874
  const selectedAccount = state.accounts.find((a) => a.id === state.selectedAccountId);
6034
5875
  const selectedWallet = selectedAccount?.wallets.find(
@@ -6078,8 +5919,36 @@ function SwypePaymentInner({
6078
5919
  }
6079
5920
  return count;
6080
5921
  }, [state.accounts]);
6081
- const activeOtpStatus = state.verificationTarget?.kind === "email" ? emailLoginState.status : state.verificationTarget?.kind === "phone" ? smsLoginState.status : "initial";
6082
- const activeOtpErrorMessage = state.verificationTarget?.kind === "email" && emailLoginState.status === "error" ? emailLoginState.error?.message ?? "Failed to continue with email." : state.verificationTarget?.kind === "phone" && smsLoginState.status === "error" ? smsLoginState.error?.message ?? "Failed to continue with phone number." : null;
5922
+ return {
5923
+ sourceType,
5924
+ sourceId,
5925
+ selectedAccount,
5926
+ selectedWallet,
5927
+ selectedSource,
5928
+ sourceName,
5929
+ sourceAddress,
5930
+ sourceVerified,
5931
+ pendingConnections,
5932
+ depositEligibleAccounts,
5933
+ maxSourceBalance,
5934
+ tokenCount
5935
+ };
5936
+ }
5937
+ function useAuthHandlers(dispatch, verificationTarget) {
5938
+ const {
5939
+ sendCode: sendEmailCode,
5940
+ loginWithCode: loginWithEmailCode,
5941
+ state: emailLoginState
5942
+ } = reactAuth.useLoginWithEmail();
5943
+ const {
5944
+ sendCode: sendSmsCode,
5945
+ loginWithCode: loginWithSmsCode,
5946
+ state: smsLoginState
5947
+ } = reactAuth.useLoginWithSms();
5948
+ const [authInput, setAuthInput] = react.useState("");
5949
+ const [otpCode, setOtpCode] = react.useState("");
5950
+ const activeOtpStatus = verificationTarget?.kind === "email" ? emailLoginState.status : verificationTarget?.kind === "phone" ? smsLoginState.status : "initial";
5951
+ const activeOtpErrorMessage = verificationTarget?.kind === "email" && emailLoginState.status === "error" ? emailLoginState.error?.message ?? "Failed to continue with email." : verificationTarget?.kind === "phone" && smsLoginState.status === "error" ? smsLoginState.error?.message ?? "Failed to continue with phone number." : null;
6083
5952
  const handleSendLoginCode = react.useCallback(async () => {
6084
5953
  const normalizedIdentifier = normalizeAuthIdentifier(authInput);
6085
5954
  if (!normalizedIdentifier) {
@@ -6101,9 +5970,9 @@ function SwypePaymentInner({
6101
5970
  error: err instanceof Error ? err.message : "Failed to send verification code"
6102
5971
  });
6103
5972
  }
6104
- }, [authInput, sendEmailCode, sendSmsCode]);
5973
+ }, [authInput, sendEmailCode, sendSmsCode, dispatch]);
6105
5974
  const handleVerifyLoginCode = react.useCallback(async () => {
6106
- if (!state.verificationTarget) return;
5975
+ if (!verificationTarget) return;
6107
5976
  const trimmedCode = otpCode.trim();
6108
5977
  if (!/^\d{6}$/.test(trimmedCode)) {
6109
5978
  dispatch({ type: "SET_ERROR", error: "Enter the 6-digit verification code." });
@@ -6111,7 +5980,7 @@ function SwypePaymentInner({
6111
5980
  }
6112
5981
  dispatch({ type: "SET_ERROR", error: null });
6113
5982
  try {
6114
- if (state.verificationTarget.kind === "email") {
5983
+ if (verificationTarget.kind === "email") {
6115
5984
  await loginWithEmailCode({ code: trimmedCode });
6116
5985
  } else {
6117
5986
  await loginWithSmsCode({ code: trimmedCode });
@@ -6123,15 +5992,15 @@ function SwypePaymentInner({
6123
5992
  error: err instanceof Error ? err.message : "Failed to verify code"
6124
5993
  });
6125
5994
  }
6126
- }, [state.verificationTarget, otpCode, loginWithEmailCode, loginWithSmsCode]);
5995
+ }, [verificationTarget, otpCode, loginWithEmailCode, loginWithSmsCode, dispatch]);
6127
5996
  const handleResendLoginCode = react.useCallback(async () => {
6128
- if (!state.verificationTarget) return;
5997
+ if (!verificationTarget) return;
6129
5998
  dispatch({ type: "SET_ERROR", error: null });
6130
5999
  try {
6131
- if (state.verificationTarget.kind === "email") {
6132
- await sendEmailCode({ email: state.verificationTarget.value });
6000
+ if (verificationTarget.kind === "email") {
6001
+ await sendEmailCode({ email: verificationTarget.value });
6133
6002
  } else {
6134
- await sendSmsCode({ phoneNumber: state.verificationTarget.value });
6003
+ await sendSmsCode({ phoneNumber: verificationTarget.value });
6135
6004
  }
6136
6005
  } catch (err) {
6137
6006
  captureException(err);
@@ -6140,7 +6009,68 @@ function SwypePaymentInner({
6140
6009
  error: err instanceof Error ? err.message : "Failed to resend code"
6141
6010
  });
6142
6011
  }
6143
- }, [state.verificationTarget, sendEmailCode, sendSmsCode]);
6012
+ }, [verificationTarget, sendEmailCode, sendSmsCode, dispatch]);
6013
+ return {
6014
+ authInput,
6015
+ otpCode,
6016
+ activeOtpStatus,
6017
+ activeOtpErrorMessage,
6018
+ setAuthInput,
6019
+ setOtpCode,
6020
+ handleSendLoginCode,
6021
+ handleVerifyLoginCode,
6022
+ handleResendLoginCode
6023
+ };
6024
+ }
6025
+
6026
+ // src/mobileFlow.ts
6027
+ function hasActiveWallet(accounts) {
6028
+ return accounts.some((account) => account.wallets.some((wallet) => wallet.status === "ACTIVE"));
6029
+ }
6030
+ function resolvePostAuthStep(state) {
6031
+ if (!state.hasPasskey) {
6032
+ return { step: "create-passkey", clearPersistedFlow: false };
6033
+ }
6034
+ if (state.persistedMobileFlow) {
6035
+ if (state.persistedMobileFlow.isReauthorization) {
6036
+ return { step: "open-wallet", clearPersistedFlow: false };
6037
+ }
6038
+ if (state.persistedMobileFlow.isSetup && hasActiveWallet(state.accounts)) {
6039
+ return { step: "deposit", clearPersistedFlow: true };
6040
+ }
6041
+ return { step: "open-wallet", clearPersistedFlow: false };
6042
+ }
6043
+ if (state.mobileSetupInProgress && !hasActiveWallet(state.accounts)) {
6044
+ return { step: "open-wallet", clearPersistedFlow: false };
6045
+ }
6046
+ if ((state.accounts.length === 0 || !hasActiveWallet(state.accounts)) && !state.connectingNewAccount) {
6047
+ return { step: "wallet-picker", clearPersistedFlow: false };
6048
+ }
6049
+ return { step: "deposit", clearPersistedFlow: false };
6050
+ }
6051
+ function resolveRestoredMobileFlow(transferStatus, isSetup) {
6052
+ if (transferStatus === "AUTHORIZED") {
6053
+ return isSetup ? { kind: "resume-setup-deposit", step: "deposit", clearPersistedFlow: true } : { kind: "resume-confirm-sign", step: "confirm-sign", clearPersistedFlow: true };
6054
+ }
6055
+ if (transferStatus === "COMPLETED") {
6056
+ return { kind: "resume-success", step: "success", clearPersistedFlow: true };
6057
+ }
6058
+ if (transferStatus === "FAILED") {
6059
+ return { kind: "resume-failed", step: "success", clearPersistedFlow: true };
6060
+ }
6061
+ if (transferStatus === "SENDING" || transferStatus === "SENT") {
6062
+ return { kind: "resume-processing", step: "processing", clearPersistedFlow: true };
6063
+ }
6064
+ if (isSetup) {
6065
+ return { kind: "resume-stale-setup", step: "wallet-picker", clearPersistedFlow: true };
6066
+ }
6067
+ return { kind: "resume-open-wallet", step: "open-wallet", clearPersistedFlow: false };
6068
+ }
6069
+
6070
+ // src/hooks/usePasskeyHandlers.ts
6071
+ function usePasskeyHandlers(dispatch, apiBaseUrl, accounts, knownCredentialIds, mobileSetupFlowRef) {
6072
+ const { user, getAccessToken } = reactAuth.usePrivy();
6073
+ const checkingPasskeyRef = react.useRef(false);
6144
6074
  const completePasskeyRegistration = react.useCallback(async (credentialId, publicKey) => {
6145
6075
  const token = await getAccessToken();
6146
6076
  if (!token) throw new Error("Not authenticated");
@@ -6149,14 +6079,14 @@ function SwypePaymentInner({
6149
6079
  window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
6150
6080
  const resolved = resolvePostAuthStep({
6151
6081
  hasPasskey: true,
6152
- accounts: state.accounts,
6082
+ accounts,
6153
6083
  persistedMobileFlow: loadMobileFlowState(),
6154
6084
  mobileSetupInProgress: mobileSetupFlowRef.current,
6155
6085
  connectingNewAccount: false
6156
6086
  });
6157
6087
  if (resolved.clearPersistedFlow) clearMobileFlowState();
6158
6088
  dispatch({ type: "NAVIGATE", step: resolved.step });
6159
- }, [getAccessToken, apiBaseUrl, state.accounts]);
6089
+ }, [getAccessToken, apiBaseUrl, accounts, mobileSetupFlowRef, dispatch]);
6160
6090
  const handleRegisterPasskey = react.useCallback(async () => {
6161
6091
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
6162
6092
  dispatch({ type: "SET_ERROR", error: null });
@@ -6180,7 +6110,7 @@ function SwypePaymentInner({
6180
6110
  } finally {
6181
6111
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
6182
6112
  }
6183
- }, [user, completePasskeyRegistration]);
6113
+ }, [user, completePasskeyRegistration, dispatch]);
6184
6114
  const handleCreatePasskeyViaPopup = react.useCallback(async () => {
6185
6115
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
6186
6116
  dispatch({ type: "SET_ERROR", error: null });
@@ -6198,7 +6128,7 @@ function SwypePaymentInner({
6198
6128
  localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
6199
6129
  const resolved = resolvePostAuthStep({
6200
6130
  hasPasskey: true,
6201
- accounts: state.accounts,
6131
+ accounts,
6202
6132
  persistedMobileFlow: loadMobileFlowState(),
6203
6133
  mobileSetupInProgress: mobileSetupFlowRef.current,
6204
6134
  connectingNewAccount: false
@@ -6214,14 +6144,14 @@ function SwypePaymentInner({
6214
6144
  } finally {
6215
6145
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
6216
6146
  }
6217
- }, [user, getAccessToken, apiBaseUrl, state.accounts]);
6147
+ }, [user, getAccessToken, apiBaseUrl, accounts, mobileSetupFlowRef, dispatch]);
6218
6148
  const handleVerifyPasskeyViaPopup = react.useCallback(async () => {
6219
6149
  dispatch({ type: "SET_VERIFYING_PASSKEY", value: true });
6220
6150
  dispatch({ type: "SET_ERROR", error: null });
6221
6151
  try {
6222
6152
  const token = await getAccessToken();
6223
6153
  const matched = await findDevicePasskeyViaPopup({
6224
- credentialIds: state.knownCredentialIds,
6154
+ credentialIds: knownCredentialIds,
6225
6155
  rpId: resolvePasskeyRpId(),
6226
6156
  authToken: token ?? void 0,
6227
6157
  apiBaseUrl
@@ -6235,7 +6165,7 @@ function SwypePaymentInner({
6235
6165
  }
6236
6166
  const resolved = resolvePostAuthStep({
6237
6167
  hasPasskey: true,
6238
- accounts: state.accounts,
6168
+ accounts,
6239
6169
  persistedMobileFlow: loadMobileFlowState(),
6240
6170
  mobileSetupInProgress: mobileSetupFlowRef.current,
6241
6171
  connectingNewAccount: false
@@ -6257,44 +6187,48 @@ function SwypePaymentInner({
6257
6187
  } finally {
6258
6188
  dispatch({ type: "SET_VERIFYING_PASSKEY", value: false });
6259
6189
  }
6260
- }, [state.knownCredentialIds, getAccessToken, apiBaseUrl, state.accounts]);
6190
+ }, [knownCredentialIds, getAccessToken, apiBaseUrl, accounts, mobileSetupFlowRef, dispatch]);
6191
+ return {
6192
+ handleRegisterPasskey,
6193
+ handleCreatePasskeyViaPopup,
6194
+ handleVerifyPasskeyViaPopup,
6195
+ checkingPasskeyRef
6196
+ };
6197
+ }
6198
+ function useTransferHandlers(deps) {
6199
+ const {
6200
+ dispatch,
6201
+ getAccessToken,
6202
+ apiBaseUrl,
6203
+ depositAmount,
6204
+ destination,
6205
+ idempotencyKey,
6206
+ merchantAuthorization,
6207
+ onComplete,
6208
+ onError,
6209
+ polling,
6210
+ transferSigning,
6211
+ sourceType,
6212
+ sourceId,
6213
+ sourceTokenAddress,
6214
+ activeCredentialId,
6215
+ selectedAccountId,
6216
+ transfer,
6217
+ accounts
6218
+ } = deps;
6219
+ const processingStartedAtRef = react.useRef(null);
6220
+ const pollingTransferIdRef = react.useRef(null);
6261
6221
  const reloadAccounts = react.useCallback(async () => {
6262
6222
  const token = await getAccessToken();
6263
- if (!token || !state.activeCredentialId) return;
6223
+ if (!token || !activeCredentialId) return;
6264
6224
  const [accts, prov] = await Promise.all([
6265
- fetchAccounts(apiBaseUrl, token, state.activeCredentialId),
6225
+ fetchAccounts(apiBaseUrl, token, activeCredentialId),
6266
6226
  fetchProviders(apiBaseUrl, token)
6267
6227
  ]);
6268
6228
  const parsedAmt = depositAmount != null ? depositAmount : 0;
6269
- const defaults = resolveDepositSelection(accts, parsedAmt, state.selectedAccountId);
6229
+ const defaults = resolveDepositSelection(accts, parsedAmt, selectedAccountId);
6270
6230
  dispatch({ type: "ACCOUNTS_RELOADED", accounts: accts, providers: prov, defaults });
6271
- }, [getAccessToken, state.activeCredentialId, state.selectedAccountId, apiBaseUrl, depositAmount]);
6272
- const handleAuthorizedMobileReturn = react.useCallback(async (authorizedTransfer, isSetup) => {
6273
- if (handlingMobileReturnRef.current) return;
6274
- handlingMobileReturnRef.current = true;
6275
- polling.stopPolling();
6276
- if (isSetup) {
6277
- mobileSetupFlowRef.current = false;
6278
- clearMobileFlowState();
6279
- try {
6280
- await reloadAccounts();
6281
- loadingDataRef.current = false;
6282
- dispatch({ type: "MOBILE_SETUP_COMPLETE", transfer: authorizedTransfer });
6283
- } catch (err) {
6284
- handlingMobileReturnRef.current = false;
6285
- dispatch({
6286
- type: "SET_ERROR",
6287
- error: err instanceof Error ? err.message : "Wallet authorized, but we could not refresh your account yet."
6288
- });
6289
- dispatch({ type: "NAVIGATE", step: "open-wallet" });
6290
- }
6291
- return;
6292
- }
6293
- mobileSetupFlowRef.current = false;
6294
- clearMobileFlowState();
6295
- dispatch({ type: "MOBILE_SIGN_READY", transfer: authorizedTransfer });
6296
- }, [polling.stopPolling, reloadAccounts]);
6297
- handleAuthorizedMobileReturnRef.current = handleAuthorizedMobileReturn;
6231
+ }, [getAccessToken, activeCredentialId, selectedAccountId, apiBaseUrl, depositAmount, dispatch]);
6298
6232
  const handlePay = react.useCallback(async (payAmount, sourceOverrides) => {
6299
6233
  if (isNaN(payAmount) || payAmount < MIN_SEND_AMOUNT_USD) {
6300
6234
  dispatch({ type: "SET_ERROR", error: `Minimum amount is $${MIN_SEND_AMOUNT_USD.toFixed(2)}.` });
@@ -6304,7 +6238,7 @@ function SwypePaymentInner({
6304
6238
  dispatch({ type: "SET_ERROR", error: "No account or provider selected." });
6305
6239
  return;
6306
6240
  }
6307
- if (!state.activeCredentialId) {
6241
+ if (!activeCredentialId) {
6308
6242
  dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6309
6243
  dispatch({ type: "NAVIGATE", step: "create-passkey" });
6310
6244
  return;
@@ -6312,10 +6246,10 @@ function SwypePaymentInner({
6312
6246
  dispatch({ type: "PAY_STARTED", isSetupRedirect: false });
6313
6247
  processingStartedAtRef.current = Date.now();
6314
6248
  try {
6315
- if (state.transfer?.status === "AUTHORIZED") {
6316
- const signedTransfer2 = await transferSigning.signTransfer(state.transfer.id);
6249
+ if (transfer?.status === "AUTHORIZED") {
6250
+ const signedTransfer2 = await transferSigning.signTransfer(transfer.id);
6317
6251
  dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer2 });
6318
- polling.startPolling(state.transfer.id);
6252
+ polling.startPolling(transfer.id);
6319
6253
  return;
6320
6254
  }
6321
6255
  const token = await getAccessToken();
@@ -6323,7 +6257,7 @@ function SwypePaymentInner({
6323
6257
  let effectiveSourceType = sourceOverrides?.sourceType ?? sourceType;
6324
6258
  let effectiveSourceId = sourceOverrides?.sourceId ?? sourceId;
6325
6259
  if (effectiveSourceType === "accountId") {
6326
- const acct = state.accounts.find((a) => a.id === effectiveSourceId);
6260
+ const acct = accounts.find((a) => a.id === effectiveSourceId);
6327
6261
  const preferredWallet = acct ? getPreferredDepositWallet(acct, payAmount) : null;
6328
6262
  if (preferredWallet?.status === "ACTIVE") {
6329
6263
  effectiveSourceType = "walletId";
@@ -6332,10 +6266,11 @@ function SwypePaymentInner({
6332
6266
  }
6333
6267
  const t = await createTransfer(apiBaseUrl, token, {
6334
6268
  id: idempotencyKey,
6335
- credentialId: state.activeCredentialId,
6269
+ credentialId: activeCredentialId,
6336
6270
  merchantAuthorization,
6337
6271
  sourceType: effectiveSourceType,
6338
6272
  sourceId: effectiveSourceId,
6273
+ sourceTokenAddress,
6339
6274
  destination,
6340
6275
  amount: payAmount
6341
6276
  });
@@ -6355,11 +6290,7 @@ function SwypePaymentInner({
6355
6290
  } catch (err) {
6356
6291
  captureException(err);
6357
6292
  const msg = err instanceof Error ? err.message : "Transfer failed";
6358
- dispatch({
6359
- type: "PAY_ERROR",
6360
- error: msg,
6361
- fallbackStep: "deposit"
6362
- });
6293
+ dispatch({ type: "PAY_ERROR", error: msg, fallbackStep: "deposit" });
6363
6294
  onError?.(msg);
6364
6295
  } finally {
6365
6296
  dispatch({ type: "PAY_ENDED" });
@@ -6367,9 +6298,10 @@ function SwypePaymentInner({
6367
6298
  }, [
6368
6299
  sourceId,
6369
6300
  sourceType,
6370
- state.activeCredentialId,
6371
- state.transfer,
6372
- state.accounts,
6301
+ sourceTokenAddress,
6302
+ activeCredentialId,
6303
+ transfer,
6304
+ accounts,
6373
6305
  destination,
6374
6306
  apiBaseUrl,
6375
6307
  getAccessToken,
@@ -6378,51 +6310,222 @@ function SwypePaymentInner({
6378
6310
  onError,
6379
6311
  onComplete,
6380
6312
  idempotencyKey,
6381
- merchantAuthorization
6313
+ merchantAuthorization,
6314
+ dispatch
6382
6315
  ]);
6383
- const handleIncreaseLimit = react.useCallback(async () => {
6384
- if (!state.selectedAccountId) {
6385
- dispatch({ type: "SET_ERROR", error: "No account selected." });
6316
+ const handleConfirmSign = react.useCallback(async () => {
6317
+ const t = transfer ?? polling.transfer;
6318
+ if (!t) return;
6319
+ try {
6320
+ const signedTransfer = await transferSigning.signTransfer(t.id);
6321
+ clearMobileFlowState();
6322
+ dispatch({ type: "CONFIRM_SIGN_SUCCESS", transfer: signedTransfer });
6323
+ polling.startPolling(t.id);
6324
+ } catch (err) {
6325
+ captureException(err);
6326
+ const msg = err instanceof Error ? err.message : "Failed to sign transfer";
6327
+ dispatch({ type: "SET_ERROR", error: msg });
6328
+ onError?.(msg);
6329
+ }
6330
+ }, [transfer, polling.transfer, polling.startPolling, transferSigning, onError, dispatch]);
6331
+ return {
6332
+ reloadAccounts,
6333
+ handlePay,
6334
+ handleConfirmSign,
6335
+ processingStartedAtRef,
6336
+ pollingTransferIdRef
6337
+ };
6338
+ }
6339
+ function useSourceSelectionHandlers(dispatch, authExecutor) {
6340
+ const [selectSourceChainName, setSelectSourceChainName] = react.useState("");
6341
+ const [selectSourceTokenSymbol, setSelectSourceTokenSymbol] = react.useState("");
6342
+ const initializedSelectSourceActionRef = react.useRef(null);
6343
+ const preSelectSourceStepRef = react.useRef(null);
6344
+ const pendingSelectSourceAction = authExecutor.pendingSelectSource;
6345
+ const selectSourceChoices = react.useMemo(() => {
6346
+ if (!pendingSelectSourceAction) return [];
6347
+ const options = pendingSelectSourceAction.metadata?.options ?? [];
6348
+ return buildSelectSourceChoices(options);
6349
+ }, [pendingSelectSourceAction]);
6350
+ const selectSourceRecommended = react.useMemo(() => {
6351
+ if (!pendingSelectSourceAction) return null;
6352
+ return pendingSelectSourceAction.metadata?.recommended ?? null;
6353
+ }, [pendingSelectSourceAction]);
6354
+ const selectSourceAvailableBalance = react.useMemo(() => {
6355
+ if (!pendingSelectSourceAction) return 0;
6356
+ const options = pendingSelectSourceAction.metadata?.options ?? [];
6357
+ const recommended = selectSourceRecommended;
6358
+ if (recommended) {
6359
+ const match = options.find(
6360
+ (opt) => opt.chainName === recommended.chainName && opt.tokenSymbol === recommended.tokenSymbol
6361
+ );
6362
+ if (match) return Number(match.rawBalance) / Math.pow(10, match.decimals);
6363
+ }
6364
+ let max = 0;
6365
+ for (const opt of options) {
6366
+ const bal = Number(opt.rawBalance) / Math.pow(10, opt.decimals);
6367
+ if (bal > max) max = bal;
6368
+ }
6369
+ return max;
6370
+ }, [pendingSelectSourceAction, selectSourceRecommended]);
6371
+ const handleSelectSourceChainChange = react.useCallback(
6372
+ (chainName) => {
6373
+ setSelectSourceChainName(chainName);
6374
+ const chain = selectSourceChoices.find((c) => c.chainName === chainName);
6375
+ if (!chain || chain.tokens.length === 0) return;
6376
+ const recommendedToken = selectSourceRecommended?.chainName === chainName ? selectSourceRecommended.tokenSymbol : null;
6377
+ const hasRecommended = !!recommendedToken && chain.tokens.some((t) => t.tokenSymbol === recommendedToken);
6378
+ setSelectSourceTokenSymbol(
6379
+ hasRecommended ? recommendedToken : chain.tokens[0].tokenSymbol
6380
+ );
6381
+ },
6382
+ [selectSourceChoices, selectSourceRecommended]
6383
+ );
6384
+ const handleConfirmSelectSource = react.useCallback(() => {
6385
+ authExecutor.resolveSelectSource({
6386
+ chainName: selectSourceChainName,
6387
+ tokenSymbol: selectSourceTokenSymbol
6388
+ });
6389
+ }, [authExecutor, selectSourceChainName, selectSourceTokenSymbol]);
6390
+ return {
6391
+ selectSourceChainName,
6392
+ selectSourceTokenSymbol,
6393
+ setSelectSourceChainName,
6394
+ setSelectSourceTokenSymbol,
6395
+ selectSourceChoices,
6396
+ selectSourceRecommended,
6397
+ selectSourceAvailableBalance,
6398
+ handleSelectSourceChainChange,
6399
+ handleConfirmSelectSource,
6400
+ pendingSelectSourceAction,
6401
+ initializedSelectSourceActionRef,
6402
+ preSelectSourceStepRef
6403
+ };
6404
+ }
6405
+ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs) {
6406
+ const {
6407
+ mobileSetupFlowRef,
6408
+ handlingMobileReturnRef,
6409
+ loadingDataRef
6410
+ } = refs;
6411
+ const handleAuthorizedMobileReturn = react.useCallback(async (authorizedTransfer, isSetup) => {
6412
+ if (handlingMobileReturnRef.current) return;
6413
+ handlingMobileReturnRef.current = true;
6414
+ polling.stopPolling();
6415
+ if (isSetup) {
6416
+ mobileSetupFlowRef.current = false;
6417
+ clearMobileFlowState();
6418
+ try {
6419
+ await reloadAccounts();
6420
+ loadingDataRef.current = false;
6421
+ dispatch({ type: "MOBILE_SETUP_COMPLETE", transfer: authorizedTransfer });
6422
+ } catch (err) {
6423
+ handlingMobileReturnRef.current = false;
6424
+ dispatch({
6425
+ type: "SET_ERROR",
6426
+ error: err instanceof Error ? err.message : "Wallet authorized, but we could not refresh your account yet."
6427
+ });
6428
+ dispatch({ type: "NAVIGATE", step: "open-wallet" });
6429
+ }
6430
+ return;
6431
+ }
6432
+ mobileSetupFlowRef.current = false;
6433
+ clearMobileFlowState();
6434
+ dispatch({ type: "MOBILE_SIGN_READY", transfer: authorizedTransfer });
6435
+ }, [polling.stopPolling, reloadAccounts, dispatch]);
6436
+ const handleRetryMobileStatus = react.useCallback(() => {
6437
+ dispatch({ type: "SET_ERROR", error: null });
6438
+ handlingMobileReturnRef.current = false;
6439
+ const currentTransfer = polling.transfer ?? stateTransfer;
6440
+ if (currentTransfer?.status === "AUTHORIZED") {
6441
+ void handleAuthorizedMobileReturn(currentTransfer, mobileSetupFlowRef.current);
6386
6442
  return;
6387
6443
  }
6388
- if (!state.activeCredentialId) {
6444
+ const transferIdToResume = pollingTransferIdRef.current ?? currentTransfer?.id;
6445
+ if (transferIdToResume) {
6446
+ polling.startPolling(transferIdToResume);
6447
+ }
6448
+ }, [handleAuthorizedMobileReturn, polling, stateTransfer, pollingTransferIdRef, dispatch]);
6449
+ return {
6450
+ handleAuthorizedMobileReturn,
6451
+ handleRetryMobileStatus
6452
+ };
6453
+ }
6454
+
6455
+ // src/walletFlow.ts
6456
+ var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
6457
+ function isMobileUserAgent(userAgent) {
6458
+ if (!userAgent) {
6459
+ return false;
6460
+ }
6461
+ return MOBILE_USER_AGENT_PATTERN.test(userAgent);
6462
+ }
6463
+ function shouldUseWalletConnector(options) {
6464
+ return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
6465
+ }
6466
+
6467
+ // src/hooks/useProviderHandlers.ts
6468
+ function useProviderHandlers(deps) {
6469
+ const {
6470
+ dispatch,
6471
+ getAccessToken,
6472
+ apiBaseUrl,
6473
+ depositAmount,
6474
+ useWalletConnectorProp,
6475
+ activeCredentialId,
6476
+ selectedAccountId,
6477
+ accounts,
6478
+ providers,
6479
+ authExecutor,
6480
+ reloadAccounts,
6481
+ onError,
6482
+ mobileSetupFlowRef,
6483
+ handlingMobileReturnRef,
6484
+ setupAccountIdRef,
6485
+ reauthSessionIdRef,
6486
+ reauthTokenRef
6487
+ } = deps;
6488
+ const handleSelectProvider = react.useCallback(async (providerId) => {
6489
+ dispatch({ type: "SELECT_PROVIDER", providerId });
6490
+ if (!activeCredentialId) {
6389
6491
  dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6390
6492
  dispatch({ type: "NAVIGATE", step: "create-passkey" });
6391
6493
  return;
6392
6494
  }
6393
- const acct = state.accounts.find((a) => a.id === state.selectedAccountId);
6394
- const matchedProvider = acct ? state.providers.find((p) => p.name === acct.name) : void 0;
6395
- if (matchedProvider) {
6396
- dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
6495
+ const provider = providers.find((p) => p.id === providerId);
6496
+ const providerName = provider?.name ?? "Wallet";
6497
+ const isMobile = !shouldUseWalletConnector({
6498
+ useWalletConnector: useWalletConnectorProp,
6499
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6500
+ });
6501
+ if (isMobile) {
6502
+ dispatch({ type: "PAY_STARTED", isSetupRedirect: true });
6503
+ } else {
6504
+ dispatch({ type: "PAY_STARTED", isSetupRedirect: false });
6505
+ dispatch({ type: "NAVIGATE", step: "setup-status" });
6397
6506
  }
6398
- dispatch({ type: "SET_ERROR", error: null });
6399
- dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6400
6507
  try {
6401
6508
  const token = await getAccessToken();
6402
6509
  if (!token) throw new Error("Not authenticated");
6403
- const session = await createAccountAuthorizationSession(
6404
- apiBaseUrl,
6405
- token,
6406
- state.selectedAccountId,
6407
- state.activeCredentialId
6408
- );
6409
- const isMobile = !shouldUseWalletConnector({
6410
- useWalletConnector: useWalletConnectorProp,
6411
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6510
+ const accountId = crypto.randomUUID();
6511
+ const account = await createAccount(apiBaseUrl, token, {
6512
+ id: accountId,
6513
+ name: providerName,
6514
+ credentialId: activeCredentialId,
6515
+ providerId
6412
6516
  });
6517
+ const session = account.authorizationSessions?.[0];
6518
+ if (!session) throw new Error("No authorization session returned.");
6413
6519
  if (isMobile) {
6414
6520
  handlingMobileReturnRef.current = false;
6415
6521
  mobileSetupFlowRef.current = true;
6416
- setupAccountIdRef.current = state.selectedAccountId;
6417
- reauthSessionIdRef.current = session.id;
6418
- reauthTokenRef.current = null;
6522
+ setupAccountIdRef.current = account.id;
6419
6523
  persistMobileFlowState({
6420
- accountId: state.selectedAccountId,
6524
+ accountId: account.id,
6421
6525
  sessionId: session.id,
6422
6526
  deeplinkUri: session.uri,
6423
- providerId: matchedProvider?.id ?? null,
6424
- isSetup: true,
6425
- isReauthorization: true
6527
+ providerId,
6528
+ isSetup: true
6426
6529
  });
6427
6530
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6428
6531
  triggerDeeplink(session.uri);
@@ -6433,57 +6536,75 @@ function SwypePaymentInner({
6433
6536
  }
6434
6537
  } catch (err) {
6435
6538
  captureException(err);
6436
- const msg = err instanceof Error ? err.message : "Failed to increase limit";
6437
- dispatch({ type: "SET_ERROR", error: msg });
6539
+ const msg = err instanceof Error ? err.message : "Failed to set up wallet";
6540
+ dispatch({ type: "PAY_ERROR", error: msg, fallbackStep: "wallet-picker" });
6438
6541
  onError?.(msg);
6439
6542
  } finally {
6440
- dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6543
+ dispatch({ type: "PAY_ENDED" });
6441
6544
  }
6442
6545
  }, [
6443
- state.selectedAccountId,
6444
- state.activeCredentialId,
6445
- state.accounts,
6446
- state.providers,
6546
+ activeCredentialId,
6547
+ providers,
6447
6548
  apiBaseUrl,
6448
6549
  getAccessToken,
6449
6550
  authExecutor,
6450
6551
  useWalletConnectorProp,
6451
6552
  reloadAccounts,
6452
- onError
6553
+ onError,
6554
+ dispatch,
6555
+ mobileSetupFlowRef,
6556
+ handlingMobileReturnRef,
6557
+ setupAccountIdRef
6453
6558
  ]);
6454
- const handleNavigateToTokenPicker = react.useCallback(() => {
6455
- dispatch({ type: "NAVIGATE", step: "token-picker" });
6456
- }, []);
6457
- const handleSelectAuthorizedToken = react.useCallback((walletId, tokenSymbol) => {
6458
- dispatch({ type: "SELECT_TOKEN", walletId, tokenSymbol });
6459
- }, []);
6460
- const handleAuthorizeToken = react.useCallback(async (_walletId, tokenAddress, chainId, tokenSymbol) => {
6461
- if (!state.selectedAccountId) {
6559
+ const handleContinueConnection = react.useCallback(
6560
+ (accountId) => {
6561
+ const acct = accounts.find((a) => a.id === accountId);
6562
+ if (!acct) return;
6563
+ const matchedProvider = providers.find((p) => p.name === acct.name);
6564
+ if (matchedProvider) {
6565
+ handleSelectProvider(matchedProvider.id);
6566
+ }
6567
+ },
6568
+ [accounts, providers, handleSelectProvider]
6569
+ );
6570
+ const handleSelectAccount = react.useCallback(
6571
+ (accountId) => {
6572
+ const acct = accounts.find((a) => a.id === accountId);
6573
+ if (!acct) return;
6574
+ const activeWallet = getPreferredDepositWallet(acct, depositAmount ?? 0);
6575
+ dispatch({
6576
+ type: "SELECT_ACCOUNT",
6577
+ accountId,
6578
+ walletId: activeWallet?.id ?? null
6579
+ });
6580
+ },
6581
+ [accounts, depositAmount, dispatch]
6582
+ );
6583
+ const handleIncreaseLimit = react.useCallback(async () => {
6584
+ if (!selectedAccountId) {
6462
6585
  dispatch({ type: "SET_ERROR", error: "No account selected." });
6463
6586
  return;
6464
6587
  }
6465
- if (!state.activeCredentialId) {
6588
+ if (!activeCredentialId) {
6466
6589
  dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6467
6590
  dispatch({ type: "NAVIGATE", step: "create-passkey" });
6468
6591
  return;
6469
6592
  }
6470
- const acct = state.accounts.find((a) => a.id === state.selectedAccountId);
6471
- const matchedProvider = acct ? state.providers.find((p) => p.name === acct.name) : void 0;
6593
+ const acct = accounts.find((a) => a.id === selectedAccountId);
6594
+ const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
6472
6595
  if (matchedProvider) {
6473
6596
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
6474
6597
  }
6475
6598
  dispatch({ type: "SET_ERROR", error: null });
6476
6599
  dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6477
- pendingTokenAuthRef.current = { tokenAddress, chainId, tokenSymbol, walletId: _walletId };
6478
6600
  try {
6479
6601
  const token = await getAccessToken();
6480
6602
  if (!token) throw new Error("Not authenticated");
6481
6603
  const session = await createAccountAuthorizationSession(
6482
6604
  apiBaseUrl,
6483
6605
  token,
6484
- state.selectedAccountId,
6485
- state.activeCredentialId,
6486
- { tokenAddress, chainId }
6606
+ selectedAccountId,
6607
+ activeCredentialId
6487
6608
  );
6488
6609
  const isMobile = !shouldUseWalletConnector({
6489
6610
  useWalletConnector: useWalletConnectorProp,
@@ -6492,205 +6613,187 @@ function SwypePaymentInner({
6492
6613
  if (isMobile) {
6493
6614
  handlingMobileReturnRef.current = false;
6494
6615
  mobileSetupFlowRef.current = true;
6495
- setupAccountIdRef.current = state.selectedAccountId;
6616
+ setupAccountIdRef.current = selectedAccountId;
6496
6617
  reauthSessionIdRef.current = session.id;
6497
- reauthTokenRef.current = { walletId: _walletId, tokenSymbol };
6618
+ reauthTokenRef.current = null;
6498
6619
  persistMobileFlowState({
6499
- accountId: state.selectedAccountId,
6620
+ accountId: selectedAccountId,
6500
6621
  sessionId: session.id,
6501
6622
  deeplinkUri: session.uri,
6502
6623
  providerId: matchedProvider?.id ?? null,
6503
6624
  isSetup: true,
6504
- isReauthorization: true,
6505
- reauthorizationToken: { walletId: _walletId, tokenSymbol }
6625
+ isReauthorization: true
6506
6626
  });
6507
6627
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6508
6628
  triggerDeeplink(session.uri);
6509
6629
  } else {
6510
- pendingTokenAuthSessionRef.current = {
6511
- sessionId: session.id,
6512
- walletId: _walletId,
6513
- tokenSymbol,
6514
- tokenAddress,
6515
- chainId
6516
- };
6517
- dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
6518
- dispatch({ type: "NAVIGATE", step: "setup" });
6630
+ dispatch({ type: "NAVIGATE", step: "setup-status" });
6631
+ await authExecutor.executeSessionById(session.id);
6632
+ await reloadAccounts();
6633
+ dispatch({ type: "NAVIGATE", step: "deposit" });
6519
6634
  }
6520
6635
  } catch (err) {
6521
6636
  captureException(err);
6522
- const msg = err instanceof Error ? err.message : "Failed to authorize token";
6637
+ const msg = err instanceof Error ? err.message : "Failed to increase limit";
6523
6638
  dispatch({ type: "SET_ERROR", error: msg });
6524
6639
  onError?.(msg);
6525
6640
  } finally {
6526
- pendingTokenAuthRef.current = null;
6527
6641
  dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6528
6642
  }
6529
6643
  }, [
6530
- state.selectedAccountId,
6531
- state.activeCredentialId,
6532
- state.accounts,
6533
- state.providers,
6644
+ selectedAccountId,
6645
+ activeCredentialId,
6646
+ accounts,
6647
+ providers,
6534
6648
  apiBaseUrl,
6535
6649
  getAccessToken,
6536
6650
  authExecutor,
6537
6651
  useWalletConnectorProp,
6538
6652
  reloadAccounts,
6539
- onError
6653
+ onError,
6654
+ dispatch,
6655
+ mobileSetupFlowRef,
6656
+ handlingMobileReturnRef,
6657
+ setupAccountIdRef,
6658
+ reauthSessionIdRef,
6659
+ reauthTokenRef
6540
6660
  ]);
6541
- const handleConfirmSign = react.useCallback(async () => {
6542
- const t = state.transfer ?? polling.transfer;
6543
- if (!t) return;
6544
- try {
6545
- const signedTransfer = await transferSigning.signTransfer(t.id);
6546
- clearMobileFlowState();
6547
- dispatch({ type: "CONFIRM_SIGN_SUCCESS", transfer: signedTransfer });
6548
- polling.startPolling(t.id);
6549
- } catch (err) {
6550
- captureException(err);
6551
- const msg = err instanceof Error ? err.message : "Failed to sign transfer";
6552
- dispatch({ type: "SET_ERROR", error: msg });
6553
- onError?.(msg);
6661
+ const handleNavigateToTokenPicker = react.useCallback(() => {
6662
+ if (authExecutor.pendingSelectSource) {
6663
+ dispatch({ type: "NAVIGATE", step: "select-source" });
6664
+ } else {
6665
+ dispatch({ type: "NAVIGATE", step: "token-picker" });
6554
6666
  }
6555
- }, [state.transfer, polling.transfer, polling.startPolling, transferSigning, onError]);
6556
- const handleRetryMobileStatus = react.useCallback(() => {
6557
- dispatch({ type: "SET_ERROR", error: null });
6558
- handlingMobileReturnRef.current = false;
6559
- const currentTransfer = polling.transfer ?? state.transfer;
6560
- if (currentTransfer?.status === "AUTHORIZED") {
6561
- void handleAuthorizedMobileReturn(currentTransfer, mobileSetupFlowRef.current);
6667
+ }, [dispatch, authExecutor.pendingSelectSource]);
6668
+ const handleSelectAuthorizedToken = react.useCallback((walletId, tokenSymbol) => {
6669
+ dispatch({ type: "SELECT_TOKEN", walletId, tokenSymbol });
6670
+ }, [dispatch]);
6671
+ const handleAuthorizeToken = react.useCallback(async (_walletId, tokenAddress, chainId, tokenSymbol) => {
6672
+ if (!selectedAccountId) {
6673
+ dispatch({ type: "SET_ERROR", error: "No account selected." });
6562
6674
  return;
6563
6675
  }
6564
- const transferIdToResume = pollingTransferIdRef.current ?? currentTransfer?.id;
6565
- if (transferIdToResume) {
6566
- polling.startPolling(transferIdToResume);
6567
- }
6568
- }, [handleAuthorizedMobileReturn, polling, state.transfer]);
6569
- const handleSelectProvider = react.useCallback(async (providerId) => {
6570
- dispatch({ type: "SELECT_PROVIDER", providerId });
6571
- if (!state.activeCredentialId) {
6676
+ if (!activeCredentialId) {
6572
6677
  dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6573
6678
  dispatch({ type: "NAVIGATE", step: "create-passkey" });
6574
6679
  return;
6575
6680
  }
6576
- const provider = state.providers.find((p) => p.id === providerId);
6577
- const providerName = provider?.name ?? "Wallet";
6578
- const isMobile = !shouldUseWalletConnector({
6579
- useWalletConnector: useWalletConnectorProp,
6580
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6581
- });
6582
- if (isMobile) {
6583
- dispatch({ type: "PAY_STARTED", isSetupRedirect: true });
6584
- } else {
6585
- dispatch({ type: "PAY_STARTED", isSetupRedirect: false });
6586
- dispatch({ type: "NAVIGATE", step: "setup-status" });
6681
+ const acct = accounts.find((a) => a.id === selectedAccountId);
6682
+ const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
6683
+ if (matchedProvider) {
6684
+ dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
6587
6685
  }
6686
+ dispatch({ type: "SET_ERROR", error: null });
6687
+ dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6588
6688
  try {
6589
6689
  const token = await getAccessToken();
6590
6690
  if (!token) throw new Error("Not authenticated");
6591
- const accountId = crypto.randomUUID();
6592
- const account = await createAccount(apiBaseUrl, token, {
6593
- id: accountId,
6594
- name: providerName,
6595
- credentialId: state.activeCredentialId,
6596
- providerId
6691
+ const session = await createAccountAuthorizationSession(
6692
+ apiBaseUrl,
6693
+ token,
6694
+ selectedAccountId,
6695
+ activeCredentialId,
6696
+ { tokenAddress, chainId }
6697
+ );
6698
+ const isMobile = !shouldUseWalletConnector({
6699
+ useWalletConnector: useWalletConnectorProp,
6700
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6597
6701
  });
6598
- const session = account.authorizationSessions?.[0];
6599
- if (!session) throw new Error("No authorization session returned.");
6600
6702
  if (isMobile) {
6601
6703
  handlingMobileReturnRef.current = false;
6602
6704
  mobileSetupFlowRef.current = true;
6603
- setupAccountIdRef.current = account.id;
6705
+ setupAccountIdRef.current = selectedAccountId;
6706
+ reauthSessionIdRef.current = session.id;
6707
+ reauthTokenRef.current = { walletId: _walletId, tokenSymbol };
6604
6708
  persistMobileFlowState({
6605
- accountId: account.id,
6709
+ accountId: selectedAccountId,
6606
6710
  sessionId: session.id,
6607
6711
  deeplinkUri: session.uri,
6608
- providerId,
6609
- isSetup: true
6712
+ providerId: matchedProvider?.id ?? null,
6713
+ isSetup: true,
6714
+ isReauthorization: true,
6715
+ reauthorizationToken: { walletId: _walletId, tokenSymbol }
6610
6716
  });
6611
6717
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6612
6718
  triggerDeeplink(session.uri);
6613
6719
  } else {
6720
+ dispatch({ type: "NAVIGATE", step: "setup-status" });
6614
6721
  await authExecutor.executeSessionById(session.id);
6615
6722
  await reloadAccounts();
6616
- dispatch({ type: "NAVIGATE", step: "deposit" });
6723
+ dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
6617
6724
  }
6618
6725
  } catch (err) {
6619
6726
  captureException(err);
6620
- const msg = err instanceof Error ? err.message : "Failed to set up wallet";
6621
- dispatch({ type: "PAY_ERROR", error: msg, fallbackStep: "wallet-picker" });
6727
+ const msg = err instanceof Error ? err.message : "Failed to authorize token";
6728
+ dispatch({ type: "SET_ERROR", error: msg });
6622
6729
  onError?.(msg);
6623
6730
  } finally {
6624
- dispatch({ type: "PAY_ENDED" });
6731
+ dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6625
6732
  }
6626
6733
  }, [
6627
- state.activeCredentialId,
6628
- state.providers,
6734
+ selectedAccountId,
6735
+ activeCredentialId,
6736
+ accounts,
6737
+ providers,
6629
6738
  apiBaseUrl,
6630
6739
  getAccessToken,
6631
6740
  authExecutor,
6632
6741
  useWalletConnectorProp,
6633
6742
  reloadAccounts,
6634
- onError
6743
+ onError,
6744
+ dispatch,
6745
+ mobileSetupFlowRef,
6746
+ handlingMobileReturnRef,
6747
+ setupAccountIdRef,
6748
+ reauthSessionIdRef,
6749
+ reauthTokenRef
6635
6750
  ]);
6636
- const handleContinueConnection = react.useCallback(
6637
- (accountId) => {
6638
- const acct = state.accounts.find((a) => a.id === accountId);
6639
- if (!acct) return;
6640
- const matchedProvider = state.providers.find((p) => p.name === acct.name);
6641
- if (matchedProvider) {
6642
- handleSelectProvider(matchedProvider.id);
6643
- }
6644
- },
6645
- [state.accounts, state.providers, handleSelectProvider]
6646
- );
6647
- const handleSelectAccount = react.useCallback(
6648
- (accountId) => {
6649
- const acct = state.accounts.find((a) => a.id === accountId);
6650
- if (!acct) return;
6651
- const activeWallet = getPreferredDepositWallet(acct, depositAmount ?? 0);
6652
- dispatch({
6653
- type: "SELECT_ACCOUNT",
6654
- accountId,
6655
- walletId: activeWallet?.id ?? null
6656
- });
6657
- },
6658
- [state.accounts, depositAmount]
6659
- );
6751
+ return {
6752
+ handleSelectProvider,
6753
+ handleContinueConnection,
6754
+ handleSelectAccount,
6755
+ handleIncreaseLimit,
6756
+ handleNavigateToTokenPicker,
6757
+ handleSelectAuthorizedToken,
6758
+ handleAuthorizeToken
6759
+ };
6760
+ }
6761
+ function useOneTapSetupHandlers(deps) {
6762
+ const {
6763
+ dispatch,
6764
+ getAccessToken,
6765
+ apiBaseUrl,
6766
+ authExecutor,
6767
+ selectSourceChainName,
6768
+ selectSourceTokenSymbol
6769
+ } = deps;
6770
+ const [savingOneTapLimit, setSavingOneTapLimit] = react.useState(false);
6771
+ const oneTapLimitSavedDuringSetupRef = react.useRef(false);
6660
6772
  const handleSetupOneTap = react.useCallback(async (limit) => {
6661
6773
  setSavingOneTapLimit(true);
6662
6774
  try {
6663
6775
  const token = await getAccessToken();
6664
6776
  if (!token) throw new Error("Not authenticated");
6665
6777
  await updateUserConfig(apiBaseUrl, token, { defaultAllowance: limit });
6666
- if (authExecutor.pendingOneTapSetup) {
6667
- authExecutor.resolveOneTapSetup();
6668
- } else if (pendingTokenAuthSessionRef.current) {
6669
- const pending = pendingTokenAuthSessionRef.current;
6670
- pendingTokenAuthSessionRef.current = null;
6671
- pendingTokenAuthRef.current = {
6672
- tokenAddress: pending.tokenAddress,
6673
- chainId: pending.chainId,
6674
- tokenSymbol: pending.tokenSymbol,
6675
- walletId: pending.walletId
6676
- };
6677
- dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6678
- dispatch({ type: "NAVIGATE", step: "setup-status" });
6679
- try {
6680
- await authExecutor.executeSessionById(pending.sessionId);
6681
- await reloadAccounts();
6682
- dispatch({ type: "SELECT_TOKEN", walletId: pending.walletId, tokenSymbol: pending.tokenSymbol });
6683
- } catch (authErr) {
6684
- captureException(authErr);
6685
- dispatch({
6686
- type: "SET_ERROR",
6687
- error: authErr instanceof Error ? authErr.message : "Failed to authorize token"
6688
- });
6689
- dispatch({ type: "NAVIGATE", step: "deposit" });
6690
- } finally {
6691
- pendingTokenAuthRef.current = null;
6692
- dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6778
+ if (authExecutor.pendingSelectSource) {
6779
+ const action = authExecutor.pendingSelectSource;
6780
+ const recommended = action.metadata?.recommended;
6781
+ let chainName;
6782
+ let tokenSymbol;
6783
+ if (selectSourceChainName && selectSourceTokenSymbol) {
6784
+ chainName = selectSourceChainName;
6785
+ tokenSymbol = selectSourceTokenSymbol;
6786
+ } else {
6787
+ const options = action.metadata?.options ?? [];
6788
+ const choices = buildSelectSourceChoices(options);
6789
+ chainName = recommended?.chainName ?? choices[0]?.chainName ?? "Base";
6790
+ tokenSymbol = recommended?.tokenSymbol ?? choices[0]?.tokens[0]?.tokenSymbol ?? "USDC";
6693
6791
  }
6792
+ oneTapLimitSavedDuringSetupRef.current = true;
6793
+ authExecutor.resolveSelectSource({ chainName, tokenSymbol });
6794
+ dispatch({ type: "NAVIGATE", step: "setup-status" });
6795
+ } else if (authExecutor.pendingOneTapSetup) {
6796
+ authExecutor.resolveOneTapSetup();
6694
6797
  } else {
6695
6798
  dispatch({ type: "NAVIGATE", step: "deposit" });
6696
6799
  }
@@ -6703,76 +6806,118 @@ function SwypePaymentInner({
6703
6806
  } finally {
6704
6807
  setSavingOneTapLimit(false);
6705
6808
  }
6706
- }, [getAccessToken, apiBaseUrl, authExecutor, reloadAccounts, onError]);
6707
- const handleNewPayment = react.useCallback(() => {
6708
- clearMobileFlowState();
6709
- processingStartedAtRef.current = null;
6710
- pollingTransferIdRef.current = null;
6711
- preSelectSourceStepRef.current = null;
6712
- pendingTokenSelectionRef.current = null;
6713
- pendingTokenAuthSessionRef.current = null;
6714
- dispatch({
6715
- type: "NEW_PAYMENT",
6716
- depositAmount,
6717
- firstAccountId: state.accounts.length > 0 ? state.accounts[0].id : null
6718
- });
6719
- }, [depositAmount, state.accounts]);
6720
- const handleLogout = react.useCallback(async () => {
6721
- try {
6722
- await logout();
6723
- } catch {
6724
- }
6725
- clearMobileFlowState();
6726
- if (typeof window !== "undefined") {
6727
- window.localStorage.removeItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
6728
- }
6729
- polling.stopPolling();
6730
- preSelectSourceStepRef.current = null;
6731
- pendingTokenSelectionRef.current = null;
6732
- pendingTokenAuthSessionRef.current = null;
6733
- checkingPasskeyRef.current = false;
6734
- setAuthInput("");
6735
- setOtpCode("");
6736
- dispatch({ type: "LOGOUT", depositAmount });
6737
- }, [logout, polling, depositAmount]);
6738
- const pendingSelectSourceAction = authExecutor.pendingSelectSource;
6739
- const selectSourceChoices = react.useMemo(() => {
6740
- if (!pendingSelectSourceAction) return [];
6741
- const options = pendingSelectSourceAction.metadata?.options ?? [];
6742
- return buildSelectSourceChoices(options);
6743
- }, [pendingSelectSourceAction]);
6744
- const selectSourceRecommended = react.useMemo(() => {
6745
- if (!pendingSelectSourceAction) return null;
6746
- return pendingSelectSourceAction.metadata?.recommended ?? null;
6747
- }, [pendingSelectSourceAction]);
6748
- const handleSelectSourceChainChange = react.useCallback(
6749
- (chainName) => {
6750
- setSelectSourceChainName(chainName);
6751
- const chain = selectSourceChoices.find((c) => c.chainName === chainName);
6752
- if (!chain || chain.tokens.length === 0) return;
6753
- const recommendedToken = selectSourceRecommended?.chainName === chainName ? selectSourceRecommended.tokenSymbol : null;
6754
- const hasRecommended = !!recommendedToken && chain.tokens.some((t) => t.tokenSymbol === recommendedToken);
6755
- setSelectSourceTokenSymbol(
6756
- hasRecommended ? recommendedToken : chain.tokens[0].tokenSymbol
6757
- );
6758
- },
6759
- [selectSourceChoices, selectSourceRecommended]
6760
- );
6761
- const handleConfirmSelectSource = react.useCallback(() => {
6762
- authExecutor.resolveSelectSource({
6763
- chainName: selectSourceChainName,
6764
- tokenSymbol: selectSourceTokenSymbol
6765
- });
6766
- }, [authExecutor, selectSourceChainName, selectSourceTokenSymbol]);
6809
+ }, [getAccessToken, apiBaseUrl, authExecutor, dispatch, selectSourceChainName, selectSourceTokenSymbol]);
6810
+ return {
6811
+ handleSetupOneTap,
6812
+ savingOneTapLimit,
6813
+ oneTapLimitSavedDuringSetupRef
6814
+ };
6815
+ }
6816
+
6817
+ // src/dataLoading.ts
6818
+ function resolveDataLoadAction({
6819
+ authenticated,
6820
+ step,
6821
+ accountsCount,
6822
+ hasActiveCredential,
6823
+ loading
6824
+ }) {
6825
+ if (!authenticated || step === "login" || step === "otp-verify" || accountsCount > 0 || !hasActiveCredential) {
6826
+ return "reset";
6827
+ }
6828
+ if (loading) {
6829
+ return "wait";
6830
+ }
6831
+ return "load";
6832
+ }
6833
+
6834
+ // src/processingStatus.ts
6835
+ var PROCESSING_TIMEOUT_MS = 18e4;
6836
+ function resolvePreferredTransfer(polledTransfer, localTransfer) {
6837
+ return polledTransfer ?? localTransfer;
6838
+ }
6839
+ function getTransferStatus(polledTransfer, localTransfer) {
6840
+ const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
6841
+ return transfer?.status ?? "UNKNOWN";
6842
+ }
6843
+ function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
6844
+ if (!processingStartedAtMs) return false;
6845
+ return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
6846
+ }
6847
+ var STATUS_DISPLAY_LABELS = {
6848
+ CREATED: "created",
6849
+ AUTHORIZED: "authorized",
6850
+ SENDING: "sending",
6851
+ SENT: "confirming delivery",
6852
+ COMPLETED: "completed",
6853
+ FAILED: "failed"
6854
+ };
6855
+ function getStatusDisplayLabel(status) {
6856
+ return STATUS_DISPLAY_LABELS[status] ?? status;
6857
+ }
6858
+ function buildProcessingTimeoutMessage(status) {
6859
+ const label = getStatusDisplayLabel(status);
6860
+ return `Payment is taking longer than expected (status: ${label}). Please try again.`;
6861
+ }
6862
+
6863
+ // src/hooks/usePaymentEffects.ts
6864
+ function usePaymentEffects(deps) {
6865
+ const {
6866
+ state,
6867
+ dispatch,
6868
+ ready,
6869
+ authenticated,
6870
+ apiBaseUrl,
6871
+ depositAmount,
6872
+ useWalletConnectorProp,
6873
+ onComplete,
6874
+ onError,
6875
+ polling,
6876
+ authExecutor,
6877
+ reloadAccounts,
6878
+ activeOtpStatus,
6879
+ activeOtpErrorMessage,
6880
+ otpCode,
6881
+ handleVerifyLoginCode,
6882
+ setAuthInput,
6883
+ setOtpCode,
6884
+ mobileSetupFlowRef,
6885
+ handlingMobileReturnRef,
6886
+ setupAccountIdRef,
6887
+ reauthSessionIdRef,
6888
+ reauthTokenRef,
6889
+ loadingDataRef,
6890
+ pollingTransferIdRef,
6891
+ processingStartedAtRef,
6892
+ checkingPasskeyRef,
6893
+ pendingSelectSourceAction,
6894
+ selectSourceChoices,
6895
+ selectSourceRecommended,
6896
+ setSelectSourceChainName,
6897
+ setSelectSourceTokenSymbol,
6898
+ initializedSelectSourceActionRef,
6899
+ preSelectSourceStepRef,
6900
+ oneTapLimitSavedDuringSetupRef,
6901
+ handleAuthorizedMobileReturn
6902
+ } = deps;
6903
+ const { getAccessToken } = reactAuth.usePrivy();
6904
+ const onCompleteRef = react.useRef(onComplete);
6905
+ onCompleteRef.current = onComplete;
6906
+ const getAccessTokenRef = react.useRef(getAccessToken);
6907
+ getAccessTokenRef.current = getAccessToken;
6908
+ const pollingRef = react.useRef(polling);
6909
+ pollingRef.current = polling;
6910
+ const handleAuthorizedMobileReturnRef = react.useRef(handleAuthorizedMobileReturn);
6911
+ handleAuthorizedMobileReturnRef.current = handleAuthorizedMobileReturn;
6767
6912
  react.useEffect(() => {
6768
6913
  if (depositAmount != null) {
6769
6914
  dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
6770
6915
  }
6771
- }, [depositAmount]);
6916
+ }, [depositAmount, dispatch]);
6772
6917
  react.useEffect(() => {
6773
6918
  if (authenticated || state.step !== "otp-verify") return;
6774
6919
  if (activeOtpErrorMessage) dispatch({ type: "SET_ERROR", error: activeOtpErrorMessage });
6775
- }, [activeOtpErrorMessage, authenticated, state.step]);
6920
+ }, [activeOtpErrorMessage, authenticated, state.step, dispatch]);
6776
6921
  react.useEffect(() => {
6777
6922
  if (state.step === "otp-verify" && /^\d{6}$/.test(otpCode.trim()) && activeOtpStatus === "awaiting-code-input") {
6778
6923
  handleVerifyLoginCode();
@@ -6999,7 +7144,7 @@ function SwypePaymentInner({
6999
7144
  const load = async () => {
7000
7145
  dispatch({ type: "DATA_LOAD_START" });
7001
7146
  try {
7002
- const token = await getAccessToken();
7147
+ const token = await getAccessTokenRef.current();
7003
7148
  if (!token) throw new Error("Not authenticated");
7004
7149
  const [prov, accts, chn] = await Promise.all([
7005
7150
  fetchProviders(apiBaseUrl, token),
@@ -7053,7 +7198,6 @@ function SwypePaymentInner({
7053
7198
  state.step,
7054
7199
  state.accounts.length,
7055
7200
  apiBaseUrl,
7056
- getAccessToken,
7057
7201
  state.activeCredentialId,
7058
7202
  state.selectedAccountId,
7059
7203
  depositAmount
@@ -7068,7 +7212,7 @@ function SwypePaymentInner({
7068
7212
  clearMobileFlowState();
7069
7213
  dispatch({ type: "TRANSFER_FAILED", transfer: polling.transfer, error: "Transfer failed." });
7070
7214
  }
7071
- }, [polling.transfer, onComplete]);
7215
+ }, [polling.transfer, onComplete, dispatch]);
7072
7216
  react.useEffect(() => {
7073
7217
  if (state.step !== "processing") {
7074
7218
  processingStartedAtRef.current = null;
@@ -7094,7 +7238,7 @@ function SwypePaymentInner({
7094
7238
  }
7095
7239
  const timeoutId = window.setTimeout(handleTimeout, remainingMs);
7096
7240
  return () => window.clearTimeout(timeoutId);
7097
- }, [state.step, polling.transfer, state.transfer, polling.stopPolling, onError]);
7241
+ }, [state.step, polling.transfer, state.transfer, polling.stopPolling, onError, dispatch, processingStartedAtRef]);
7098
7242
  react.useEffect(() => {
7099
7243
  if (!state.mobileFlow) {
7100
7244
  handlingMobileReturnRef.current = false;
@@ -7104,7 +7248,7 @@ function SwypePaymentInner({
7104
7248
  const polledTransfer = polling.transfer;
7105
7249
  if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
7106
7250
  void handleAuthorizedMobileReturn(polledTransfer, mobileSetupFlowRef.current);
7107
- }, [state.mobileFlow, polling.transfer, handleAuthorizedMobileReturn]);
7251
+ }, [state.mobileFlow, polling.transfer, handleAuthorizedMobileReturn, handlingMobileReturnRef, mobileSetupFlowRef]);
7108
7252
  react.useEffect(() => {
7109
7253
  if (!state.mobileFlow || !mobileSetupFlowRef.current) return;
7110
7254
  if (state.step !== "open-wallet") return;
@@ -7175,7 +7319,18 @@ function SwypePaymentInner({
7175
7319
  window.clearInterval(intervalId);
7176
7320
  document.removeEventListener("visibilitychange", handleVisibility);
7177
7321
  };
7178
- }, [state.mobileFlow, state.step, state.activeCredentialId, apiBaseUrl, reloadAccounts]);
7322
+ }, [
7323
+ state.mobileFlow,
7324
+ state.step,
7325
+ state.activeCredentialId,
7326
+ apiBaseUrl,
7327
+ reloadAccounts,
7328
+ dispatch,
7329
+ mobileSetupFlowRef,
7330
+ setupAccountIdRef,
7331
+ reauthSessionIdRef,
7332
+ reauthTokenRef
7333
+ ]);
7179
7334
  react.useEffect(() => {
7180
7335
  if (!state.mobileFlow) return;
7181
7336
  if (handlingMobileReturnRef.current) return;
@@ -7189,7 +7344,14 @@ function SwypePaymentInner({
7189
7344
  };
7190
7345
  document.addEventListener("visibilitychange", handleVisibility);
7191
7346
  return () => document.removeEventListener("visibilitychange", handleVisibility);
7192
- }, [state.mobileFlow, state.transfer?.id, polling.isPolling, polling.startPolling]);
7347
+ }, [
7348
+ state.mobileFlow,
7349
+ state.transfer?.id,
7350
+ polling.isPolling,
7351
+ polling.startPolling,
7352
+ handlingMobileReturnRef,
7353
+ pollingTransferIdRef
7354
+ ]);
7193
7355
  react.useEffect(() => {
7194
7356
  if (!pendingSelectSourceAction) {
7195
7357
  initializedSelectSourceActionRef.current = null;
@@ -7198,20 +7360,6 @@ function SwypePaymentInner({
7198
7360
  return;
7199
7361
  }
7200
7362
  if (initializedSelectSourceActionRef.current === pendingSelectSourceAction.id) return;
7201
- const options = pendingSelectSourceAction.metadata?.options ?? [];
7202
- if (pendingTokenAuthRef.current) {
7203
- const { tokenAddress, chainId } = pendingTokenAuthRef.current;
7204
- const chainIdHex = `0x${chainId.toString(16)}`;
7205
- const match = options.find(
7206
- (opt) => opt.tokenAddress.toLowerCase() === tokenAddress.toLowerCase() && opt.chainId.toLowerCase() === chainIdHex.toLowerCase()
7207
- );
7208
- if (match) {
7209
- setSelectSourceChainName(match.chainName);
7210
- setSelectSourceTokenSymbol(match.tokenSymbol);
7211
- initializedSelectSourceActionRef.current = pendingSelectSourceAction.id;
7212
- return;
7213
- }
7214
- }
7215
7363
  const hasRecommended = !!selectSourceRecommended && selectSourceChoices.some(
7216
7364
  (chain) => chain.chainName === selectSourceRecommended.chainName && chain.tokens.some((t) => t.tokenSymbol === selectSourceRecommended.tokenSymbol)
7217
7365
  );
@@ -7226,7 +7374,14 @@ function SwypePaymentInner({
7226
7374
  setSelectSourceTokenSymbol("USDC");
7227
7375
  }
7228
7376
  initializedSelectSourceActionRef.current = pendingSelectSourceAction.id;
7229
- }, [pendingSelectSourceAction, selectSourceChoices, selectSourceRecommended]);
7377
+ }, [
7378
+ pendingSelectSourceAction,
7379
+ selectSourceChoices,
7380
+ selectSourceRecommended,
7381
+ setSelectSourceChainName,
7382
+ setSelectSourceTokenSymbol,
7383
+ initializedSelectSourceActionRef
7384
+ ]);
7230
7385
  react.useEffect(() => {
7231
7386
  if (pendingSelectSourceAction && (state.step === "processing" || state.step === "open-wallet" || state.step === "setup-status")) {
7232
7387
  const isDesktop = shouldUseWalletConnector({
@@ -7234,24 +7389,8 @@ function SwypePaymentInner({
7234
7389
  userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
7235
7390
  });
7236
7391
  if (isDesktop && state.step === "setup-status") {
7237
- const recommended = pendingSelectSourceAction.metadata?.recommended;
7238
- const options = pendingSelectSourceAction.metadata?.options ?? [];
7239
- let chainName;
7240
- let tokenSymbol;
7241
- if (pendingTokenAuthRef.current) {
7242
- const { tokenAddress, chainId } = pendingTokenAuthRef.current;
7243
- const chainIdHex = `0x${chainId.toString(16)}`;
7244
- const match = options.find(
7245
- (opt) => opt.tokenAddress.toLowerCase() === tokenAddress.toLowerCase() && opt.chainId.toLowerCase() === chainIdHex.toLowerCase()
7246
- );
7247
- chainName = match?.chainName ?? recommended?.chainName ?? "Base";
7248
- tokenSymbol = match?.tokenSymbol ?? recommended?.tokenSymbol ?? "USDC";
7249
- } else {
7250
- const choices = buildSelectSourceChoices(options);
7251
- chainName = recommended?.chainName ?? choices[0]?.chainName ?? "Base";
7252
- tokenSymbol = recommended?.tokenSymbol ?? choices[0]?.tokens[0]?.tokenSymbol ?? "USDC";
7253
- }
7254
- authExecutor.resolveSelectSource({ chainName, tokenSymbol });
7392
+ preSelectSourceStepRef.current = state.step;
7393
+ dispatch({ type: "NAVIGATE", step: "setup" });
7255
7394
  return;
7256
7395
  }
7257
7396
  preSelectSourceStepRef.current = state.step;
@@ -7260,75 +7399,243 @@ function SwypePaymentInner({
7260
7399
  dispatch({ type: "NAVIGATE", step: preSelectSourceStepRef.current ?? "processing" });
7261
7400
  preSelectSourceStepRef.current = null;
7262
7401
  }
7263
- }, [pendingSelectSourceAction, state.step, authExecutor, useWalletConnectorProp]);
7402
+ }, [pendingSelectSourceAction, state.step, useWalletConnectorProp, dispatch, preSelectSourceStepRef, authExecutor]);
7264
7403
  const pendingOneTapSetupAction = authExecutor.pendingOneTapSetup;
7265
7404
  const preOneTapSetupStepRef = react.useRef(null);
7266
7405
  react.useEffect(() => {
7267
7406
  if (pendingOneTapSetupAction && state.step === "setup-status") {
7268
- preOneTapSetupStepRef.current = state.step;
7269
- reloadAccounts().then(() => {
7270
- dispatch({ type: "NAVIGATE", step: "setup" });
7271
- });
7407
+ if (oneTapLimitSavedDuringSetupRef.current) {
7408
+ oneTapLimitSavedDuringSetupRef.current = false;
7409
+ authExecutor.resolveOneTapSetup();
7410
+ } else {
7411
+ preOneTapSetupStepRef.current = state.step;
7412
+ reloadAccounts().then(() => {
7413
+ dispatch({ type: "NAVIGATE", step: "setup" });
7414
+ });
7415
+ }
7272
7416
  } else if (!pendingOneTapSetupAction && state.step === "setup" && preOneTapSetupStepRef.current) {
7273
7417
  dispatch({ type: "NAVIGATE", step: preOneTapSetupStepRef.current });
7274
7418
  preOneTapSetupStepRef.current = null;
7275
7419
  }
7276
- }, [pendingOneTapSetupAction, state.step, reloadAccounts]);
7420
+ }, [pendingOneTapSetupAction, state.step, reloadAccounts, authExecutor, dispatch, oneTapLimitSavedDuringSetupRef]);
7421
+ }
7422
+ function SwypePayment(props) {
7423
+ const resetKey = react.useRef(0);
7424
+ const handleBoundaryReset = react.useCallback(() => {
7425
+ resetKey.current += 1;
7426
+ }, []);
7427
+ return /* @__PURE__ */ jsxRuntime.jsx(PaymentErrorBoundary, { onReset: handleBoundaryReset, children: /* @__PURE__ */ jsxRuntime.jsx(SwypePaymentInner, { ...props }) }, resetKey.current);
7428
+ }
7429
+ function SwypePaymentInner({
7430
+ destination,
7431
+ onComplete,
7432
+ onError,
7433
+ useWalletConnector: useWalletConnectorProp,
7434
+ idempotencyKey,
7435
+ merchantAuthorization,
7436
+ merchantName,
7437
+ onBack,
7438
+ onDismiss,
7439
+ autoCloseSeconds
7440
+ }) {
7441
+ const { apiBaseUrl, depositAmount } = useSwypeConfig();
7442
+ const { ready, authenticated, logout, getAccessToken } = reactAuth.usePrivy();
7443
+ reactAuth.useLoginWithOAuth();
7444
+ const [state, dispatch] = react.useReducer(
7445
+ paymentReducer,
7446
+ {
7447
+ depositAmount,
7448
+ passkeyPopupNeeded: isSafari() && isInCrossOriginIframe(),
7449
+ activeCredentialId: typeof window === "undefined" ? null : window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY)
7450
+ },
7451
+ createInitialState
7452
+ );
7453
+ const authExecutor = useAuthorizationExecutor();
7454
+ const polling = useTransferPolling();
7455
+ const transferSigning = useTransferSigning();
7456
+ const mobileFlowRefs = {
7457
+ mobileSetupFlowRef: react.useRef(false),
7458
+ handlingMobileReturnRef: react.useRef(false),
7459
+ setupAccountIdRef: react.useRef(null),
7460
+ reauthSessionIdRef: react.useRef(null),
7461
+ reauthTokenRef: react.useRef(null),
7462
+ loadingDataRef: react.useRef(false)
7463
+ };
7464
+ const derived = useDerivedState(state);
7465
+ const auth = useAuthHandlers(dispatch, state.verificationTarget);
7466
+ const passkey = usePasskeyHandlers(
7467
+ dispatch,
7468
+ apiBaseUrl,
7469
+ state.accounts,
7470
+ state.knownCredentialIds,
7471
+ mobileFlowRefs.mobileSetupFlowRef
7472
+ );
7473
+ const transfer = useTransferHandlers({
7474
+ dispatch,
7475
+ getAccessToken,
7476
+ apiBaseUrl,
7477
+ depositAmount,
7478
+ destination,
7479
+ idempotencyKey,
7480
+ merchantAuthorization,
7481
+ onComplete,
7482
+ onError,
7483
+ polling,
7484
+ transferSigning,
7485
+ sourceType: derived.sourceType,
7486
+ sourceId: derived.sourceId,
7487
+ sourceTokenAddress: derived.selectedSource?.address,
7488
+ activeCredentialId: state.activeCredentialId,
7489
+ selectedAccountId: state.selectedAccountId,
7490
+ transfer: state.transfer,
7491
+ accounts: state.accounts
7492
+ });
7493
+ const mobileFlow = useMobileFlowHandlers(
7494
+ dispatch,
7495
+ polling,
7496
+ transfer.reloadAccounts,
7497
+ transfer.pollingTransferIdRef,
7498
+ state.transfer,
7499
+ mobileFlowRefs
7500
+ );
7501
+ const sourceSelection = useSourceSelectionHandlers(dispatch, authExecutor);
7502
+ const provider = useProviderHandlers({
7503
+ dispatch,
7504
+ getAccessToken,
7505
+ apiBaseUrl,
7506
+ depositAmount,
7507
+ useWalletConnectorProp,
7508
+ activeCredentialId: state.activeCredentialId,
7509
+ selectedAccountId: state.selectedAccountId,
7510
+ accounts: state.accounts,
7511
+ providers: state.providers,
7512
+ authExecutor,
7513
+ reloadAccounts: transfer.reloadAccounts,
7514
+ onError,
7515
+ mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
7516
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
7517
+ setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
7518
+ reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
7519
+ reauthTokenRef: mobileFlowRefs.reauthTokenRef
7520
+ });
7521
+ const oneTapSetup = useOneTapSetupHandlers({
7522
+ dispatch,
7523
+ getAccessToken,
7524
+ apiBaseUrl,
7525
+ authExecutor,
7526
+ selectSourceChainName: sourceSelection.selectSourceChainName,
7527
+ selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol
7528
+ });
7529
+ const handleNewPayment = react.useCallback(() => {
7530
+ clearMobileFlowState();
7531
+ transfer.processingStartedAtRef.current = null;
7532
+ transfer.pollingTransferIdRef.current = null;
7533
+ sourceSelection.preSelectSourceStepRef.current = null;
7534
+ oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
7535
+ dispatch({
7536
+ type: "NEW_PAYMENT",
7537
+ depositAmount,
7538
+ firstAccountId: state.accounts.length > 0 ? state.accounts[0].id : null
7539
+ });
7540
+ }, [depositAmount, state.accounts, transfer, sourceSelection, provider, oneTapSetup]);
7541
+ const handleLogout = react.useCallback(async () => {
7542
+ try {
7543
+ await logout();
7544
+ } catch {
7545
+ }
7546
+ clearMobileFlowState();
7547
+ if (typeof window !== "undefined") {
7548
+ window.localStorage.removeItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
7549
+ }
7550
+ polling.stopPolling();
7551
+ sourceSelection.preSelectSourceStepRef.current = null;
7552
+ passkey.checkingPasskeyRef.current = false;
7553
+ oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
7554
+ auth.setAuthInput("");
7555
+ auth.setOtpCode("");
7556
+ dispatch({ type: "LOGOUT", depositAmount });
7557
+ }, [logout, polling, depositAmount, auth, sourceSelection, provider, passkey, oneTapSetup]);
7558
+ usePaymentEffects({
7559
+ state,
7560
+ dispatch,
7561
+ ready,
7562
+ authenticated,
7563
+ apiBaseUrl,
7564
+ depositAmount,
7565
+ useWalletConnectorProp,
7566
+ onComplete,
7567
+ onError,
7568
+ polling,
7569
+ authExecutor,
7570
+ reloadAccounts: transfer.reloadAccounts,
7571
+ activeOtpStatus: auth.activeOtpStatus,
7572
+ activeOtpErrorMessage: auth.activeOtpErrorMessage,
7573
+ otpCode: auth.otpCode,
7574
+ handleVerifyLoginCode: auth.handleVerifyLoginCode,
7575
+ setAuthInput: auth.setAuthInput,
7576
+ setOtpCode: auth.setOtpCode,
7577
+ mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
7578
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
7579
+ setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
7580
+ reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
7581
+ reauthTokenRef: mobileFlowRefs.reauthTokenRef,
7582
+ loadingDataRef: mobileFlowRefs.loadingDataRef,
7583
+ pollingTransferIdRef: transfer.pollingTransferIdRef,
7584
+ processingStartedAtRef: transfer.processingStartedAtRef,
7585
+ checkingPasskeyRef: passkey.checkingPasskeyRef,
7586
+ pendingSelectSourceAction: sourceSelection.pendingSelectSourceAction,
7587
+ selectSourceChoices: sourceSelection.selectSourceChoices,
7588
+ selectSourceRecommended: sourceSelection.selectSourceRecommended,
7589
+ setSelectSourceChainName: sourceSelection.setSelectSourceChainName,
7590
+ setSelectSourceTokenSymbol: sourceSelection.setSelectSourceTokenSymbol,
7591
+ initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef,
7592
+ preSelectSourceStepRef: sourceSelection.preSelectSourceStepRef,
7593
+ oneTapLimitSavedDuringSetupRef: oneTapSetup.oneTapLimitSavedDuringSetupRef,
7594
+ handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn
7595
+ });
7277
7596
  const handlers = react.useMemo(() => ({
7278
- onSendLoginCode: handleSendLoginCode,
7279
- onVerifyLoginCode: handleVerifyLoginCode,
7280
- onResendLoginCode: handleResendLoginCode,
7597
+ onSendLoginCode: auth.handleSendLoginCode,
7598
+ onVerifyLoginCode: auth.handleVerifyLoginCode,
7599
+ onResendLoginCode: auth.handleResendLoginCode,
7281
7600
  onBackFromOtp: () => {
7282
- setOtpCode("");
7601
+ auth.setOtpCode("");
7283
7602
  dispatch({ type: "BACK_TO_LOGIN" });
7284
7603
  },
7285
- onRegisterPasskey: handleRegisterPasskey,
7286
- onCreatePasskeyViaPopup: handleCreatePasskeyViaPopup,
7287
- onVerifyPasskeyViaPopup: handleVerifyPasskeyViaPopup,
7288
- onSelectProvider: handleSelectProvider,
7289
- onContinueConnection: handleContinueConnection,
7290
- onSelectAccount: handleSelectAccount,
7291
- onPay: handlePay,
7292
- onIncreaseLimit: handleIncreaseLimit,
7293
- onConfirmSign: handleConfirmSign,
7294
- onRetryMobileStatus: handleRetryMobileStatus,
7604
+ onRegisterPasskey: passkey.handleRegisterPasskey,
7605
+ onCreatePasskeyViaPopup: passkey.handleCreatePasskeyViaPopup,
7606
+ onVerifyPasskeyViaPopup: passkey.handleVerifyPasskeyViaPopup,
7607
+ onSelectProvider: provider.handleSelectProvider,
7608
+ onContinueConnection: provider.handleContinueConnection,
7609
+ onSelectAccount: provider.handleSelectAccount,
7610
+ onPay: transfer.handlePay,
7611
+ onIncreaseLimit: provider.handleIncreaseLimit,
7612
+ onConfirmSign: transfer.handleConfirmSign,
7613
+ onRetryMobileStatus: mobileFlow.handleRetryMobileStatus,
7295
7614
  onLogout: handleLogout,
7296
7615
  onNewPayment: handleNewPayment,
7297
7616
  onNavigate: (step) => dispatch({ type: "NAVIGATE", step }),
7298
- onSetAuthInput: setAuthInput,
7617
+ onSetAuthInput: auth.setAuthInput,
7299
7618
  onSetOtpCode: (code) => {
7300
- setOtpCode(code);
7619
+ auth.setOtpCode(code);
7301
7620
  dispatch({ type: "SET_ERROR", error: null });
7302
7621
  },
7303
- onSelectSourceChainChange: handleSelectSourceChainChange,
7304
- onSetSelectSourceTokenSymbol: setSelectSourceTokenSymbol,
7305
- onConfirmSelectSource: handleConfirmSelectSource,
7306
- onSetupOneTap: handleSetupOneTap,
7307
- onSelectToken: handleNavigateToTokenPicker,
7308
- onSelectAuthorizedToken: handleSelectAuthorizedToken,
7309
- onAuthorizeToken: handleAuthorizeToken
7622
+ onSelectSourceChainChange: sourceSelection.handleSelectSourceChainChange,
7623
+ onSetSelectSourceTokenSymbol: sourceSelection.setSelectSourceTokenSymbol,
7624
+ onConfirmSelectSource: sourceSelection.handleConfirmSelectSource,
7625
+ onSetupOneTap: oneTapSetup.handleSetupOneTap,
7626
+ onSelectToken: provider.handleNavigateToTokenPicker,
7627
+ onSelectAuthorizedToken: provider.handleSelectAuthorizedToken,
7628
+ onAuthorizeToken: provider.handleAuthorizeToken
7310
7629
  }), [
7311
- handleSendLoginCode,
7312
- handleVerifyLoginCode,
7313
- handleResendLoginCode,
7314
- handleRegisterPasskey,
7315
- handleCreatePasskeyViaPopup,
7316
- handleVerifyPasskeyViaPopup,
7317
- handleSelectProvider,
7318
- handleContinueConnection,
7319
- handleSelectAccount,
7320
- handlePay,
7321
- handleIncreaseLimit,
7322
- handleConfirmSign,
7323
- handleRetryMobileStatus,
7630
+ auth,
7631
+ passkey,
7632
+ provider,
7633
+ transfer,
7634
+ mobileFlow,
7635
+ sourceSelection,
7636
+ oneTapSetup,
7324
7637
  handleLogout,
7325
- handleNewPayment,
7326
- handleSelectSourceChainChange,
7327
- handleConfirmSelectSource,
7328
- handleSetupOneTap,
7329
- handleNavigateToTokenPicker,
7330
- handleSelectAuthorizedToken,
7331
- handleAuthorizeToken
7638
+ handleNewPayment
7332
7639
  ]);
7333
7640
  return /* @__PURE__ */ jsxRuntime.jsx(
7334
7641
  StepRenderer,
@@ -7336,28 +7643,29 @@ function SwypePaymentInner({
7336
7643
  state,
7337
7644
  ready,
7338
7645
  authenticated,
7339
- activeOtpStatus,
7646
+ activeOtpStatus: auth.activeOtpStatus,
7340
7647
  pollingTransfer: polling.transfer,
7341
7648
  pollingError: polling.error,
7342
7649
  authExecutorError: authExecutor.error,
7343
7650
  transferSigningSigning: transferSigning.signing,
7344
7651
  transferSigningError: transferSigning.error,
7345
- pendingConnections,
7346
- depositEligibleAccounts,
7347
- sourceName,
7348
- sourceAddress,
7349
- sourceVerified,
7350
- maxSourceBalance,
7351
- tokenCount,
7352
- selectedAccount,
7353
- selectedSource,
7354
- selectSourceChoices,
7355
- selectSourceRecommended,
7356
- authInput,
7357
- otpCode,
7358
- selectSourceChainName,
7359
- selectSourceTokenSymbol,
7360
- savingOneTapLimit,
7652
+ pendingConnections: derived.pendingConnections,
7653
+ depositEligibleAccounts: derived.depositEligibleAccounts,
7654
+ sourceName: derived.sourceName,
7655
+ sourceAddress: derived.sourceAddress,
7656
+ sourceVerified: derived.sourceVerified,
7657
+ maxSourceBalance: derived.maxSourceBalance,
7658
+ tokenCount: derived.tokenCount,
7659
+ selectedAccount: derived.selectedAccount,
7660
+ selectedSource: derived.selectedSource,
7661
+ selectSourceChoices: sourceSelection.selectSourceChoices,
7662
+ selectSourceRecommended: sourceSelection.selectSourceRecommended,
7663
+ selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance,
7664
+ authInput: auth.authInput,
7665
+ otpCode: auth.otpCode,
7666
+ selectSourceChainName: sourceSelection.selectSourceChainName,
7667
+ selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
7668
+ savingOneTapLimit: oneTapSetup.savingOneTapLimit,
7361
7669
  merchantName,
7362
7670
  onBack,
7363
7671
  onDismiss,