@swype-org/react-sdk 0.1.133 → 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);
@@ -4252,8 +4115,7 @@ function DepositScreen({
4252
4115
  onAuthorizeAccount,
4253
4116
  onAddProvider,
4254
4117
  onSelectToken,
4255
- pendingSetup,
4256
- onStartSetup
4118
+ selectedSourceLabel
4257
4119
  }) {
4258
4120
  const { tokens } = useSwypeConfig();
4259
4121
  const amount = initialAmount;
@@ -4261,29 +4123,6 @@ function DepositScreen({
4261
4123
  const exceedsLimit = amount > remainingLimit && !isLowBalance;
4262
4124
  const canDeposit = amount >= MIN_DEPOSIT && !exceedsLimit && !isLowBalance && !processing;
4263
4125
  const headerTitle = merchantName ? `Deposit to ${merchantName}` : "Deposit";
4264
- if (pendingSetup) {
4265
- return /* @__PURE__ */ jsxRuntime.jsxs(
4266
- ScreenLayout,
4267
- {
4268
- footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4269
- /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onStartSetup, children: "Set up One-Tap" }),
4270
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: setupHintStyle(tokens.textSecondary), children: "Choose your source and set your One-Tap limit to deposit instantly." }),
4271
- /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
4272
- ] }),
4273
- children: [
4274
- /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { title: headerTitle, right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
4275
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: setupContentStyle, children: [
4276
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: amountDisplayStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: amountStyle(tokens), children: [
4277
- "$",
4278
- amount.toFixed(2)
4279
- ] }) }),
4280
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: setupDescStyle(tokens.textSecondary), children: "Set up your payment source to deposit with One-Tap \u2014 no approvals needed after setup." })
4281
- ] }),
4282
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle5(tokens), children: error })
4283
- ]
4284
- }
4285
- );
4286
- }
4287
4126
  if (isLowBalance) {
4288
4127
  return /* @__PURE__ */ jsxRuntime.jsxs(
4289
4128
  ScreenLayout,
@@ -4321,7 +4160,7 @@ function DepositScreen({
4321
4160
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "12", fill: "#fff", fontWeight: "700", children: "$" })
4322
4161
  ] }) }),
4323
4162
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4324
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: "Available" }),
4163
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: selectedSourceLabel ?? "Available" }),
4325
4164
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...balanceAmountStyle, color: tokens.warning }, children: [
4326
4165
  "$",
4327
4166
  availableBalance.toFixed(2)
@@ -4396,10 +4235,7 @@ function DepositScreen({
4396
4235
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "12", fill: "#fff", fontWeight: "700", children: "$" })
4397
4236
  ] }) }),
4398
4237
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4399
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceLabelStyle2(tokens.textMuted), children: [
4400
- "Paying from ",
4401
- sourceName
4402
- ] }),
4238
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: selectedSourceLabel ?? `Paying from ${sourceName}` }),
4403
4239
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceAmountStyle, children: [
4404
4240
  "$",
4405
4241
  availableBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
@@ -4546,29 +4382,6 @@ var switchHintStyle = (color) => ({
4546
4382
  var outlineBtnWrapStyle = {
4547
4383
  marginBottom: 8
4548
4384
  };
4549
- var setupContentStyle = {
4550
- flex: 1,
4551
- display: "flex",
4552
- flexDirection: "column",
4553
- alignItems: "center",
4554
- justifyContent: "center",
4555
- textAlign: "center",
4556
- padding: "0 24px"
4557
- };
4558
- var setupDescStyle = (color) => ({
4559
- fontSize: "0.88rem",
4560
- color,
4561
- margin: "8px 0 0",
4562
- lineHeight: 1.6,
4563
- maxWidth: 300
4564
- });
4565
- var setupHintStyle = (color) => ({
4566
- textAlign: "center",
4567
- fontSize: "0.78rem",
4568
- color,
4569
- margin: "12px 0 2px",
4570
- lineHeight: 1.5
4571
- });
4572
4385
  function SuccessScreen({
4573
4386
  amount,
4574
4387
  currency,
@@ -5218,6 +5031,28 @@ var waitHintStyle2 = (color) => ({
5218
5031
  color,
5219
5032
  margin: 0
5220
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
+ }
5221
5056
  function OpenWalletScreen({
5222
5057
  walletName,
5223
5058
  deeplinkUri,
@@ -5663,12 +5498,12 @@ function StepRenderer({
5663
5498
  selectedSource,
5664
5499
  selectSourceChoices,
5665
5500
  selectSourceRecommended,
5501
+ selectSourceAvailableBalance,
5666
5502
  authInput,
5667
5503
  otpCode,
5668
5504
  selectSourceChainName,
5669
5505
  selectSourceTokenSymbol,
5670
5506
  savingOneTapLimit,
5671
- pendingSetup,
5672
5507
  merchantName,
5673
5508
  onBack,
5674
5509
  onDismiss,
@@ -5785,19 +5620,23 @@ function StepRenderer({
5785
5620
  );
5786
5621
  }
5787
5622
  if (step === "setup") {
5788
- const hasPendingSelectSource = selectSourceChoices.length > 0;
5789
- const setupSourceLabel = hasPendingSelectSource && selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : selectedSourceLabel;
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);
5790
5629
  return /* @__PURE__ */ jsxRuntime.jsx(
5791
5630
  SetupScreen,
5792
5631
  {
5793
- availableBalance: selectedSource ? selectedSource.balance.available.amount : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : maxSourceBalance,
5794
- 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,
5795
5634
  sourceName,
5796
5635
  onSetupOneTap: handlers.onSetupOneTap,
5797
5636
  onBack: () => handlers.onNavigate("deposit"),
5798
5637
  onLogout: handlers.onLogout,
5799
- onAdvanced: hasPendingSelectSource ? () => handlers.onNavigate("select-source") : handlers.onSelectToken,
5800
- selectedSourceLabel: setupSourceLabel,
5638
+ onAdvanced: handlers.onSelectToken,
5639
+ selectedSourceLabel: effectiveSourceLabel,
5801
5640
  loading: savingOneTapLimit,
5802
5641
  error: state.error
5803
5642
  }
@@ -5846,8 +5685,7 @@ function StepRenderer({
5846
5685
  onAuthorizeAccount: handlers.onContinueConnection,
5847
5686
  onAddProvider: () => handlers.onNavigate("wallet-picker"),
5848
5687
  onSelectToken: handlers.onSelectToken,
5849
- pendingSetup,
5850
- onStartSetup: () => handlers.onNavigate("setup")
5688
+ selectedSourceLabel
5851
5689
  }
5852
5690
  );
5853
5691
  }
@@ -5879,7 +5717,7 @@ function StepRenderer({
5879
5717
  );
5880
5718
  }
5881
5719
  if (step === "select-source") {
5882
- const enteredFromSetup = state.previousStep === "setup";
5720
+ const cameFromSetup = state.previousStep === "setup";
5883
5721
  return /* @__PURE__ */ jsxRuntime.jsx(
5884
5722
  SelectSourceScreen,
5885
5723
  {
@@ -5889,8 +5727,8 @@ function StepRenderer({
5889
5727
  recommended: selectSourceRecommended,
5890
5728
  onChainChange: handlers.onSelectSourceChainChange,
5891
5729
  onTokenChange: handlers.onSetSelectSourceTokenSymbol,
5892
- onConfirm: enteredFromSetup ? () => handlers.onNavigate("setup") : handlers.onConfirmSelectSource,
5893
- onBack: enteredFromSetup ? () => handlers.onNavigate("setup") : void 0,
5730
+ onConfirm: cameFromSetup ? () => handlers.onNavigate("setup") : handlers.onConfirmSelectSource,
5731
+ onBack: cameFromSetup ? () => handlers.onNavigate("setup") : void 0,
5894
5732
  onLogout: handlers.onLogout
5895
5733
  }
5896
5734
  );
@@ -5940,21 +5778,38 @@ function StepRenderer({
5940
5778
  selectedAccountId: state.selectedAccountId,
5941
5779
  onSelectAccount: handlers.onSelectAccount,
5942
5780
  onAuthorizeAccount: handlers.onContinueConnection,
5943
- onAddProvider: () => handlers.onNavigate("wallet-picker")
5781
+ onAddProvider: () => handlers.onNavigate("wallet-picker"),
5782
+ selectedSourceLabel
5944
5783
  }
5945
5784
  );
5946
5785
  }
5947
5786
  return null;
5948
5787
  }
5949
- var PaymentErrorBoundary = class extends react.Component {
5950
- constructor(props) {
5951
- super(props);
5952
- this.state = { hasError: false };
5953
- }
5954
- static getDerivedStateFromError() {
5955
- return { hasError: true };
5956
- }
5957
- componentDidCatch(error, _info) {
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
+ }
5804
+ var PaymentErrorBoundary = class extends react.Component {
5805
+ constructor(props) {
5806
+ super(props);
5807
+ this.state = { hasError: false };
5808
+ }
5809
+ static getDerivedStateFromError() {
5810
+ return { hasError: true };
5811
+ }
5812
+ componentDidCatch(error, _info) {
5958
5813
  captureException(error);
5959
5814
  }
5960
5815
  handleReset = () => {
@@ -6014,78 +5869,7 @@ var buttonStyle3 = {
6014
5869
  fontFamily: "inherit",
6015
5870
  cursor: "pointer"
6016
5871
  };
6017
- function SwypePayment(props) {
6018
- const resetKey = react.useRef(0);
6019
- const handleBoundaryReset = react.useCallback(() => {
6020
- resetKey.current += 1;
6021
- }, []);
6022
- return /* @__PURE__ */ jsxRuntime.jsx(PaymentErrorBoundary, { onReset: handleBoundaryReset, children: /* @__PURE__ */ jsxRuntime.jsx(SwypePaymentInner, { ...props }) }, resetKey.current);
6023
- }
6024
- function SwypePaymentInner({
6025
- destination,
6026
- onComplete,
6027
- onError,
6028
- useWalletConnector: useWalletConnectorProp,
6029
- idempotencyKey,
6030
- merchantAuthorization,
6031
- merchantName,
6032
- onBack,
6033
- onDismiss,
6034
- autoCloseSeconds
6035
- }) {
6036
- const { apiBaseUrl, depositAmount } = useSwypeConfig();
6037
- const { ready, authenticated, user, logout, getAccessToken } = reactAuth.usePrivy();
6038
- const {
6039
- sendCode: sendEmailCode,
6040
- loginWithCode: loginWithEmailCode,
6041
- state: emailLoginState
6042
- } = reactAuth.useLoginWithEmail();
6043
- const {
6044
- sendCode: sendSmsCode,
6045
- loginWithCode: loginWithSmsCode,
6046
- state: smsLoginState
6047
- } = reactAuth.useLoginWithSms();
6048
- reactAuth.useLoginWithOAuth();
6049
- const [state, dispatch] = react.useReducer(
6050
- paymentReducer,
6051
- {
6052
- depositAmount,
6053
- passkeyPopupNeeded: isSafari() && isInCrossOriginIframe(),
6054
- activeCredentialId: typeof window === "undefined" ? null : window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY)
6055
- },
6056
- createInitialState
6057
- );
6058
- const loadingDataRef = react.useRef(false);
6059
- const pollingTransferIdRef = react.useRef(null);
6060
- const setupAccountIdRef = react.useRef(null);
6061
- const mobileSetupFlowRef = react.useRef(false);
6062
- const handlingMobileReturnRef = react.useRef(false);
6063
- const processingStartedAtRef = react.useRef(null);
6064
- const initializedSelectSourceActionRef = react.useRef(null);
6065
- const preSelectSourceStepRef = react.useRef(null);
6066
- const pendingTokenAuthRef = react.useRef(null);
6067
- const pendingTokenSelectionRef = react.useRef(null);
6068
- const pendingTokenAuthSessionRef = react.useRef(null);
6069
- const reauthSessionIdRef = react.useRef(null);
6070
- const reauthTokenRef = react.useRef(null);
6071
- const checkingPasskeyRef = react.useRef(false);
6072
- const onCompleteRef = react.useRef(onComplete);
6073
- onCompleteRef.current = onComplete;
6074
- const pollingRef = react.useRef(null);
6075
- const getAccessTokenRef = react.useRef(getAccessToken);
6076
- getAccessTokenRef.current = getAccessToken;
6077
- const handleAuthorizedMobileReturnRef = react.useRef(
6078
- null
6079
- );
6080
- const [authInput, setAuthInput] = react.useState("");
6081
- const [otpCode, setOtpCode] = react.useState("");
6082
- const [selectSourceChainName, setSelectSourceChainName] = react.useState("");
6083
- const [selectSourceTokenSymbol, setSelectSourceTokenSymbol] = react.useState("");
6084
- const [savingOneTapLimit, setSavingOneTapLimit] = react.useState(false);
6085
- const authExecutor = useAuthorizationExecutor();
6086
- const polling = useTransferPolling();
6087
- pollingRef.current = polling;
6088
- const transferSigning = useTransferSigning();
5872
+ function useDerivedState(state) {
6089
5873
  const { sourceType, sourceId } = deriveSourceTypeAndId(state);
6090
5874
  const selectedAccount = state.accounts.find((a) => a.id === state.selectedAccountId);
6091
5875
  const selectedWallet = selectedAccount?.wallets.find(
@@ -6135,8 +5919,36 @@ function SwypePaymentInner({
6135
5919
  }
6136
5920
  return count;
6137
5921
  }, [state.accounts]);
6138
- const activeOtpStatus = state.verificationTarget?.kind === "email" ? emailLoginState.status : state.verificationTarget?.kind === "phone" ? smsLoginState.status : "initial";
6139
- 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;
6140
5952
  const handleSendLoginCode = react.useCallback(async () => {
6141
5953
  const normalizedIdentifier = normalizeAuthIdentifier(authInput);
6142
5954
  if (!normalizedIdentifier) {
@@ -6158,9 +5970,9 @@ function SwypePaymentInner({
6158
5970
  error: err instanceof Error ? err.message : "Failed to send verification code"
6159
5971
  });
6160
5972
  }
6161
- }, [authInput, sendEmailCode, sendSmsCode]);
5973
+ }, [authInput, sendEmailCode, sendSmsCode, dispatch]);
6162
5974
  const handleVerifyLoginCode = react.useCallback(async () => {
6163
- if (!state.verificationTarget) return;
5975
+ if (!verificationTarget) return;
6164
5976
  const trimmedCode = otpCode.trim();
6165
5977
  if (!/^\d{6}$/.test(trimmedCode)) {
6166
5978
  dispatch({ type: "SET_ERROR", error: "Enter the 6-digit verification code." });
@@ -6168,7 +5980,7 @@ function SwypePaymentInner({
6168
5980
  }
6169
5981
  dispatch({ type: "SET_ERROR", error: null });
6170
5982
  try {
6171
- if (state.verificationTarget.kind === "email") {
5983
+ if (verificationTarget.kind === "email") {
6172
5984
  await loginWithEmailCode({ code: trimmedCode });
6173
5985
  } else {
6174
5986
  await loginWithSmsCode({ code: trimmedCode });
@@ -6180,15 +5992,15 @@ function SwypePaymentInner({
6180
5992
  error: err instanceof Error ? err.message : "Failed to verify code"
6181
5993
  });
6182
5994
  }
6183
- }, [state.verificationTarget, otpCode, loginWithEmailCode, loginWithSmsCode]);
5995
+ }, [verificationTarget, otpCode, loginWithEmailCode, loginWithSmsCode, dispatch]);
6184
5996
  const handleResendLoginCode = react.useCallback(async () => {
6185
- if (!state.verificationTarget) return;
5997
+ if (!verificationTarget) return;
6186
5998
  dispatch({ type: "SET_ERROR", error: null });
6187
5999
  try {
6188
- if (state.verificationTarget.kind === "email") {
6189
- await sendEmailCode({ email: state.verificationTarget.value });
6000
+ if (verificationTarget.kind === "email") {
6001
+ await sendEmailCode({ email: verificationTarget.value });
6190
6002
  } else {
6191
- await sendSmsCode({ phoneNumber: state.verificationTarget.value });
6003
+ await sendSmsCode({ phoneNumber: verificationTarget.value });
6192
6004
  }
6193
6005
  } catch (err) {
6194
6006
  captureException(err);
@@ -6197,7 +6009,68 @@ function SwypePaymentInner({
6197
6009
  error: err instanceof Error ? err.message : "Failed to resend code"
6198
6010
  });
6199
6011
  }
6200
- }, [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);
6201
6074
  const completePasskeyRegistration = react.useCallback(async (credentialId, publicKey) => {
6202
6075
  const token = await getAccessToken();
6203
6076
  if (!token) throw new Error("Not authenticated");
@@ -6206,14 +6079,14 @@ function SwypePaymentInner({
6206
6079
  window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
6207
6080
  const resolved = resolvePostAuthStep({
6208
6081
  hasPasskey: true,
6209
- accounts: state.accounts,
6082
+ accounts,
6210
6083
  persistedMobileFlow: loadMobileFlowState(),
6211
6084
  mobileSetupInProgress: mobileSetupFlowRef.current,
6212
6085
  connectingNewAccount: false
6213
6086
  });
6214
6087
  if (resolved.clearPersistedFlow) clearMobileFlowState();
6215
6088
  dispatch({ type: "NAVIGATE", step: resolved.step });
6216
- }, [getAccessToken, apiBaseUrl, state.accounts]);
6089
+ }, [getAccessToken, apiBaseUrl, accounts, mobileSetupFlowRef, dispatch]);
6217
6090
  const handleRegisterPasskey = react.useCallback(async () => {
6218
6091
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
6219
6092
  dispatch({ type: "SET_ERROR", error: null });
@@ -6237,7 +6110,7 @@ function SwypePaymentInner({
6237
6110
  } finally {
6238
6111
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
6239
6112
  }
6240
- }, [user, completePasskeyRegistration]);
6113
+ }, [user, completePasskeyRegistration, dispatch]);
6241
6114
  const handleCreatePasskeyViaPopup = react.useCallback(async () => {
6242
6115
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
6243
6116
  dispatch({ type: "SET_ERROR", error: null });
@@ -6255,7 +6128,7 @@ function SwypePaymentInner({
6255
6128
  localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
6256
6129
  const resolved = resolvePostAuthStep({
6257
6130
  hasPasskey: true,
6258
- accounts: state.accounts,
6131
+ accounts,
6259
6132
  persistedMobileFlow: loadMobileFlowState(),
6260
6133
  mobileSetupInProgress: mobileSetupFlowRef.current,
6261
6134
  connectingNewAccount: false
@@ -6271,14 +6144,14 @@ function SwypePaymentInner({
6271
6144
  } finally {
6272
6145
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
6273
6146
  }
6274
- }, [user, getAccessToken, apiBaseUrl, state.accounts]);
6147
+ }, [user, getAccessToken, apiBaseUrl, accounts, mobileSetupFlowRef, dispatch]);
6275
6148
  const handleVerifyPasskeyViaPopup = react.useCallback(async () => {
6276
6149
  dispatch({ type: "SET_VERIFYING_PASSKEY", value: true });
6277
6150
  dispatch({ type: "SET_ERROR", error: null });
6278
6151
  try {
6279
6152
  const token = await getAccessToken();
6280
6153
  const matched = await findDevicePasskeyViaPopup({
6281
- credentialIds: state.knownCredentialIds,
6154
+ credentialIds: knownCredentialIds,
6282
6155
  rpId: resolvePasskeyRpId(),
6283
6156
  authToken: token ?? void 0,
6284
6157
  apiBaseUrl
@@ -6292,7 +6165,7 @@ function SwypePaymentInner({
6292
6165
  }
6293
6166
  const resolved = resolvePostAuthStep({
6294
6167
  hasPasskey: true,
6295
- accounts: state.accounts,
6168
+ accounts,
6296
6169
  persistedMobileFlow: loadMobileFlowState(),
6297
6170
  mobileSetupInProgress: mobileSetupFlowRef.current,
6298
6171
  connectingNewAccount: false
@@ -6314,44 +6187,48 @@ function SwypePaymentInner({
6314
6187
  } finally {
6315
6188
  dispatch({ type: "SET_VERIFYING_PASSKEY", value: false });
6316
6189
  }
6317
- }, [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);
6318
6221
  const reloadAccounts = react.useCallback(async () => {
6319
6222
  const token = await getAccessToken();
6320
- if (!token || !state.activeCredentialId) return;
6223
+ if (!token || !activeCredentialId) return;
6321
6224
  const [accts, prov] = await Promise.all([
6322
- fetchAccounts(apiBaseUrl, token, state.activeCredentialId),
6225
+ fetchAccounts(apiBaseUrl, token, activeCredentialId),
6323
6226
  fetchProviders(apiBaseUrl, token)
6324
6227
  ]);
6325
6228
  const parsedAmt = depositAmount != null ? depositAmount : 0;
6326
- const defaults = resolveDepositSelection(accts, parsedAmt, state.selectedAccountId);
6229
+ const defaults = resolveDepositSelection(accts, parsedAmt, selectedAccountId);
6327
6230
  dispatch({ type: "ACCOUNTS_RELOADED", accounts: accts, providers: prov, defaults });
6328
- }, [getAccessToken, state.activeCredentialId, state.selectedAccountId, apiBaseUrl, depositAmount]);
6329
- const handleAuthorizedMobileReturn = react.useCallback(async (authorizedTransfer, isSetup) => {
6330
- if (handlingMobileReturnRef.current) return;
6331
- handlingMobileReturnRef.current = true;
6332
- polling.stopPolling();
6333
- if (isSetup) {
6334
- mobileSetupFlowRef.current = false;
6335
- clearMobileFlowState();
6336
- try {
6337
- await reloadAccounts();
6338
- loadingDataRef.current = false;
6339
- dispatch({ type: "MOBILE_SETUP_COMPLETE", transfer: authorizedTransfer });
6340
- } catch (err) {
6341
- handlingMobileReturnRef.current = false;
6342
- dispatch({
6343
- type: "SET_ERROR",
6344
- error: err instanceof Error ? err.message : "Wallet authorized, but we could not refresh your account yet."
6345
- });
6346
- dispatch({ type: "NAVIGATE", step: "open-wallet" });
6347
- }
6348
- return;
6349
- }
6350
- mobileSetupFlowRef.current = false;
6351
- clearMobileFlowState();
6352
- dispatch({ type: "MOBILE_SIGN_READY", transfer: authorizedTransfer });
6353
- }, [polling.stopPolling, reloadAccounts]);
6354
- handleAuthorizedMobileReturnRef.current = handleAuthorizedMobileReturn;
6231
+ }, [getAccessToken, activeCredentialId, selectedAccountId, apiBaseUrl, depositAmount, dispatch]);
6355
6232
  const handlePay = react.useCallback(async (payAmount, sourceOverrides) => {
6356
6233
  if (isNaN(payAmount) || payAmount < MIN_SEND_AMOUNT_USD) {
6357
6234
  dispatch({ type: "SET_ERROR", error: `Minimum amount is $${MIN_SEND_AMOUNT_USD.toFixed(2)}.` });
@@ -6361,7 +6238,7 @@ function SwypePaymentInner({
6361
6238
  dispatch({ type: "SET_ERROR", error: "No account or provider selected." });
6362
6239
  return;
6363
6240
  }
6364
- if (!state.activeCredentialId) {
6241
+ if (!activeCredentialId) {
6365
6242
  dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6366
6243
  dispatch({ type: "NAVIGATE", step: "create-passkey" });
6367
6244
  return;
@@ -6369,10 +6246,10 @@ function SwypePaymentInner({
6369
6246
  dispatch({ type: "PAY_STARTED", isSetupRedirect: false });
6370
6247
  processingStartedAtRef.current = Date.now();
6371
6248
  try {
6372
- if (state.transfer?.status === "AUTHORIZED") {
6373
- const signedTransfer2 = await transferSigning.signTransfer(state.transfer.id);
6249
+ if (transfer?.status === "AUTHORIZED") {
6250
+ const signedTransfer2 = await transferSigning.signTransfer(transfer.id);
6374
6251
  dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer2 });
6375
- polling.startPolling(state.transfer.id);
6252
+ polling.startPolling(transfer.id);
6376
6253
  return;
6377
6254
  }
6378
6255
  const token = await getAccessToken();
@@ -6380,7 +6257,7 @@ function SwypePaymentInner({
6380
6257
  let effectiveSourceType = sourceOverrides?.sourceType ?? sourceType;
6381
6258
  let effectiveSourceId = sourceOverrides?.sourceId ?? sourceId;
6382
6259
  if (effectiveSourceType === "accountId") {
6383
- const acct = state.accounts.find((a) => a.id === effectiveSourceId);
6260
+ const acct = accounts.find((a) => a.id === effectiveSourceId);
6384
6261
  const preferredWallet = acct ? getPreferredDepositWallet(acct, payAmount) : null;
6385
6262
  if (preferredWallet?.status === "ACTIVE") {
6386
6263
  effectiveSourceType = "walletId";
@@ -6389,10 +6266,11 @@ function SwypePaymentInner({
6389
6266
  }
6390
6267
  const t = await createTransfer(apiBaseUrl, token, {
6391
6268
  id: idempotencyKey,
6392
- credentialId: state.activeCredentialId,
6269
+ credentialId: activeCredentialId,
6393
6270
  merchantAuthorization,
6394
6271
  sourceType: effectiveSourceType,
6395
6272
  sourceId: effectiveSourceId,
6273
+ sourceTokenAddress,
6396
6274
  destination,
6397
6275
  amount: payAmount
6398
6276
  });
@@ -6412,11 +6290,7 @@ function SwypePaymentInner({
6412
6290
  } catch (err) {
6413
6291
  captureException(err);
6414
6292
  const msg = err instanceof Error ? err.message : "Transfer failed";
6415
- dispatch({
6416
- type: "PAY_ERROR",
6417
- error: msg,
6418
- fallbackStep: "deposit"
6419
- });
6293
+ dispatch({ type: "PAY_ERROR", error: msg, fallbackStep: "deposit" });
6420
6294
  onError?.(msg);
6421
6295
  } finally {
6422
6296
  dispatch({ type: "PAY_ENDED" });
@@ -6424,9 +6298,10 @@ function SwypePaymentInner({
6424
6298
  }, [
6425
6299
  sourceId,
6426
6300
  sourceType,
6427
- state.activeCredentialId,
6428
- state.transfer,
6429
- state.accounts,
6301
+ sourceTokenAddress,
6302
+ activeCredentialId,
6303
+ transfer,
6304
+ accounts,
6430
6305
  destination,
6431
6306
  apiBaseUrl,
6432
6307
  getAccessToken,
@@ -6435,185 +6310,133 @@ function SwypePaymentInner({
6435
6310
  onError,
6436
6311
  onComplete,
6437
6312
  idempotencyKey,
6438
- merchantAuthorization
6313
+ merchantAuthorization,
6314
+ dispatch
6439
6315
  ]);
6440
- const handleIncreaseLimit = react.useCallback(async () => {
6441
- if (!state.selectedAccountId) {
6442
- dispatch({ type: "SET_ERROR", error: "No account selected." });
6443
- return;
6444
- }
6445
- if (!state.activeCredentialId) {
6446
- dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6447
- dispatch({ type: "NAVIGATE", step: "create-passkey" });
6448
- return;
6449
- }
6450
- const acct = state.accounts.find((a) => a.id === state.selectedAccountId);
6451
- const matchedProvider = acct ? state.providers.find((p) => p.name === acct.name) : void 0;
6452
- if (matchedProvider) {
6453
- dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
6454
- }
6455
- dispatch({ type: "SET_ERROR", error: null });
6456
- dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6316
+ const handleConfirmSign = react.useCallback(async () => {
6317
+ const t = transfer ?? polling.transfer;
6318
+ if (!t) return;
6457
6319
  try {
6458
- const token = await getAccessToken();
6459
- if (!token) throw new Error("Not authenticated");
6460
- const session = await createAccountAuthorizationSession(
6461
- apiBaseUrl,
6462
- token,
6463
- state.selectedAccountId,
6464
- state.activeCredentialId
6465
- );
6466
- const isMobile = !shouldUseWalletConnector({
6467
- useWalletConnector: useWalletConnectorProp,
6468
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6469
- });
6470
- if (isMobile) {
6471
- handlingMobileReturnRef.current = false;
6472
- mobileSetupFlowRef.current = true;
6473
- setupAccountIdRef.current = state.selectedAccountId;
6474
- reauthSessionIdRef.current = session.id;
6475
- reauthTokenRef.current = null;
6476
- persistMobileFlowState({
6477
- accountId: state.selectedAccountId,
6478
- sessionId: session.id,
6479
- deeplinkUri: session.uri,
6480
- providerId: matchedProvider?.id ?? null,
6481
- isSetup: true,
6482
- isReauthorization: true
6483
- });
6484
- dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6485
- triggerDeeplink(session.uri);
6486
- } else {
6487
- await authExecutor.executeSessionById(session.id);
6488
- await reloadAccounts();
6489
- dispatch({ type: "NAVIGATE", step: "deposit" });
6490
- }
6320
+ const signedTransfer = await transferSigning.signTransfer(t.id);
6321
+ clearMobileFlowState();
6322
+ dispatch({ type: "CONFIRM_SIGN_SUCCESS", transfer: signedTransfer });
6323
+ polling.startPolling(t.id);
6491
6324
  } catch (err) {
6492
6325
  captureException(err);
6493
- const msg = err instanceof Error ? err.message : "Failed to increase limit";
6326
+ const msg = err instanceof Error ? err.message : "Failed to sign transfer";
6494
6327
  dispatch({ type: "SET_ERROR", error: msg });
6495
6328
  onError?.(msg);
6496
- } finally {
6497
- dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6498
6329
  }
6499
- }, [
6500
- state.selectedAccountId,
6501
- state.activeCredentialId,
6502
- state.accounts,
6503
- state.providers,
6504
- apiBaseUrl,
6505
- getAccessToken,
6506
- authExecutor,
6507
- useWalletConnectorProp,
6330
+ }, [transfer, polling.transfer, polling.startPolling, transferSigning, onError, dispatch]);
6331
+ return {
6508
6332
  reloadAccounts,
6509
- onError
6510
- ]);
6511
- const handleNavigateToTokenPicker = react.useCallback(() => {
6512
- dispatch({ type: "NAVIGATE", step: "token-picker" });
6513
- }, []);
6514
- const handleSelectAuthorizedToken = react.useCallback((walletId, tokenSymbol) => {
6515
- dispatch({ type: "SELECT_TOKEN", walletId, tokenSymbol });
6516
- }, []);
6517
- const handleAuthorizeToken = react.useCallback(async (_walletId, tokenAddress, chainId, tokenSymbol) => {
6518
- if (!state.selectedAccountId) {
6519
- dispatch({ type: "SET_ERROR", error: "No account selected." });
6520
- return;
6521
- }
6522
- if (!state.activeCredentialId) {
6523
- dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6524
- dispatch({ type: "NAVIGATE", step: "create-passkey" });
6525
- return;
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);
6526
6363
  }
6527
- const acct = state.accounts.find((a) => a.id === state.selectedAccountId);
6528
- const matchedProvider = acct ? state.providers.find((p) => p.name === acct.name) : void 0;
6529
- if (matchedProvider) {
6530
- dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
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;
6531
6368
  }
6532
- dispatch({ type: "SET_ERROR", error: null });
6533
- dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6534
- pendingTokenAuthRef.current = { tokenAddress, chainId, tokenSymbol, walletId: _walletId };
6535
- try {
6536
- const token = await getAccessToken();
6537
- if (!token) throw new Error("Not authenticated");
6538
- const session = await createAccountAuthorizationSession(
6539
- apiBaseUrl,
6540
- token,
6541
- state.selectedAccountId,
6542
- state.activeCredentialId,
6543
- { tokenAddress, chainId }
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
6544
6380
  );
6545
- const isMobile = !shouldUseWalletConnector({
6546
- useWalletConnector: useWalletConnectorProp,
6547
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6548
- });
6549
- if (isMobile) {
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) {
6550
6423
  handlingMobileReturnRef.current = false;
6551
- mobileSetupFlowRef.current = true;
6552
- setupAccountIdRef.current = state.selectedAccountId;
6553
- reauthSessionIdRef.current = session.id;
6554
- reauthTokenRef.current = { walletId: _walletId, tokenSymbol };
6555
- persistMobileFlowState({
6556
- accountId: state.selectedAccountId,
6557
- sessionId: session.id,
6558
- deeplinkUri: session.uri,
6559
- providerId: matchedProvider?.id ?? null,
6560
- isSetup: true,
6561
- isReauthorization: true,
6562
- reauthorizationToken: { walletId: _walletId, tokenSymbol }
6424
+ dispatch({
6425
+ type: "SET_ERROR",
6426
+ error: err instanceof Error ? err.message : "Wallet authorized, but we could not refresh your account yet."
6563
6427
  });
6564
- dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6565
- triggerDeeplink(session.uri);
6566
- } else {
6567
- pendingTokenAuthSessionRef.current = {
6568
- sessionId: session.id,
6569
- walletId: _walletId,
6570
- tokenSymbol,
6571
- tokenAddress,
6572
- chainId
6573
- };
6574
- dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
6575
- dispatch({ type: "NAVIGATE", step: "setup" });
6428
+ dispatch({ type: "NAVIGATE", step: "open-wallet" });
6576
6429
  }
6577
- } catch (err) {
6578
- captureException(err);
6579
- const msg = err instanceof Error ? err.message : "Failed to authorize token";
6580
- dispatch({ type: "SET_ERROR", error: msg });
6581
- onError?.(msg);
6582
- } finally {
6583
- pendingTokenAuthRef.current = null;
6584
- dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6585
- }
6586
- }, [
6587
- state.selectedAccountId,
6588
- state.activeCredentialId,
6589
- state.accounts,
6590
- state.providers,
6591
- apiBaseUrl,
6592
- getAccessToken,
6593
- authExecutor,
6594
- useWalletConnectorProp,
6595
- reloadAccounts,
6596
- onError
6597
- ]);
6598
- const handleConfirmSign = react.useCallback(async () => {
6599
- const t = state.transfer ?? polling.transfer;
6600
- if (!t) return;
6601
- try {
6602
- const signedTransfer = await transferSigning.signTransfer(t.id);
6603
- clearMobileFlowState();
6604
- dispatch({ type: "CONFIRM_SIGN_SUCCESS", transfer: signedTransfer });
6605
- polling.startPolling(t.id);
6606
- } catch (err) {
6607
- captureException(err);
6608
- const msg = err instanceof Error ? err.message : "Failed to sign transfer";
6609
- dispatch({ type: "SET_ERROR", error: msg });
6610
- onError?.(msg);
6430
+ return;
6611
6431
  }
6612
- }, [state.transfer, polling.transfer, polling.startPolling, transferSigning, onError]);
6432
+ mobileSetupFlowRef.current = false;
6433
+ clearMobileFlowState();
6434
+ dispatch({ type: "MOBILE_SIGN_READY", transfer: authorizedTransfer });
6435
+ }, [polling.stopPolling, reloadAccounts, dispatch]);
6613
6436
  const handleRetryMobileStatus = react.useCallback(() => {
6614
6437
  dispatch({ type: "SET_ERROR", error: null });
6615
6438
  handlingMobileReturnRef.current = false;
6616
- const currentTransfer = polling.transfer ?? state.transfer;
6439
+ const currentTransfer = polling.transfer ?? stateTransfer;
6617
6440
  if (currentTransfer?.status === "AUTHORIZED") {
6618
6441
  void handleAuthorizedMobileReturn(currentTransfer, mobileSetupFlowRef.current);
6619
6442
  return;
@@ -6622,15 +6445,54 @@ function SwypePaymentInner({
6622
6445
  if (transferIdToResume) {
6623
6446
  polling.startPolling(transferIdToResume);
6624
6447
  }
6625
- }, [handleAuthorizedMobileReturn, polling, state.transfer]);
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;
6626
6488
  const handleSelectProvider = react.useCallback(async (providerId) => {
6627
6489
  dispatch({ type: "SELECT_PROVIDER", providerId });
6628
- if (!state.activeCredentialId) {
6490
+ if (!activeCredentialId) {
6629
6491
  dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6630
6492
  dispatch({ type: "NAVIGATE", step: "create-passkey" });
6631
6493
  return;
6632
6494
  }
6633
- const provider = state.providers.find((p) => p.id === providerId);
6495
+ const provider = providers.find((p) => p.id === providerId);
6634
6496
  const providerName = provider?.name ?? "Wallet";
6635
6497
  const isMobile = !shouldUseWalletConnector({
6636
6498
  useWalletConnector: useWalletConnectorProp,
@@ -6649,7 +6511,7 @@ function SwypePaymentInner({
6649
6511
  const account = await createAccount(apiBaseUrl, token, {
6650
6512
  id: accountId,
6651
6513
  name: providerName,
6652
- credentialId: state.activeCredentialId,
6514
+ credentialId: activeCredentialId,
6653
6515
  providerId
6654
6516
  });
6655
6517
  const session = account.authorizationSessions?.[0];
@@ -6681,29 +6543,33 @@ function SwypePaymentInner({
6681
6543
  dispatch({ type: "PAY_ENDED" });
6682
6544
  }
6683
6545
  }, [
6684
- state.activeCredentialId,
6685
- state.providers,
6546
+ activeCredentialId,
6547
+ providers,
6686
6548
  apiBaseUrl,
6687
6549
  getAccessToken,
6688
6550
  authExecutor,
6689
6551
  useWalletConnectorProp,
6690
6552
  reloadAccounts,
6691
- onError
6553
+ onError,
6554
+ dispatch,
6555
+ mobileSetupFlowRef,
6556
+ handlingMobileReturnRef,
6557
+ setupAccountIdRef
6692
6558
  ]);
6693
6559
  const handleContinueConnection = react.useCallback(
6694
6560
  (accountId) => {
6695
- const acct = state.accounts.find((a) => a.id === accountId);
6561
+ const acct = accounts.find((a) => a.id === accountId);
6696
6562
  if (!acct) return;
6697
- const matchedProvider = state.providers.find((p) => p.name === acct.name);
6563
+ const matchedProvider = providers.find((p) => p.name === acct.name);
6698
6564
  if (matchedProvider) {
6699
6565
  handleSelectProvider(matchedProvider.id);
6700
6566
  }
6701
6567
  },
6702
- [state.accounts, state.providers, handleSelectProvider]
6568
+ [accounts, providers, handleSelectProvider]
6703
6569
  );
6704
6570
  const handleSelectAccount = react.useCallback(
6705
6571
  (accountId) => {
6706
- const acct = state.accounts.find((a) => a.id === accountId);
6572
+ const acct = accounts.find((a) => a.id === accountId);
6707
6573
  if (!acct) return;
6708
6574
  const activeWallet = getPreferredDepositWallet(acct, depositAmount ?? 0);
6709
6575
  dispatch({
@@ -6712,8 +6578,197 @@ function SwypePaymentInner({
6712
6578
  walletId: activeWallet?.id ?? null
6713
6579
  });
6714
6580
  },
6715
- [state.accounts, depositAmount]
6581
+ [accounts, depositAmount, dispatch]
6716
6582
  );
6583
+ const handleIncreaseLimit = react.useCallback(async () => {
6584
+ if (!selectedAccountId) {
6585
+ dispatch({ type: "SET_ERROR", error: "No account selected." });
6586
+ return;
6587
+ }
6588
+ if (!activeCredentialId) {
6589
+ dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6590
+ dispatch({ type: "NAVIGATE", step: "create-passkey" });
6591
+ return;
6592
+ }
6593
+ const acct = accounts.find((a) => a.id === selectedAccountId);
6594
+ const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
6595
+ if (matchedProvider) {
6596
+ dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
6597
+ }
6598
+ dispatch({ type: "SET_ERROR", error: null });
6599
+ dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6600
+ try {
6601
+ const token = await getAccessToken();
6602
+ if (!token) throw new Error("Not authenticated");
6603
+ const session = await createAccountAuthorizationSession(
6604
+ apiBaseUrl,
6605
+ token,
6606
+ selectedAccountId,
6607
+ activeCredentialId
6608
+ );
6609
+ const isMobile = !shouldUseWalletConnector({
6610
+ useWalletConnector: useWalletConnectorProp,
6611
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6612
+ });
6613
+ if (isMobile) {
6614
+ handlingMobileReturnRef.current = false;
6615
+ mobileSetupFlowRef.current = true;
6616
+ setupAccountIdRef.current = selectedAccountId;
6617
+ reauthSessionIdRef.current = session.id;
6618
+ reauthTokenRef.current = null;
6619
+ persistMobileFlowState({
6620
+ accountId: selectedAccountId,
6621
+ sessionId: session.id,
6622
+ deeplinkUri: session.uri,
6623
+ providerId: matchedProvider?.id ?? null,
6624
+ isSetup: true,
6625
+ isReauthorization: true
6626
+ });
6627
+ dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6628
+ triggerDeeplink(session.uri);
6629
+ } else {
6630
+ dispatch({ type: "NAVIGATE", step: "setup-status" });
6631
+ await authExecutor.executeSessionById(session.id);
6632
+ await reloadAccounts();
6633
+ dispatch({ type: "NAVIGATE", step: "deposit" });
6634
+ }
6635
+ } catch (err) {
6636
+ captureException(err);
6637
+ const msg = err instanceof Error ? err.message : "Failed to increase limit";
6638
+ dispatch({ type: "SET_ERROR", error: msg });
6639
+ onError?.(msg);
6640
+ } finally {
6641
+ dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6642
+ }
6643
+ }, [
6644
+ selectedAccountId,
6645
+ activeCredentialId,
6646
+ accounts,
6647
+ providers,
6648
+ apiBaseUrl,
6649
+ getAccessToken,
6650
+ authExecutor,
6651
+ useWalletConnectorProp,
6652
+ reloadAccounts,
6653
+ onError,
6654
+ dispatch,
6655
+ mobileSetupFlowRef,
6656
+ handlingMobileReturnRef,
6657
+ setupAccountIdRef,
6658
+ reauthSessionIdRef,
6659
+ reauthTokenRef
6660
+ ]);
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" });
6666
+ }
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." });
6674
+ return;
6675
+ }
6676
+ if (!activeCredentialId) {
6677
+ dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6678
+ dispatch({ type: "NAVIGATE", step: "create-passkey" });
6679
+ return;
6680
+ }
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 });
6685
+ }
6686
+ dispatch({ type: "SET_ERROR", error: null });
6687
+ dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6688
+ try {
6689
+ const token = await getAccessToken();
6690
+ if (!token) throw new Error("Not authenticated");
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
6701
+ });
6702
+ if (isMobile) {
6703
+ handlingMobileReturnRef.current = false;
6704
+ mobileSetupFlowRef.current = true;
6705
+ setupAccountIdRef.current = selectedAccountId;
6706
+ reauthSessionIdRef.current = session.id;
6707
+ reauthTokenRef.current = { walletId: _walletId, tokenSymbol };
6708
+ persistMobileFlowState({
6709
+ accountId: selectedAccountId,
6710
+ sessionId: session.id,
6711
+ deeplinkUri: session.uri,
6712
+ providerId: matchedProvider?.id ?? null,
6713
+ isSetup: true,
6714
+ isReauthorization: true,
6715
+ reauthorizationToken: { walletId: _walletId, tokenSymbol }
6716
+ });
6717
+ dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6718
+ triggerDeeplink(session.uri);
6719
+ } else {
6720
+ dispatch({ type: "NAVIGATE", step: "setup-status" });
6721
+ await authExecutor.executeSessionById(session.id);
6722
+ await reloadAccounts();
6723
+ dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
6724
+ }
6725
+ } catch (err) {
6726
+ captureException(err);
6727
+ const msg = err instanceof Error ? err.message : "Failed to authorize token";
6728
+ dispatch({ type: "SET_ERROR", error: msg });
6729
+ onError?.(msg);
6730
+ } finally {
6731
+ dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6732
+ }
6733
+ }, [
6734
+ selectedAccountId,
6735
+ activeCredentialId,
6736
+ accounts,
6737
+ providers,
6738
+ apiBaseUrl,
6739
+ getAccessToken,
6740
+ authExecutor,
6741
+ useWalletConnectorProp,
6742
+ reloadAccounts,
6743
+ onError,
6744
+ dispatch,
6745
+ mobileSetupFlowRef,
6746
+ handlingMobileReturnRef,
6747
+ setupAccountIdRef,
6748
+ reauthSessionIdRef,
6749
+ reauthTokenRef
6750
+ ]);
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);
6717
6772
  const handleSetupOneTap = react.useCallback(async (limit) => {
6718
6773
  setSavingOneTapLimit(true);
6719
6774
  try {
@@ -6721,39 +6776,24 @@ function SwypePaymentInner({
6721
6776
  if (!token) throw new Error("Not authenticated");
6722
6777
  await updateUserConfig(apiBaseUrl, token, { defaultAllowance: limit });
6723
6778
  if (authExecutor.pendingSelectSource) {
6724
- authExecutor.resolveSelectSource({
6725
- chainName: selectSourceChainName,
6726
- tokenSymbol: selectSourceTokenSymbol
6727
- });
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";
6791
+ }
6792
+ oneTapLimitSavedDuringSetupRef.current = true;
6793
+ authExecutor.resolveSelectSource({ chainName, tokenSymbol });
6728
6794
  dispatch({ type: "NAVIGATE", step: "setup-status" });
6729
6795
  } else if (authExecutor.pendingOneTapSetup) {
6730
6796
  authExecutor.resolveOneTapSetup();
6731
- } else if (pendingTokenAuthSessionRef.current) {
6732
- const pending = pendingTokenAuthSessionRef.current;
6733
- pendingTokenAuthSessionRef.current = null;
6734
- pendingTokenAuthRef.current = {
6735
- tokenAddress: pending.tokenAddress,
6736
- chainId: pending.chainId,
6737
- tokenSymbol: pending.tokenSymbol,
6738
- walletId: pending.walletId
6739
- };
6740
- dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6741
- dispatch({ type: "NAVIGATE", step: "setup-status" });
6742
- try {
6743
- await authExecutor.executeSessionById(pending.sessionId);
6744
- await reloadAccounts();
6745
- dispatch({ type: "SELECT_TOKEN", walletId: pending.walletId, tokenSymbol: pending.tokenSymbol });
6746
- } catch (authErr) {
6747
- captureException(authErr);
6748
- dispatch({
6749
- type: "SET_ERROR",
6750
- error: authErr instanceof Error ? authErr.message : "Failed to authorize token"
6751
- });
6752
- dispatch({ type: "NAVIGATE", step: "deposit" });
6753
- } finally {
6754
- pendingTokenAuthRef.current = null;
6755
- dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6756
- }
6757
6797
  } else {
6758
6798
  dispatch({ type: "NAVIGATE", step: "deposit" });
6759
6799
  }
@@ -6766,76 +6806,118 @@ function SwypePaymentInner({
6766
6806
  } finally {
6767
6807
  setSavingOneTapLimit(false);
6768
6808
  }
6769
- }, [getAccessToken, apiBaseUrl, authExecutor, reloadAccounts, onError, selectSourceChainName, selectSourceTokenSymbol]);
6770
- const handleNewPayment = react.useCallback(() => {
6771
- clearMobileFlowState();
6772
- processingStartedAtRef.current = null;
6773
- pollingTransferIdRef.current = null;
6774
- preSelectSourceStepRef.current = null;
6775
- pendingTokenSelectionRef.current = null;
6776
- pendingTokenAuthSessionRef.current = null;
6777
- dispatch({
6778
- type: "NEW_PAYMENT",
6779
- depositAmount,
6780
- firstAccountId: state.accounts.length > 0 ? state.accounts[0].id : null
6781
- });
6782
- }, [depositAmount, state.accounts]);
6783
- const handleLogout = react.useCallback(async () => {
6784
- try {
6785
- await logout();
6786
- } catch {
6787
- }
6788
- clearMobileFlowState();
6789
- if (typeof window !== "undefined") {
6790
- window.localStorage.removeItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
6791
- }
6792
- polling.stopPolling();
6793
- preSelectSourceStepRef.current = null;
6794
- pendingTokenSelectionRef.current = null;
6795
- pendingTokenAuthSessionRef.current = null;
6796
- checkingPasskeyRef.current = false;
6797
- setAuthInput("");
6798
- setOtpCode("");
6799
- dispatch({ type: "LOGOUT", depositAmount });
6800
- }, [logout, polling, depositAmount]);
6801
- const pendingSelectSourceAction = authExecutor.pendingSelectSource;
6802
- const selectSourceChoices = react.useMemo(() => {
6803
- if (!pendingSelectSourceAction) return [];
6804
- const options = pendingSelectSourceAction.metadata?.options ?? [];
6805
- return buildSelectSourceChoices(options);
6806
- }, [pendingSelectSourceAction]);
6807
- const selectSourceRecommended = react.useMemo(() => {
6808
- if (!pendingSelectSourceAction) return null;
6809
- return pendingSelectSourceAction.metadata?.recommended ?? null;
6810
- }, [pendingSelectSourceAction]);
6811
- const handleSelectSourceChainChange = react.useCallback(
6812
- (chainName) => {
6813
- setSelectSourceChainName(chainName);
6814
- const chain = selectSourceChoices.find((c) => c.chainName === chainName);
6815
- if (!chain || chain.tokens.length === 0) return;
6816
- const recommendedToken = selectSourceRecommended?.chainName === chainName ? selectSourceRecommended.tokenSymbol : null;
6817
- const hasRecommended = !!recommendedToken && chain.tokens.some((t) => t.tokenSymbol === recommendedToken);
6818
- setSelectSourceTokenSymbol(
6819
- hasRecommended ? recommendedToken : chain.tokens[0].tokenSymbol
6820
- );
6821
- },
6822
- [selectSourceChoices, selectSourceRecommended]
6823
- );
6824
- const handleConfirmSelectSource = react.useCallback(() => {
6825
- authExecutor.resolveSelectSource({
6826
- chainName: selectSourceChainName,
6827
- tokenSymbol: selectSourceTokenSymbol
6828
- });
6829
- }, [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;
6830
6912
  react.useEffect(() => {
6831
6913
  if (depositAmount != null) {
6832
6914
  dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
6833
6915
  }
6834
- }, [depositAmount]);
6916
+ }, [depositAmount, dispatch]);
6835
6917
  react.useEffect(() => {
6836
6918
  if (authenticated || state.step !== "otp-verify") return;
6837
6919
  if (activeOtpErrorMessage) dispatch({ type: "SET_ERROR", error: activeOtpErrorMessage });
6838
- }, [activeOtpErrorMessage, authenticated, state.step]);
6920
+ }, [activeOtpErrorMessage, authenticated, state.step, dispatch]);
6839
6921
  react.useEffect(() => {
6840
6922
  if (state.step === "otp-verify" && /^\d{6}$/.test(otpCode.trim()) && activeOtpStatus === "awaiting-code-input") {
6841
6923
  handleVerifyLoginCode();
@@ -7062,7 +7144,7 @@ function SwypePaymentInner({
7062
7144
  const load = async () => {
7063
7145
  dispatch({ type: "DATA_LOAD_START" });
7064
7146
  try {
7065
- const token = await getAccessToken();
7147
+ const token = await getAccessTokenRef.current();
7066
7148
  if (!token) throw new Error("Not authenticated");
7067
7149
  const [prov, accts, chn] = await Promise.all([
7068
7150
  fetchProviders(apiBaseUrl, token),
@@ -7116,7 +7198,6 @@ function SwypePaymentInner({
7116
7198
  state.step,
7117
7199
  state.accounts.length,
7118
7200
  apiBaseUrl,
7119
- getAccessToken,
7120
7201
  state.activeCredentialId,
7121
7202
  state.selectedAccountId,
7122
7203
  depositAmount
@@ -7131,7 +7212,7 @@ function SwypePaymentInner({
7131
7212
  clearMobileFlowState();
7132
7213
  dispatch({ type: "TRANSFER_FAILED", transfer: polling.transfer, error: "Transfer failed." });
7133
7214
  }
7134
- }, [polling.transfer, onComplete]);
7215
+ }, [polling.transfer, onComplete, dispatch]);
7135
7216
  react.useEffect(() => {
7136
7217
  if (state.step !== "processing") {
7137
7218
  processingStartedAtRef.current = null;
@@ -7157,7 +7238,7 @@ function SwypePaymentInner({
7157
7238
  }
7158
7239
  const timeoutId = window.setTimeout(handleTimeout, remainingMs);
7159
7240
  return () => window.clearTimeout(timeoutId);
7160
- }, [state.step, polling.transfer, state.transfer, polling.stopPolling, onError]);
7241
+ }, [state.step, polling.transfer, state.transfer, polling.stopPolling, onError, dispatch, processingStartedAtRef]);
7161
7242
  react.useEffect(() => {
7162
7243
  if (!state.mobileFlow) {
7163
7244
  handlingMobileReturnRef.current = false;
@@ -7167,7 +7248,7 @@ function SwypePaymentInner({
7167
7248
  const polledTransfer = polling.transfer;
7168
7249
  if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
7169
7250
  void handleAuthorizedMobileReturn(polledTransfer, mobileSetupFlowRef.current);
7170
- }, [state.mobileFlow, polling.transfer, handleAuthorizedMobileReturn]);
7251
+ }, [state.mobileFlow, polling.transfer, handleAuthorizedMobileReturn, handlingMobileReturnRef, mobileSetupFlowRef]);
7171
7252
  react.useEffect(() => {
7172
7253
  if (!state.mobileFlow || !mobileSetupFlowRef.current) return;
7173
7254
  if (state.step !== "open-wallet") return;
@@ -7238,7 +7319,18 @@ function SwypePaymentInner({
7238
7319
  window.clearInterval(intervalId);
7239
7320
  document.removeEventListener("visibilitychange", handleVisibility);
7240
7321
  };
7241
- }, [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
+ ]);
7242
7334
  react.useEffect(() => {
7243
7335
  if (!state.mobileFlow) return;
7244
7336
  if (handlingMobileReturnRef.current) return;
@@ -7252,7 +7344,14 @@ function SwypePaymentInner({
7252
7344
  };
7253
7345
  document.addEventListener("visibilitychange", handleVisibility);
7254
7346
  return () => document.removeEventListener("visibilitychange", handleVisibility);
7255
- }, [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
+ ]);
7256
7355
  react.useEffect(() => {
7257
7356
  if (!pendingSelectSourceAction) {
7258
7357
  initializedSelectSourceActionRef.current = null;
@@ -7261,20 +7360,6 @@ function SwypePaymentInner({
7261
7360
  return;
7262
7361
  }
7263
7362
  if (initializedSelectSourceActionRef.current === pendingSelectSourceAction.id) return;
7264
- const options = pendingSelectSourceAction.metadata?.options ?? [];
7265
- if (pendingTokenAuthRef.current) {
7266
- const { tokenAddress, chainId } = pendingTokenAuthRef.current;
7267
- const chainIdHex = `0x${chainId.toString(16)}`;
7268
- const match = options.find(
7269
- (opt) => opt.tokenAddress.toLowerCase() === tokenAddress.toLowerCase() && opt.chainId.toLowerCase() === chainIdHex.toLowerCase()
7270
- );
7271
- if (match) {
7272
- setSelectSourceChainName(match.chainName);
7273
- setSelectSourceTokenSymbol(match.tokenSymbol);
7274
- initializedSelectSourceActionRef.current = pendingSelectSourceAction.id;
7275
- return;
7276
- }
7277
- }
7278
7363
  const hasRecommended = !!selectSourceRecommended && selectSourceChoices.some(
7279
7364
  (chain) => chain.chainName === selectSourceRecommended.chainName && chain.tokens.some((t) => t.tokenSymbol === selectSourceRecommended.tokenSymbol)
7280
7365
  );
@@ -7289,7 +7374,14 @@ function SwypePaymentInner({
7289
7374
  setSelectSourceTokenSymbol("USDC");
7290
7375
  }
7291
7376
  initializedSelectSourceActionRef.current = pendingSelectSourceAction.id;
7292
- }, [pendingSelectSourceAction, selectSourceChoices, selectSourceRecommended]);
7377
+ }, [
7378
+ pendingSelectSourceAction,
7379
+ selectSourceChoices,
7380
+ selectSourceRecommended,
7381
+ setSelectSourceChainName,
7382
+ setSelectSourceTokenSymbol,
7383
+ initializedSelectSourceActionRef
7384
+ ]);
7293
7385
  react.useEffect(() => {
7294
7386
  if (pendingSelectSourceAction && (state.step === "processing" || state.step === "open-wallet" || state.step === "setup-status")) {
7295
7387
  const isDesktop = shouldUseWalletConnector({
@@ -7297,9 +7389,8 @@ function SwypePaymentInner({
7297
7389
  userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
7298
7390
  });
7299
7391
  if (isDesktop && state.step === "setup-status") {
7300
- reloadAccounts().then(() => {
7301
- dispatch({ type: "NAVIGATE", step: "deposit" });
7302
- });
7392
+ preSelectSourceStepRef.current = state.step;
7393
+ dispatch({ type: "NAVIGATE", step: "setup" });
7303
7394
  return;
7304
7395
  }
7305
7396
  preSelectSourceStepRef.current = state.step;
@@ -7308,75 +7399,243 @@ function SwypePaymentInner({
7308
7399
  dispatch({ type: "NAVIGATE", step: preSelectSourceStepRef.current ?? "processing" });
7309
7400
  preSelectSourceStepRef.current = null;
7310
7401
  }
7311
- }, [pendingSelectSourceAction, state.step, authExecutor, useWalletConnectorProp, reloadAccounts]);
7402
+ }, [pendingSelectSourceAction, state.step, useWalletConnectorProp, dispatch, preSelectSourceStepRef, authExecutor]);
7312
7403
  const pendingOneTapSetupAction = authExecutor.pendingOneTapSetup;
7313
7404
  const preOneTapSetupStepRef = react.useRef(null);
7314
7405
  react.useEffect(() => {
7315
7406
  if (pendingOneTapSetupAction && state.step === "setup-status") {
7316
- preOneTapSetupStepRef.current = state.step;
7317
- reloadAccounts().then(() => {
7318
- dispatch({ type: "NAVIGATE", step: "setup" });
7319
- });
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
+ }
7320
7416
  } else if (!pendingOneTapSetupAction && state.step === "setup" && preOneTapSetupStepRef.current) {
7321
7417
  dispatch({ type: "NAVIGATE", step: preOneTapSetupStepRef.current });
7322
7418
  preOneTapSetupStepRef.current = null;
7323
7419
  }
7324
- }, [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
+ });
7325
7596
  const handlers = react.useMemo(() => ({
7326
- onSendLoginCode: handleSendLoginCode,
7327
- onVerifyLoginCode: handleVerifyLoginCode,
7328
- onResendLoginCode: handleResendLoginCode,
7597
+ onSendLoginCode: auth.handleSendLoginCode,
7598
+ onVerifyLoginCode: auth.handleVerifyLoginCode,
7599
+ onResendLoginCode: auth.handleResendLoginCode,
7329
7600
  onBackFromOtp: () => {
7330
- setOtpCode("");
7601
+ auth.setOtpCode("");
7331
7602
  dispatch({ type: "BACK_TO_LOGIN" });
7332
7603
  },
7333
- onRegisterPasskey: handleRegisterPasskey,
7334
- onCreatePasskeyViaPopup: handleCreatePasskeyViaPopup,
7335
- onVerifyPasskeyViaPopup: handleVerifyPasskeyViaPopup,
7336
- onSelectProvider: handleSelectProvider,
7337
- onContinueConnection: handleContinueConnection,
7338
- onSelectAccount: handleSelectAccount,
7339
- onPay: handlePay,
7340
- onIncreaseLimit: handleIncreaseLimit,
7341
- onConfirmSign: handleConfirmSign,
7342
- 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,
7343
7614
  onLogout: handleLogout,
7344
7615
  onNewPayment: handleNewPayment,
7345
7616
  onNavigate: (step) => dispatch({ type: "NAVIGATE", step }),
7346
- onSetAuthInput: setAuthInput,
7617
+ onSetAuthInput: auth.setAuthInput,
7347
7618
  onSetOtpCode: (code) => {
7348
- setOtpCode(code);
7619
+ auth.setOtpCode(code);
7349
7620
  dispatch({ type: "SET_ERROR", error: null });
7350
7621
  },
7351
- onSelectSourceChainChange: handleSelectSourceChainChange,
7352
- onSetSelectSourceTokenSymbol: setSelectSourceTokenSymbol,
7353
- onConfirmSelectSource: handleConfirmSelectSource,
7354
- onSetupOneTap: handleSetupOneTap,
7355
- onSelectToken: handleNavigateToTokenPicker,
7356
- onSelectAuthorizedToken: handleSelectAuthorizedToken,
7357
- 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
7358
7629
  }), [
7359
- handleSendLoginCode,
7360
- handleVerifyLoginCode,
7361
- handleResendLoginCode,
7362
- handleRegisterPasskey,
7363
- handleCreatePasskeyViaPopup,
7364
- handleVerifyPasskeyViaPopup,
7365
- handleSelectProvider,
7366
- handleContinueConnection,
7367
- handleSelectAccount,
7368
- handlePay,
7369
- handleIncreaseLimit,
7370
- handleConfirmSign,
7371
- handleRetryMobileStatus,
7630
+ auth,
7631
+ passkey,
7632
+ provider,
7633
+ transfer,
7634
+ mobileFlow,
7635
+ sourceSelection,
7636
+ oneTapSetup,
7372
7637
  handleLogout,
7373
- handleNewPayment,
7374
- handleSelectSourceChainChange,
7375
- handleConfirmSelectSource,
7376
- handleSetupOneTap,
7377
- handleNavigateToTokenPicker,
7378
- handleSelectAuthorizedToken,
7379
- handleAuthorizeToken
7638
+ handleNewPayment
7380
7639
  ]);
7381
7640
  return /* @__PURE__ */ jsxRuntime.jsx(
7382
7641
  StepRenderer,
@@ -7384,29 +7643,29 @@ function SwypePaymentInner({
7384
7643
  state,
7385
7644
  ready,
7386
7645
  authenticated,
7387
- activeOtpStatus,
7646
+ activeOtpStatus: auth.activeOtpStatus,
7388
7647
  pollingTransfer: polling.transfer,
7389
7648
  pollingError: polling.error,
7390
7649
  authExecutorError: authExecutor.error,
7391
7650
  transferSigningSigning: transferSigning.signing,
7392
7651
  transferSigningError: transferSigning.error,
7393
- pendingConnections,
7394
- depositEligibleAccounts,
7395
- sourceName,
7396
- sourceAddress,
7397
- sourceVerified,
7398
- maxSourceBalance,
7399
- tokenCount,
7400
- selectedAccount,
7401
- selectedSource,
7402
- selectSourceChoices,
7403
- selectSourceRecommended,
7404
- authInput,
7405
- otpCode,
7406
- selectSourceChainName,
7407
- selectSourceTokenSymbol,
7408
- savingOneTapLimit,
7409
- pendingSetup: !!pendingSelectSourceAction || !!pendingOneTapSetupAction,
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,
7410
7669
  merchantName,
7411
7670
  onBack,
7412
7671
  onDismiss,