@swype-org/react-sdk 0.1.133 → 0.1.143

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";
@@ -1858,24 +1681,34 @@ function hasActiveDepositWallet(account) {
1858
1681
  function getPreferredDepositWallet(account, transferAmount) {
1859
1682
  const wallets = getAddressableWallets(account);
1860
1683
  if (wallets.length === 0) return null;
1684
+ let bestWithAllowance = null;
1685
+ let bestWithAllowanceBal = -1;
1686
+ let bestActive = null;
1687
+ let bestActiveBal = -1;
1688
+ let bestAny = null;
1689
+ let bestAnyBal = -1;
1861
1690
  for (const wallet of wallets) {
1862
- if (wallet.status === "ACTIVE" && wallet.sources.some((source) => source.balance.available.amount >= transferAmount)) {
1863
- return wallet;
1864
- }
1865
- }
1866
- let bestWallet = null;
1867
- let bestBalance = -1;
1868
- let bestIsActive = false;
1869
- for (const wallet of wallets) {
1870
- const walletBal = wallet.balance.available.amount;
1871
1691
  const isActive = wallet.status === "ACTIVE";
1872
- if (walletBal > bestBalance || walletBal === bestBalance && isActive && !bestIsActive) {
1873
- bestBalance = walletBal;
1874
- bestWallet = wallet;
1875
- bestIsActive = isActive;
1692
+ const walletBal = wallet.balance.available.amount;
1693
+ if (isActive) {
1694
+ const hasFullyEligibleSource = wallet.sources.some(
1695
+ (s) => s.balance.available.amount >= transferAmount && s.remainingAllowance != null && s.remainingAllowance >= transferAmount
1696
+ );
1697
+ if (hasFullyEligibleSource && walletBal > bestWithAllowanceBal) {
1698
+ bestWithAllowance = wallet;
1699
+ bestWithAllowanceBal = walletBal;
1700
+ }
1701
+ if (walletBal > bestActiveBal) {
1702
+ bestActive = wallet;
1703
+ bestActiveBal = walletBal;
1704
+ }
1705
+ }
1706
+ if (walletBal > bestAnyBal) {
1707
+ bestAny = wallet;
1708
+ bestAnyBal = walletBal;
1876
1709
  }
1877
1710
  }
1878
- return bestWallet ?? wallets[0];
1711
+ return bestWithAllowance ?? bestActive ?? bestAny ?? wallets[0];
1879
1712
  }
1880
1713
  function getDepositEligibleAccounts(accounts) {
1881
1714
  return accounts.filter((account) => getAddressableWallets(account).length > 0);
@@ -1895,7 +1728,9 @@ function resolveDepositSelection(accounts, transferAmount, selectedAccountId) {
1895
1728
  }
1896
1729
  for (const account of eligibleAccounts) {
1897
1730
  const fullyEligibleWallet = getAddressableWallets(account).find(
1898
- (wallet) => wallet.status === "ACTIVE" && wallet.sources.some((source) => source.balance.available.amount >= transferAmount)
1731
+ (wallet) => wallet.status === "ACTIVE" && wallet.sources.some(
1732
+ (source) => source.balance.available.amount >= transferAmount && source.remainingAllowance != null && source.remainingAllowance >= transferAmount
1733
+ )
1899
1734
  );
1900
1735
  if (fullyEligibleWallet) {
1901
1736
  return {
@@ -2235,6 +2070,46 @@ function paymentReducer(state, action) {
2235
2070
  return state;
2236
2071
  }
2237
2072
  }
2073
+
2074
+ // src/auth.ts
2075
+ var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2076
+ var PHONE_DIGIT_PATTERN = /^\d{7,15}$/;
2077
+ function normalizePhoneNumber(rawValue) {
2078
+ const trimmed = rawValue.trim();
2079
+ if (!trimmed) return null;
2080
+ const hasExplicitCountryCode = trimmed.startsWith("+");
2081
+ const digits = trimmed.replace(/\D/g, "");
2082
+ if (!PHONE_DIGIT_PATTERN.test(digits)) return null;
2083
+ return hasExplicitCountryCode ? `+${digits}` : digits;
2084
+ }
2085
+ function normalizeAuthIdentifier(rawValue) {
2086
+ const trimmed = rawValue.trim();
2087
+ if (!trimmed) return null;
2088
+ if (EMAIL_PATTERN.test(trimmed)) {
2089
+ return {
2090
+ kind: "email",
2091
+ value: trimmed.toLowerCase()
2092
+ };
2093
+ }
2094
+ const normalizedPhoneNumber = normalizePhoneNumber(trimmed);
2095
+ if (normalizedPhoneNumber) {
2096
+ return {
2097
+ kind: "phone",
2098
+ value: normalizedPhoneNumber
2099
+ };
2100
+ }
2101
+ return null;
2102
+ }
2103
+ function maskAuthIdentifier(identifier) {
2104
+ if (identifier.kind === "email") {
2105
+ const [localPart, domain = ""] = identifier.value.split("@");
2106
+ const localPrefix = localPart.slice(0, 2);
2107
+ return `${localPrefix}${"*".repeat(Math.max(localPart.length - 2, 0))}@${domain}`;
2108
+ }
2109
+ const digits = identifier.value.replace(/\D/g, "");
2110
+ const visibleSuffix = digits.slice(-4);
2111
+ return `***-***-${visibleSuffix}`;
2112
+ }
2238
2113
  var ACCENT = "#28b67a";
2239
2114
  var BG_RING = "#d2e4ea";
2240
2115
  function SwypeLoadingScreen() {
@@ -3860,7 +3735,7 @@ var dividerTextStyle = (color) => ({
3860
3735
  color,
3861
3736
  whiteSpace: "nowrap"
3862
3737
  });
3863
- var DEFAULT_MAX = 500;
3738
+ var DEFAULT_MAX = 1e7;
3864
3739
  var ABSOLUTE_MIN = 0.01;
3865
3740
  function SetupScreen({
3866
3741
  availableBalance,
@@ -3875,7 +3750,7 @@ function SetupScreen({
3875
3750
  error
3876
3751
  }) {
3877
3752
  const { tokens } = useSwypeConfig();
3878
- const effectiveMax = Math.min(DEFAULT_MAX, availableBalance);
3753
+ const effectiveMax = DEFAULT_MAX;
3879
3754
  const effectiveMin = Math.min(ABSOLUTE_MIN, effectiveMax);
3880
3755
  const [limit, setLimit] = react.useState(() => Math.min(availableBalance, effectiveMax));
3881
3756
  const [editing, setEditing] = react.useState(false);
@@ -4252,8 +4127,7 @@ function DepositScreen({
4252
4127
  onAuthorizeAccount,
4253
4128
  onAddProvider,
4254
4129
  onSelectToken,
4255
- pendingSetup,
4256
- onStartSetup
4130
+ selectedSourceLabel
4257
4131
  }) {
4258
4132
  const { tokens } = useSwypeConfig();
4259
4133
  const amount = initialAmount;
@@ -4261,29 +4135,6 @@ function DepositScreen({
4261
4135
  const exceedsLimit = amount > remainingLimit && !isLowBalance;
4262
4136
  const canDeposit = amount >= MIN_DEPOSIT && !exceedsLimit && !isLowBalance && !processing;
4263
4137
  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
4138
  if (isLowBalance) {
4288
4139
  return /* @__PURE__ */ jsxRuntime.jsxs(
4289
4140
  ScreenLayout,
@@ -4321,7 +4172,7 @@ function DepositScreen({
4321
4172
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "12", fill: "#fff", fontWeight: "700", children: "$" })
4322
4173
  ] }) }),
4323
4174
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4324
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: "Available" }),
4175
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: selectedSourceLabel ?? "Available" }),
4325
4176
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...balanceAmountStyle, color: tokens.warning }, children: [
4326
4177
  "$",
4327
4178
  availableBalance.toFixed(2)
@@ -4396,10 +4247,7 @@ function DepositScreen({
4396
4247
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: "12", y: "16", textAnchor: "middle", fontSize: "12", fill: "#fff", fontWeight: "700", children: "$" })
4397
4248
  ] }) }),
4398
4249
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4399
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceLabelStyle2(tokens.textMuted), children: [
4400
- "Paying from ",
4401
- sourceName
4402
- ] }),
4250
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: balanceLabelStyle2(tokens.textMuted), children: selectedSourceLabel ?? `Paying from ${sourceName}` }),
4403
4251
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: balanceAmountStyle, children: [
4404
4252
  "$",
4405
4253
  availableBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
@@ -4546,29 +4394,6 @@ var switchHintStyle = (color) => ({
4546
4394
  var outlineBtnWrapStyle = {
4547
4395
  marginBottom: 8
4548
4396
  };
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
4397
  function SuccessScreen({
4573
4398
  amount,
4574
4399
  currency,
@@ -5218,6 +5043,28 @@ var waitHintStyle2 = (color) => ({
5218
5043
  color,
5219
5044
  margin: 0
5220
5045
  });
5046
+
5047
+ // src/deeplink.ts
5048
+ var IFRAME_CLEANUP_DELAY_MS = 3e3;
5049
+ var LOCATION_FALLBACK_DELAY_MS = 100;
5050
+ function triggerDeeplink(uri) {
5051
+ try {
5052
+ const iframe = document.createElement("iframe");
5053
+ iframe.style.display = "none";
5054
+ iframe.src = uri;
5055
+ document.body.appendChild(iframe);
5056
+ setTimeout(() => {
5057
+ try {
5058
+ document.body.removeChild(iframe);
5059
+ } catch {
5060
+ }
5061
+ }, IFRAME_CLEANUP_DELAY_MS);
5062
+ } catch {
5063
+ }
5064
+ setTimeout(() => {
5065
+ window.location.href = uri;
5066
+ }, LOCATION_FALLBACK_DELAY_MS);
5067
+ }
5221
5068
  function OpenWalletScreen({
5222
5069
  walletName,
5223
5070
  deeplinkUri,
@@ -5663,12 +5510,12 @@ function StepRenderer({
5663
5510
  selectedSource,
5664
5511
  selectSourceChoices,
5665
5512
  selectSourceRecommended,
5513
+ selectSourceAvailableBalance,
5666
5514
  authInput,
5667
5515
  otpCode,
5668
5516
  selectSourceChainName,
5669
5517
  selectSourceTokenSymbol,
5670
5518
  savingOneTapLimit,
5671
- pendingSetup,
5672
5519
  merchantName,
5673
5520
  onBack,
5674
5521
  onDismiss,
@@ -5785,19 +5632,23 @@ function StepRenderer({
5785
5632
  );
5786
5633
  }
5787
5634
  if (step === "setup") {
5788
- const hasPendingSelectSource = selectSourceChoices.length > 0;
5789
- const setupSourceLabel = hasPendingSelectSource && selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : selectedSourceLabel;
5635
+ const selectSourceTokenCount = selectSourceChoices.reduce(
5636
+ (sum, chain) => sum + chain.tokens.length,
5637
+ 0
5638
+ );
5639
+ const effectiveTokenCount = tokenCount > 0 ? tokenCount : selectSourceTokenCount;
5640
+ const effectiveSourceLabel = selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
5790
5641
  return /* @__PURE__ */ jsxRuntime.jsx(
5791
5642
  SetupScreen,
5792
5643
  {
5793
- availableBalance: selectedSource ? selectedSource.balance.available.amount : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : maxSourceBalance,
5794
- tokenCount,
5644
+ availableBalance: selectedSource ? selectedSource.balance.available.amount : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : selectSourceAvailableBalance > 0 ? selectSourceAvailableBalance : maxSourceBalance,
5645
+ tokenCount: effectiveTokenCount,
5795
5646
  sourceName,
5796
5647
  onSetupOneTap: handlers.onSetupOneTap,
5797
5648
  onBack: () => handlers.onNavigate("deposit"),
5798
5649
  onLogout: handlers.onLogout,
5799
- onAdvanced: hasPendingSelectSource ? () => handlers.onNavigate("select-source") : handlers.onSelectToken,
5800
- selectedSourceLabel: setupSourceLabel,
5650
+ onAdvanced: handlers.onSelectToken,
5651
+ selectedSourceLabel: effectiveSourceLabel,
5801
5652
  loading: savingOneTapLimit,
5802
5653
  error: state.error
5803
5654
  }
@@ -5846,8 +5697,7 @@ function StepRenderer({
5846
5697
  onAuthorizeAccount: handlers.onContinueConnection,
5847
5698
  onAddProvider: () => handlers.onNavigate("wallet-picker"),
5848
5699
  onSelectToken: handlers.onSelectToken,
5849
- pendingSetup,
5850
- onStartSetup: () => handlers.onNavigate("setup")
5700
+ selectedSourceLabel
5851
5701
  }
5852
5702
  );
5853
5703
  }
@@ -5879,7 +5729,7 @@ function StepRenderer({
5879
5729
  );
5880
5730
  }
5881
5731
  if (step === "select-source") {
5882
- const enteredFromSetup = state.previousStep === "setup";
5732
+ const cameFromSetup = state.previousStep === "setup";
5883
5733
  return /* @__PURE__ */ jsxRuntime.jsx(
5884
5734
  SelectSourceScreen,
5885
5735
  {
@@ -5889,8 +5739,8 @@ function StepRenderer({
5889
5739
  recommended: selectSourceRecommended,
5890
5740
  onChainChange: handlers.onSelectSourceChainChange,
5891
5741
  onTokenChange: handlers.onSetSelectSourceTokenSymbol,
5892
- onConfirm: enteredFromSetup ? () => handlers.onNavigate("setup") : handlers.onConfirmSelectSource,
5893
- onBack: enteredFromSetup ? () => handlers.onNavigate("setup") : void 0,
5742
+ onConfirm: cameFromSetup ? () => handlers.onNavigate("setup") : handlers.onConfirmSelectSource,
5743
+ onBack: cameFromSetup ? () => handlers.onNavigate("setup") : void 0,
5894
5744
  onLogout: handlers.onLogout
5895
5745
  }
5896
5746
  );
@@ -5940,12 +5790,29 @@ function StepRenderer({
5940
5790
  selectedAccountId: state.selectedAccountId,
5941
5791
  onSelectAccount: handlers.onSelectAccount,
5942
5792
  onAuthorizeAccount: handlers.onContinueConnection,
5943
- onAddProvider: () => handlers.onNavigate("wallet-picker")
5793
+ onAddProvider: () => handlers.onNavigate("wallet-picker"),
5794
+ selectedSourceLabel
5944
5795
  }
5945
5796
  );
5946
5797
  }
5947
5798
  return null;
5948
5799
  }
5800
+
5801
+ // src/sentry.ts
5802
+ var _mod;
5803
+ function captureException(error) {
5804
+ if (_mod === null) return;
5805
+ if (_mod) {
5806
+ _mod.captureException(error);
5807
+ return;
5808
+ }
5809
+ import('@sentry/react').then((m) => {
5810
+ _mod = m;
5811
+ m.captureException(error);
5812
+ }).catch(() => {
5813
+ _mod = null;
5814
+ });
5815
+ }
5949
5816
  var PaymentErrorBoundary = class extends react.Component {
5950
5817
  constructor(props) {
5951
5818
  super(props);
@@ -6014,78 +5881,7 @@ var buttonStyle3 = {
6014
5881
  fontFamily: "inherit",
6015
5882
  cursor: "pointer"
6016
5883
  };
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();
5884
+ function useDerivedState(state) {
6089
5885
  const { sourceType, sourceId } = deriveSourceTypeAndId(state);
6090
5886
  const selectedAccount = state.accounts.find((a) => a.id === state.selectedAccountId);
6091
5887
  const selectedWallet = selectedAccount?.wallets.find(
@@ -6135,8 +5931,36 @@ function SwypePaymentInner({
6135
5931
  }
6136
5932
  return count;
6137
5933
  }, [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;
5934
+ return {
5935
+ sourceType,
5936
+ sourceId,
5937
+ selectedAccount,
5938
+ selectedWallet,
5939
+ selectedSource,
5940
+ sourceName,
5941
+ sourceAddress,
5942
+ sourceVerified,
5943
+ pendingConnections,
5944
+ depositEligibleAccounts,
5945
+ maxSourceBalance,
5946
+ tokenCount
5947
+ };
5948
+ }
5949
+ function useAuthHandlers(dispatch, verificationTarget) {
5950
+ const {
5951
+ sendCode: sendEmailCode,
5952
+ loginWithCode: loginWithEmailCode,
5953
+ state: emailLoginState
5954
+ } = reactAuth.useLoginWithEmail();
5955
+ const {
5956
+ sendCode: sendSmsCode,
5957
+ loginWithCode: loginWithSmsCode,
5958
+ state: smsLoginState
5959
+ } = reactAuth.useLoginWithSms();
5960
+ const [authInput, setAuthInput] = react.useState("");
5961
+ const [otpCode, setOtpCode] = react.useState("");
5962
+ const activeOtpStatus = verificationTarget?.kind === "email" ? emailLoginState.status : verificationTarget?.kind === "phone" ? smsLoginState.status : "initial";
5963
+ 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
5964
  const handleSendLoginCode = react.useCallback(async () => {
6141
5965
  const normalizedIdentifier = normalizeAuthIdentifier(authInput);
6142
5966
  if (!normalizedIdentifier) {
@@ -6158,9 +5982,9 @@ function SwypePaymentInner({
6158
5982
  error: err instanceof Error ? err.message : "Failed to send verification code"
6159
5983
  });
6160
5984
  }
6161
- }, [authInput, sendEmailCode, sendSmsCode]);
5985
+ }, [authInput, sendEmailCode, sendSmsCode, dispatch]);
6162
5986
  const handleVerifyLoginCode = react.useCallback(async () => {
6163
- if (!state.verificationTarget) return;
5987
+ if (!verificationTarget) return;
6164
5988
  const trimmedCode = otpCode.trim();
6165
5989
  if (!/^\d{6}$/.test(trimmedCode)) {
6166
5990
  dispatch({ type: "SET_ERROR", error: "Enter the 6-digit verification code." });
@@ -6168,7 +5992,7 @@ function SwypePaymentInner({
6168
5992
  }
6169
5993
  dispatch({ type: "SET_ERROR", error: null });
6170
5994
  try {
6171
- if (state.verificationTarget.kind === "email") {
5995
+ if (verificationTarget.kind === "email") {
6172
5996
  await loginWithEmailCode({ code: trimmedCode });
6173
5997
  } else {
6174
5998
  await loginWithSmsCode({ code: trimmedCode });
@@ -6180,15 +6004,15 @@ function SwypePaymentInner({
6180
6004
  error: err instanceof Error ? err.message : "Failed to verify code"
6181
6005
  });
6182
6006
  }
6183
- }, [state.verificationTarget, otpCode, loginWithEmailCode, loginWithSmsCode]);
6007
+ }, [verificationTarget, otpCode, loginWithEmailCode, loginWithSmsCode, dispatch]);
6184
6008
  const handleResendLoginCode = react.useCallback(async () => {
6185
- if (!state.verificationTarget) return;
6009
+ if (!verificationTarget) return;
6186
6010
  dispatch({ type: "SET_ERROR", error: null });
6187
6011
  try {
6188
- if (state.verificationTarget.kind === "email") {
6189
- await sendEmailCode({ email: state.verificationTarget.value });
6012
+ if (verificationTarget.kind === "email") {
6013
+ await sendEmailCode({ email: verificationTarget.value });
6190
6014
  } else {
6191
- await sendSmsCode({ phoneNumber: state.verificationTarget.value });
6015
+ await sendSmsCode({ phoneNumber: verificationTarget.value });
6192
6016
  }
6193
6017
  } catch (err) {
6194
6018
  captureException(err);
@@ -6197,7 +6021,68 @@ function SwypePaymentInner({
6197
6021
  error: err instanceof Error ? err.message : "Failed to resend code"
6198
6022
  });
6199
6023
  }
6200
- }, [state.verificationTarget, sendEmailCode, sendSmsCode]);
6024
+ }, [verificationTarget, sendEmailCode, sendSmsCode, dispatch]);
6025
+ return {
6026
+ authInput,
6027
+ otpCode,
6028
+ activeOtpStatus,
6029
+ activeOtpErrorMessage,
6030
+ setAuthInput,
6031
+ setOtpCode,
6032
+ handleSendLoginCode,
6033
+ handleVerifyLoginCode,
6034
+ handleResendLoginCode
6035
+ };
6036
+ }
6037
+
6038
+ // src/mobileFlow.ts
6039
+ function hasActiveWallet(accounts) {
6040
+ return accounts.some((account) => account.wallets.some((wallet) => wallet.status === "ACTIVE"));
6041
+ }
6042
+ function resolvePostAuthStep(state) {
6043
+ if (!state.hasPasskey) {
6044
+ return { step: "create-passkey", clearPersistedFlow: false };
6045
+ }
6046
+ if (state.persistedMobileFlow) {
6047
+ if (state.persistedMobileFlow.isReauthorization) {
6048
+ return { step: "open-wallet", clearPersistedFlow: false };
6049
+ }
6050
+ if (state.persistedMobileFlow.isSetup && hasActiveWallet(state.accounts)) {
6051
+ return { step: "deposit", clearPersistedFlow: true };
6052
+ }
6053
+ return { step: "open-wallet", clearPersistedFlow: false };
6054
+ }
6055
+ if (state.mobileSetupInProgress && !hasActiveWallet(state.accounts)) {
6056
+ return { step: "open-wallet", clearPersistedFlow: false };
6057
+ }
6058
+ if ((state.accounts.length === 0 || !hasActiveWallet(state.accounts)) && !state.connectingNewAccount) {
6059
+ return { step: "wallet-picker", clearPersistedFlow: false };
6060
+ }
6061
+ return { step: "deposit", clearPersistedFlow: false };
6062
+ }
6063
+ function resolveRestoredMobileFlow(transferStatus, isSetup) {
6064
+ if (transferStatus === "AUTHORIZED") {
6065
+ return isSetup ? { kind: "resume-setup-deposit", step: "deposit", clearPersistedFlow: true } : { kind: "resume-confirm-sign", step: "confirm-sign", clearPersistedFlow: true };
6066
+ }
6067
+ if (transferStatus === "COMPLETED") {
6068
+ return { kind: "resume-success", step: "success", clearPersistedFlow: true };
6069
+ }
6070
+ if (transferStatus === "FAILED") {
6071
+ return { kind: "resume-failed", step: "success", clearPersistedFlow: true };
6072
+ }
6073
+ if (transferStatus === "SENDING" || transferStatus === "SENT") {
6074
+ return { kind: "resume-processing", step: "processing", clearPersistedFlow: true };
6075
+ }
6076
+ if (isSetup) {
6077
+ return { kind: "resume-stale-setup", step: "wallet-picker", clearPersistedFlow: true };
6078
+ }
6079
+ return { kind: "resume-open-wallet", step: "open-wallet", clearPersistedFlow: false };
6080
+ }
6081
+
6082
+ // src/hooks/usePasskeyHandlers.ts
6083
+ function usePasskeyHandlers(dispatch, apiBaseUrl, accounts, knownCredentialIds, mobileSetupFlowRef) {
6084
+ const { user, getAccessToken } = reactAuth.usePrivy();
6085
+ const checkingPasskeyRef = react.useRef(false);
6201
6086
  const completePasskeyRegistration = react.useCallback(async (credentialId, publicKey) => {
6202
6087
  const token = await getAccessToken();
6203
6088
  if (!token) throw new Error("Not authenticated");
@@ -6206,14 +6091,14 @@ function SwypePaymentInner({
6206
6091
  window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
6207
6092
  const resolved = resolvePostAuthStep({
6208
6093
  hasPasskey: true,
6209
- accounts: state.accounts,
6094
+ accounts,
6210
6095
  persistedMobileFlow: loadMobileFlowState(),
6211
6096
  mobileSetupInProgress: mobileSetupFlowRef.current,
6212
6097
  connectingNewAccount: false
6213
6098
  });
6214
6099
  if (resolved.clearPersistedFlow) clearMobileFlowState();
6215
6100
  dispatch({ type: "NAVIGATE", step: resolved.step });
6216
- }, [getAccessToken, apiBaseUrl, state.accounts]);
6101
+ }, [getAccessToken, apiBaseUrl, accounts, mobileSetupFlowRef, dispatch]);
6217
6102
  const handleRegisterPasskey = react.useCallback(async () => {
6218
6103
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
6219
6104
  dispatch({ type: "SET_ERROR", error: null });
@@ -6237,7 +6122,7 @@ function SwypePaymentInner({
6237
6122
  } finally {
6238
6123
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
6239
6124
  }
6240
- }, [user, completePasskeyRegistration]);
6125
+ }, [user, completePasskeyRegistration, dispatch]);
6241
6126
  const handleCreatePasskeyViaPopup = react.useCallback(async () => {
6242
6127
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
6243
6128
  dispatch({ type: "SET_ERROR", error: null });
@@ -6255,7 +6140,7 @@ function SwypePaymentInner({
6255
6140
  localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
6256
6141
  const resolved = resolvePostAuthStep({
6257
6142
  hasPasskey: true,
6258
- accounts: state.accounts,
6143
+ accounts,
6259
6144
  persistedMobileFlow: loadMobileFlowState(),
6260
6145
  mobileSetupInProgress: mobileSetupFlowRef.current,
6261
6146
  connectingNewAccount: false
@@ -6271,14 +6156,14 @@ function SwypePaymentInner({
6271
6156
  } finally {
6272
6157
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
6273
6158
  }
6274
- }, [user, getAccessToken, apiBaseUrl, state.accounts]);
6159
+ }, [user, getAccessToken, apiBaseUrl, accounts, mobileSetupFlowRef, dispatch]);
6275
6160
  const handleVerifyPasskeyViaPopup = react.useCallback(async () => {
6276
6161
  dispatch({ type: "SET_VERIFYING_PASSKEY", value: true });
6277
6162
  dispatch({ type: "SET_ERROR", error: null });
6278
6163
  try {
6279
6164
  const token = await getAccessToken();
6280
6165
  const matched = await findDevicePasskeyViaPopup({
6281
- credentialIds: state.knownCredentialIds,
6166
+ credentialIds: knownCredentialIds,
6282
6167
  rpId: resolvePasskeyRpId(),
6283
6168
  authToken: token ?? void 0,
6284
6169
  apiBaseUrl
@@ -6292,7 +6177,7 @@ function SwypePaymentInner({
6292
6177
  }
6293
6178
  const resolved = resolvePostAuthStep({
6294
6179
  hasPasskey: true,
6295
- accounts: state.accounts,
6180
+ accounts,
6296
6181
  persistedMobileFlow: loadMobileFlowState(),
6297
6182
  mobileSetupInProgress: mobileSetupFlowRef.current,
6298
6183
  connectingNewAccount: false
@@ -6314,44 +6199,48 @@ function SwypePaymentInner({
6314
6199
  } finally {
6315
6200
  dispatch({ type: "SET_VERIFYING_PASSKEY", value: false });
6316
6201
  }
6317
- }, [state.knownCredentialIds, getAccessToken, apiBaseUrl, state.accounts]);
6202
+ }, [knownCredentialIds, getAccessToken, apiBaseUrl, accounts, mobileSetupFlowRef, dispatch]);
6203
+ return {
6204
+ handleRegisterPasskey,
6205
+ handleCreatePasskeyViaPopup,
6206
+ handleVerifyPasskeyViaPopup,
6207
+ checkingPasskeyRef
6208
+ };
6209
+ }
6210
+ function useTransferHandlers(deps) {
6211
+ const {
6212
+ dispatch,
6213
+ getAccessToken,
6214
+ apiBaseUrl,
6215
+ depositAmount,
6216
+ destination,
6217
+ idempotencyKey,
6218
+ merchantAuthorization,
6219
+ onComplete,
6220
+ onError,
6221
+ polling,
6222
+ transferSigning,
6223
+ sourceType,
6224
+ sourceId,
6225
+ sourceTokenAddress,
6226
+ activeCredentialId,
6227
+ selectedAccountId,
6228
+ transfer,
6229
+ accounts
6230
+ } = deps;
6231
+ const processingStartedAtRef = react.useRef(null);
6232
+ const pollingTransferIdRef = react.useRef(null);
6318
6233
  const reloadAccounts = react.useCallback(async () => {
6319
6234
  const token = await getAccessToken();
6320
- if (!token || !state.activeCredentialId) return;
6235
+ if (!token || !activeCredentialId) return;
6321
6236
  const [accts, prov] = await Promise.all([
6322
- fetchAccounts(apiBaseUrl, token, state.activeCredentialId),
6237
+ fetchAccounts(apiBaseUrl, token, activeCredentialId),
6323
6238
  fetchProviders(apiBaseUrl, token)
6324
6239
  ]);
6325
6240
  const parsedAmt = depositAmount != null ? depositAmount : 0;
6326
- const defaults = resolveDepositSelection(accts, parsedAmt, state.selectedAccountId);
6241
+ const defaults = resolveDepositSelection(accts, parsedAmt, selectedAccountId);
6327
6242
  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;
6243
+ }, [getAccessToken, activeCredentialId, selectedAccountId, apiBaseUrl, depositAmount, dispatch]);
6355
6244
  const handlePay = react.useCallback(async (payAmount, sourceOverrides) => {
6356
6245
  if (isNaN(payAmount) || payAmount < MIN_SEND_AMOUNT_USD) {
6357
6246
  dispatch({ type: "SET_ERROR", error: `Minimum amount is $${MIN_SEND_AMOUNT_USD.toFixed(2)}.` });
@@ -6361,7 +6250,7 @@ function SwypePaymentInner({
6361
6250
  dispatch({ type: "SET_ERROR", error: "No account or provider selected." });
6362
6251
  return;
6363
6252
  }
6364
- if (!state.activeCredentialId) {
6253
+ if (!activeCredentialId) {
6365
6254
  dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6366
6255
  dispatch({ type: "NAVIGATE", step: "create-passkey" });
6367
6256
  return;
@@ -6369,10 +6258,10 @@ function SwypePaymentInner({
6369
6258
  dispatch({ type: "PAY_STARTED", isSetupRedirect: false });
6370
6259
  processingStartedAtRef.current = Date.now();
6371
6260
  try {
6372
- if (state.transfer?.status === "AUTHORIZED") {
6373
- const signedTransfer2 = await transferSigning.signTransfer(state.transfer.id);
6261
+ if (transfer?.status === "AUTHORIZED") {
6262
+ const signedTransfer2 = await transferSigning.signTransfer(transfer.id);
6374
6263
  dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer2 });
6375
- polling.startPolling(state.transfer.id);
6264
+ polling.startPolling(transfer.id);
6376
6265
  return;
6377
6266
  }
6378
6267
  const token = await getAccessToken();
@@ -6380,7 +6269,7 @@ function SwypePaymentInner({
6380
6269
  let effectiveSourceType = sourceOverrides?.sourceType ?? sourceType;
6381
6270
  let effectiveSourceId = sourceOverrides?.sourceId ?? sourceId;
6382
6271
  if (effectiveSourceType === "accountId") {
6383
- const acct = state.accounts.find((a) => a.id === effectiveSourceId);
6272
+ const acct = accounts.find((a) => a.id === effectiveSourceId);
6384
6273
  const preferredWallet = acct ? getPreferredDepositWallet(acct, payAmount) : null;
6385
6274
  if (preferredWallet?.status === "ACTIVE") {
6386
6275
  effectiveSourceType = "walletId";
@@ -6389,10 +6278,11 @@ function SwypePaymentInner({
6389
6278
  }
6390
6279
  const t = await createTransfer(apiBaseUrl, token, {
6391
6280
  id: idempotencyKey,
6392
- credentialId: state.activeCredentialId,
6281
+ credentialId: activeCredentialId,
6393
6282
  merchantAuthorization,
6394
6283
  sourceType: effectiveSourceType,
6395
6284
  sourceId: effectiveSourceId,
6285
+ sourceTokenAddress,
6396
6286
  destination,
6397
6287
  amount: payAmount
6398
6288
  });
@@ -6412,11 +6302,7 @@ function SwypePaymentInner({
6412
6302
  } catch (err) {
6413
6303
  captureException(err);
6414
6304
  const msg = err instanceof Error ? err.message : "Transfer failed";
6415
- dispatch({
6416
- type: "PAY_ERROR",
6417
- error: msg,
6418
- fallbackStep: "deposit"
6419
- });
6305
+ dispatch({ type: "PAY_ERROR", error: msg, fallbackStep: "deposit" });
6420
6306
  onError?.(msg);
6421
6307
  } finally {
6422
6308
  dispatch({ type: "PAY_ENDED" });
@@ -6424,9 +6310,10 @@ function SwypePaymentInner({
6424
6310
  }, [
6425
6311
  sourceId,
6426
6312
  sourceType,
6427
- state.activeCredentialId,
6428
- state.transfer,
6429
- state.accounts,
6313
+ sourceTokenAddress,
6314
+ activeCredentialId,
6315
+ transfer,
6316
+ accounts,
6430
6317
  destination,
6431
6318
  apiBaseUrl,
6432
6319
  getAccessToken,
@@ -6435,185 +6322,133 @@ function SwypePaymentInner({
6435
6322
  onError,
6436
6323
  onComplete,
6437
6324
  idempotencyKey,
6438
- merchantAuthorization
6325
+ merchantAuthorization,
6326
+ dispatch
6439
6327
  ]);
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 });
6328
+ const handleConfirmSign = react.useCallback(async () => {
6329
+ const t = transfer ?? polling.transfer;
6330
+ if (!t) return;
6457
6331
  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
- }
6332
+ const signedTransfer = await transferSigning.signTransfer(t.id);
6333
+ clearMobileFlowState();
6334
+ dispatch({ type: "CONFIRM_SIGN_SUCCESS", transfer: signedTransfer });
6335
+ polling.startPolling(t.id);
6491
6336
  } catch (err) {
6492
6337
  captureException(err);
6493
- const msg = err instanceof Error ? err.message : "Failed to increase limit";
6338
+ const msg = err instanceof Error ? err.message : "Failed to sign transfer";
6494
6339
  dispatch({ type: "SET_ERROR", error: msg });
6495
6340
  onError?.(msg);
6496
- } finally {
6497
- dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6498
6341
  }
6499
- }, [
6500
- state.selectedAccountId,
6501
- state.activeCredentialId,
6502
- state.accounts,
6503
- state.providers,
6504
- apiBaseUrl,
6505
- getAccessToken,
6506
- authExecutor,
6507
- useWalletConnectorProp,
6342
+ }, [transfer, polling.transfer, polling.startPolling, transferSigning, onError, dispatch]);
6343
+ return {
6508
6344
  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;
6345
+ handlePay,
6346
+ handleConfirmSign,
6347
+ processingStartedAtRef,
6348
+ pollingTransferIdRef
6349
+ };
6350
+ }
6351
+ function useSourceSelectionHandlers(dispatch, authExecutor) {
6352
+ const [selectSourceChainName, setSelectSourceChainName] = react.useState("");
6353
+ const [selectSourceTokenSymbol, setSelectSourceTokenSymbol] = react.useState("");
6354
+ const initializedSelectSourceActionRef = react.useRef(null);
6355
+ const preSelectSourceStepRef = react.useRef(null);
6356
+ const pendingSelectSourceAction = authExecutor.pendingSelectSource;
6357
+ const selectSourceChoices = react.useMemo(() => {
6358
+ if (!pendingSelectSourceAction) return [];
6359
+ const options = pendingSelectSourceAction.metadata?.options ?? [];
6360
+ return buildSelectSourceChoices(options);
6361
+ }, [pendingSelectSourceAction]);
6362
+ const selectSourceRecommended = react.useMemo(() => {
6363
+ if (!pendingSelectSourceAction) return null;
6364
+ return pendingSelectSourceAction.metadata?.recommended ?? null;
6365
+ }, [pendingSelectSourceAction]);
6366
+ const selectSourceAvailableBalance = react.useMemo(() => {
6367
+ if (!pendingSelectSourceAction) return 0;
6368
+ const options = pendingSelectSourceAction.metadata?.options ?? [];
6369
+ const recommended = selectSourceRecommended;
6370
+ if (recommended) {
6371
+ const match = options.find(
6372
+ (opt) => opt.chainName === recommended.chainName && opt.tokenSymbol === recommended.tokenSymbol
6373
+ );
6374
+ if (match) return Number(match.rawBalance) / Math.pow(10, match.decimals);
6526
6375
  }
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 });
6376
+ let max = 0;
6377
+ for (const opt of options) {
6378
+ const bal = Number(opt.rawBalance) / Math.pow(10, opt.decimals);
6379
+ if (bal > max) max = bal;
6531
6380
  }
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 }
6381
+ return max;
6382
+ }, [pendingSelectSourceAction, selectSourceRecommended]);
6383
+ const handleSelectSourceChainChange = react.useCallback(
6384
+ (chainName) => {
6385
+ setSelectSourceChainName(chainName);
6386
+ const chain = selectSourceChoices.find((c) => c.chainName === chainName);
6387
+ if (!chain || chain.tokens.length === 0) return;
6388
+ const recommendedToken = selectSourceRecommended?.chainName === chainName ? selectSourceRecommended.tokenSymbol : null;
6389
+ const hasRecommended = !!recommendedToken && chain.tokens.some((t) => t.tokenSymbol === recommendedToken);
6390
+ setSelectSourceTokenSymbol(
6391
+ hasRecommended ? recommendedToken : chain.tokens[0].tokenSymbol
6544
6392
  );
6545
- const isMobile = !shouldUseWalletConnector({
6546
- useWalletConnector: useWalletConnectorProp,
6547
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6548
- });
6549
- if (isMobile) {
6393
+ },
6394
+ [selectSourceChoices, selectSourceRecommended]
6395
+ );
6396
+ const handleConfirmSelectSource = react.useCallback(() => {
6397
+ authExecutor.resolveSelectSource({
6398
+ chainName: selectSourceChainName,
6399
+ tokenSymbol: selectSourceTokenSymbol
6400
+ });
6401
+ }, [authExecutor, selectSourceChainName, selectSourceTokenSymbol]);
6402
+ return {
6403
+ selectSourceChainName,
6404
+ selectSourceTokenSymbol,
6405
+ setSelectSourceChainName,
6406
+ setSelectSourceTokenSymbol,
6407
+ selectSourceChoices,
6408
+ selectSourceRecommended,
6409
+ selectSourceAvailableBalance,
6410
+ handleSelectSourceChainChange,
6411
+ handleConfirmSelectSource,
6412
+ pendingSelectSourceAction,
6413
+ initializedSelectSourceActionRef,
6414
+ preSelectSourceStepRef
6415
+ };
6416
+ }
6417
+ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs) {
6418
+ const {
6419
+ mobileSetupFlowRef,
6420
+ handlingMobileReturnRef,
6421
+ loadingDataRef
6422
+ } = refs;
6423
+ const handleAuthorizedMobileReturn = react.useCallback(async (authorizedTransfer, isSetup) => {
6424
+ if (handlingMobileReturnRef.current) return;
6425
+ handlingMobileReturnRef.current = true;
6426
+ polling.stopPolling();
6427
+ if (isSetup) {
6428
+ mobileSetupFlowRef.current = false;
6429
+ clearMobileFlowState();
6430
+ try {
6431
+ await reloadAccounts();
6432
+ loadingDataRef.current = false;
6433
+ dispatch({ type: "MOBILE_SETUP_COMPLETE", transfer: authorizedTransfer });
6434
+ } catch (err) {
6550
6435
  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 }
6436
+ dispatch({
6437
+ type: "SET_ERROR",
6438
+ error: err instanceof Error ? err.message : "Wallet authorized, but we could not refresh your account yet."
6563
6439
  });
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" });
6440
+ dispatch({ type: "NAVIGATE", step: "open-wallet" });
6576
6441
  }
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);
6442
+ return;
6611
6443
  }
6612
- }, [state.transfer, polling.transfer, polling.startPolling, transferSigning, onError]);
6444
+ mobileSetupFlowRef.current = false;
6445
+ clearMobileFlowState();
6446
+ dispatch({ type: "MOBILE_SIGN_READY", transfer: authorizedTransfer });
6447
+ }, [polling.stopPolling, reloadAccounts, dispatch]);
6613
6448
  const handleRetryMobileStatus = react.useCallback(() => {
6614
6449
  dispatch({ type: "SET_ERROR", error: null });
6615
6450
  handlingMobileReturnRef.current = false;
6616
- const currentTransfer = polling.transfer ?? state.transfer;
6451
+ const currentTransfer = polling.transfer ?? stateTransfer;
6617
6452
  if (currentTransfer?.status === "AUTHORIZED") {
6618
6453
  void handleAuthorizedMobileReturn(currentTransfer, mobileSetupFlowRef.current);
6619
6454
  return;
@@ -6622,15 +6457,57 @@ function SwypePaymentInner({
6622
6457
  if (transferIdToResume) {
6623
6458
  polling.startPolling(transferIdToResume);
6624
6459
  }
6625
- }, [handleAuthorizedMobileReturn, polling, state.transfer]);
6460
+ }, [handleAuthorizedMobileReturn, polling, stateTransfer, pollingTransferIdRef, dispatch]);
6461
+ return {
6462
+ handleAuthorizedMobileReturn,
6463
+ handleRetryMobileStatus
6464
+ };
6465
+ }
6466
+
6467
+ // src/walletFlow.ts
6468
+ var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
6469
+ function isMobileUserAgent(userAgent) {
6470
+ if (!userAgent) {
6471
+ return false;
6472
+ }
6473
+ return MOBILE_USER_AGENT_PATTERN.test(userAgent);
6474
+ }
6475
+ function shouldUseWalletConnector(options) {
6476
+ return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
6477
+ }
6478
+
6479
+ // src/hooks/useProviderHandlers.ts
6480
+ function useProviderHandlers(deps) {
6481
+ const {
6482
+ dispatch,
6483
+ getAccessToken,
6484
+ apiBaseUrl,
6485
+ depositAmount,
6486
+ useWalletConnectorProp,
6487
+ activeCredentialId,
6488
+ selectedAccountId,
6489
+ selectedWalletId,
6490
+ selectedTokenSymbol,
6491
+ chains,
6492
+ accounts,
6493
+ providers,
6494
+ authExecutor,
6495
+ reloadAccounts,
6496
+ onError,
6497
+ mobileSetupFlowRef,
6498
+ handlingMobileReturnRef,
6499
+ setupAccountIdRef,
6500
+ reauthSessionIdRef,
6501
+ reauthTokenRef
6502
+ } = deps;
6626
6503
  const handleSelectProvider = react.useCallback(async (providerId) => {
6627
6504
  dispatch({ type: "SELECT_PROVIDER", providerId });
6628
- if (!state.activeCredentialId) {
6505
+ if (!activeCredentialId) {
6629
6506
  dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6630
6507
  dispatch({ type: "NAVIGATE", step: "create-passkey" });
6631
6508
  return;
6632
6509
  }
6633
- const provider = state.providers.find((p) => p.id === providerId);
6510
+ const provider = providers.find((p) => p.id === providerId);
6634
6511
  const providerName = provider?.name ?? "Wallet";
6635
6512
  const isMobile = !shouldUseWalletConnector({
6636
6513
  useWalletConnector: useWalletConnectorProp,
@@ -6649,7 +6526,7 @@ function SwypePaymentInner({
6649
6526
  const account = await createAccount(apiBaseUrl, token, {
6650
6527
  id: accountId,
6651
6528
  name: providerName,
6652
- credentialId: state.activeCredentialId,
6529
+ credentialId: activeCredentialId,
6653
6530
  providerId
6654
6531
  });
6655
6532
  const session = account.authorizationSessions?.[0];
@@ -6681,29 +6558,33 @@ function SwypePaymentInner({
6681
6558
  dispatch({ type: "PAY_ENDED" });
6682
6559
  }
6683
6560
  }, [
6684
- state.activeCredentialId,
6685
- state.providers,
6561
+ activeCredentialId,
6562
+ providers,
6686
6563
  apiBaseUrl,
6687
6564
  getAccessToken,
6688
6565
  authExecutor,
6689
6566
  useWalletConnectorProp,
6690
6567
  reloadAccounts,
6691
- onError
6568
+ onError,
6569
+ dispatch,
6570
+ mobileSetupFlowRef,
6571
+ handlingMobileReturnRef,
6572
+ setupAccountIdRef
6692
6573
  ]);
6693
6574
  const handleContinueConnection = react.useCallback(
6694
6575
  (accountId) => {
6695
- const acct = state.accounts.find((a) => a.id === accountId);
6576
+ const acct = accounts.find((a) => a.id === accountId);
6696
6577
  if (!acct) return;
6697
- const matchedProvider = state.providers.find((p) => p.name === acct.name);
6578
+ const matchedProvider = providers.find((p) => p.name === acct.name);
6698
6579
  if (matchedProvider) {
6699
6580
  handleSelectProvider(matchedProvider.id);
6700
6581
  }
6701
6582
  },
6702
- [state.accounts, state.providers, handleSelectProvider]
6583
+ [accounts, providers, handleSelectProvider]
6703
6584
  );
6704
6585
  const handleSelectAccount = react.useCallback(
6705
6586
  (accountId) => {
6706
- const acct = state.accounts.find((a) => a.id === accountId);
6587
+ const acct = accounts.find((a) => a.id === accountId);
6707
6588
  if (!acct) return;
6708
6589
  const activeWallet = getPreferredDepositWallet(acct, depositAmount ?? 0);
6709
6590
  dispatch({
@@ -6712,8 +6593,206 @@ function SwypePaymentInner({
6712
6593
  walletId: activeWallet?.id ?? null
6713
6594
  });
6714
6595
  },
6715
- [state.accounts, depositAmount]
6596
+ [accounts, depositAmount, dispatch]
6716
6597
  );
6598
+ const handleIncreaseLimit = react.useCallback(async () => {
6599
+ if (!selectedAccountId) {
6600
+ dispatch({ type: "SET_ERROR", error: "No account selected." });
6601
+ return;
6602
+ }
6603
+ if (!activeCredentialId) {
6604
+ dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6605
+ dispatch({ type: "NAVIGATE", step: "create-passkey" });
6606
+ return;
6607
+ }
6608
+ const acct = accounts.find((a) => a.id === selectedAccountId);
6609
+ const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
6610
+ if (matchedProvider) {
6611
+ dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
6612
+ }
6613
+ dispatch({ type: "SET_ERROR", error: null });
6614
+ dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6615
+ try {
6616
+ const token = await getAccessToken();
6617
+ if (!token) throw new Error("Not authenticated");
6618
+ const wallet = acct?.wallets.find((w) => w.id === selectedWalletId);
6619
+ const source = wallet?.sources.find(
6620
+ (s) => selectedTokenSymbol ? s.token.symbol === selectedTokenSymbol : s.token.status === "AUTHORIZED"
6621
+ );
6622
+ const evmChainId = chains.find((c) => c.name === wallet?.chain.name)?.commonId ?? void 0;
6623
+ const session = await createAccountAuthorizationSession(
6624
+ apiBaseUrl,
6625
+ token,
6626
+ selectedAccountId,
6627
+ activeCredentialId,
6628
+ { tokenAddress: source?.address, chainId: evmChainId }
6629
+ );
6630
+ const isMobile = !shouldUseWalletConnector({
6631
+ useWalletConnector: useWalletConnectorProp,
6632
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6633
+ });
6634
+ if (isMobile) {
6635
+ handlingMobileReturnRef.current = false;
6636
+ mobileSetupFlowRef.current = true;
6637
+ setupAccountIdRef.current = selectedAccountId;
6638
+ reauthSessionIdRef.current = session.id;
6639
+ reauthTokenRef.current = null;
6640
+ persistMobileFlowState({
6641
+ accountId: selectedAccountId,
6642
+ sessionId: session.id,
6643
+ deeplinkUri: session.uri,
6644
+ providerId: matchedProvider?.id ?? null,
6645
+ isSetup: true,
6646
+ isReauthorization: true
6647
+ });
6648
+ dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6649
+ triggerDeeplink(session.uri);
6650
+ } else {
6651
+ dispatch({ type: "NAVIGATE", step: "setup-status" });
6652
+ await authExecutor.executeSessionById(session.id);
6653
+ await reloadAccounts();
6654
+ dispatch({ type: "NAVIGATE", step: "deposit" });
6655
+ }
6656
+ } catch (err) {
6657
+ captureException(err);
6658
+ const msg = err instanceof Error ? err.message : "Failed to increase limit";
6659
+ dispatch({ type: "SET_ERROR", error: msg });
6660
+ onError?.(msg);
6661
+ } finally {
6662
+ dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6663
+ }
6664
+ }, [
6665
+ selectedAccountId,
6666
+ selectedWalletId,
6667
+ selectedTokenSymbol,
6668
+ chains,
6669
+ activeCredentialId,
6670
+ accounts,
6671
+ providers,
6672
+ apiBaseUrl,
6673
+ getAccessToken,
6674
+ authExecutor,
6675
+ useWalletConnectorProp,
6676
+ reloadAccounts,
6677
+ onError,
6678
+ dispatch,
6679
+ mobileSetupFlowRef,
6680
+ handlingMobileReturnRef,
6681
+ setupAccountIdRef,
6682
+ reauthSessionIdRef,
6683
+ reauthTokenRef
6684
+ ]);
6685
+ const handleNavigateToTokenPicker = react.useCallback(() => {
6686
+ if (authExecutor.pendingSelectSource) {
6687
+ dispatch({ type: "NAVIGATE", step: "select-source" });
6688
+ } else {
6689
+ dispatch({ type: "NAVIGATE", step: "token-picker" });
6690
+ }
6691
+ }, [dispatch, authExecutor.pendingSelectSource]);
6692
+ const handleSelectAuthorizedToken = react.useCallback((walletId, tokenSymbol) => {
6693
+ dispatch({ type: "SELECT_TOKEN", walletId, tokenSymbol });
6694
+ }, [dispatch]);
6695
+ const handleAuthorizeToken = react.useCallback(async (_walletId, tokenAddress, chainId, tokenSymbol) => {
6696
+ if (!selectedAccountId) {
6697
+ dispatch({ type: "SET_ERROR", error: "No account selected." });
6698
+ return;
6699
+ }
6700
+ if (!activeCredentialId) {
6701
+ dispatch({ type: "SET_ERROR", error: "Create or verify a passkey on this device before continuing." });
6702
+ dispatch({ type: "NAVIGATE", step: "create-passkey" });
6703
+ return;
6704
+ }
6705
+ const acct = accounts.find((a) => a.id === selectedAccountId);
6706
+ const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
6707
+ if (matchedProvider) {
6708
+ dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
6709
+ }
6710
+ dispatch({ type: "SET_ERROR", error: null });
6711
+ dispatch({ type: "SET_INCREASING_LIMIT", value: true });
6712
+ try {
6713
+ const token = await getAccessToken();
6714
+ if (!token) throw new Error("Not authenticated");
6715
+ const session = await createAccountAuthorizationSession(
6716
+ apiBaseUrl,
6717
+ token,
6718
+ selectedAccountId,
6719
+ activeCredentialId,
6720
+ { tokenAddress, chainId }
6721
+ );
6722
+ const isMobile = !shouldUseWalletConnector({
6723
+ useWalletConnector: useWalletConnectorProp,
6724
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
6725
+ });
6726
+ if (isMobile) {
6727
+ handlingMobileReturnRef.current = false;
6728
+ mobileSetupFlowRef.current = true;
6729
+ setupAccountIdRef.current = selectedAccountId;
6730
+ reauthSessionIdRef.current = session.id;
6731
+ reauthTokenRef.current = { walletId: _walletId, tokenSymbol };
6732
+ persistMobileFlowState({
6733
+ accountId: selectedAccountId,
6734
+ sessionId: session.id,
6735
+ deeplinkUri: session.uri,
6736
+ providerId: matchedProvider?.id ?? null,
6737
+ isSetup: true,
6738
+ isReauthorization: true,
6739
+ reauthorizationToken: { walletId: _walletId, tokenSymbol }
6740
+ });
6741
+ dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
6742
+ triggerDeeplink(session.uri);
6743
+ } else {
6744
+ dispatch({ type: "NAVIGATE", step: "setup-status" });
6745
+ await authExecutor.executeSessionById(session.id);
6746
+ await reloadAccounts();
6747
+ dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
6748
+ }
6749
+ } catch (err) {
6750
+ captureException(err);
6751
+ const msg = err instanceof Error ? err.message : "Failed to authorize token";
6752
+ dispatch({ type: "SET_ERROR", error: msg });
6753
+ onError?.(msg);
6754
+ } finally {
6755
+ dispatch({ type: "SET_INCREASING_LIMIT", value: false });
6756
+ }
6757
+ }, [
6758
+ selectedAccountId,
6759
+ activeCredentialId,
6760
+ accounts,
6761
+ providers,
6762
+ apiBaseUrl,
6763
+ getAccessToken,
6764
+ authExecutor,
6765
+ useWalletConnectorProp,
6766
+ reloadAccounts,
6767
+ onError,
6768
+ dispatch,
6769
+ mobileSetupFlowRef,
6770
+ handlingMobileReturnRef,
6771
+ setupAccountIdRef,
6772
+ reauthSessionIdRef,
6773
+ reauthTokenRef
6774
+ ]);
6775
+ return {
6776
+ handleSelectProvider,
6777
+ handleContinueConnection,
6778
+ handleSelectAccount,
6779
+ handleIncreaseLimit,
6780
+ handleNavigateToTokenPicker,
6781
+ handleSelectAuthorizedToken,
6782
+ handleAuthorizeToken
6783
+ };
6784
+ }
6785
+ function useOneTapSetupHandlers(deps) {
6786
+ const {
6787
+ dispatch,
6788
+ getAccessToken,
6789
+ apiBaseUrl,
6790
+ authExecutor,
6791
+ selectSourceChainName,
6792
+ selectSourceTokenSymbol
6793
+ } = deps;
6794
+ const [savingOneTapLimit, setSavingOneTapLimit] = react.useState(false);
6795
+ const oneTapLimitSavedDuringSetupRef = react.useRef(false);
6717
6796
  const handleSetupOneTap = react.useCallback(async (limit) => {
6718
6797
  setSavingOneTapLimit(true);
6719
6798
  try {
@@ -6721,39 +6800,24 @@ function SwypePaymentInner({
6721
6800
  if (!token) throw new Error("Not authenticated");
6722
6801
  await updateUserConfig(apiBaseUrl, token, { defaultAllowance: limit });
6723
6802
  if (authExecutor.pendingSelectSource) {
6724
- authExecutor.resolveSelectSource({
6725
- chainName: selectSourceChainName,
6726
- tokenSymbol: selectSourceTokenSymbol
6727
- });
6803
+ const action = authExecutor.pendingSelectSource;
6804
+ const recommended = action.metadata?.recommended;
6805
+ let chainName;
6806
+ let tokenSymbol;
6807
+ if (selectSourceChainName && selectSourceTokenSymbol) {
6808
+ chainName = selectSourceChainName;
6809
+ tokenSymbol = selectSourceTokenSymbol;
6810
+ } else {
6811
+ const options = action.metadata?.options ?? [];
6812
+ const choices = buildSelectSourceChoices(options);
6813
+ chainName = recommended?.chainName ?? choices[0]?.chainName ?? "Base";
6814
+ tokenSymbol = recommended?.tokenSymbol ?? choices[0]?.tokens[0]?.tokenSymbol ?? "USDC";
6815
+ }
6816
+ oneTapLimitSavedDuringSetupRef.current = true;
6817
+ authExecutor.resolveSelectSource({ chainName, tokenSymbol });
6728
6818
  dispatch({ type: "NAVIGATE", step: "setup-status" });
6729
6819
  } else if (authExecutor.pendingOneTapSetup) {
6730
6820
  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
6821
  } else {
6758
6822
  dispatch({ type: "NAVIGATE", step: "deposit" });
6759
6823
  }
@@ -6766,76 +6830,118 @@ function SwypePaymentInner({
6766
6830
  } finally {
6767
6831
  setSavingOneTapLimit(false);
6768
6832
  }
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]);
6833
+ }, [getAccessToken, apiBaseUrl, authExecutor, dispatch, selectSourceChainName, selectSourceTokenSymbol]);
6834
+ return {
6835
+ handleSetupOneTap,
6836
+ savingOneTapLimit,
6837
+ oneTapLimitSavedDuringSetupRef
6838
+ };
6839
+ }
6840
+
6841
+ // src/dataLoading.ts
6842
+ function resolveDataLoadAction({
6843
+ authenticated,
6844
+ step,
6845
+ accountsCount,
6846
+ hasActiveCredential,
6847
+ loading
6848
+ }) {
6849
+ if (!authenticated || step === "login" || step === "otp-verify" || accountsCount > 0 || !hasActiveCredential) {
6850
+ return "reset";
6851
+ }
6852
+ if (loading) {
6853
+ return "wait";
6854
+ }
6855
+ return "load";
6856
+ }
6857
+
6858
+ // src/processingStatus.ts
6859
+ var PROCESSING_TIMEOUT_MS = 18e4;
6860
+ function resolvePreferredTransfer(polledTransfer, localTransfer) {
6861
+ return polledTransfer ?? localTransfer;
6862
+ }
6863
+ function getTransferStatus(polledTransfer, localTransfer) {
6864
+ const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
6865
+ return transfer?.status ?? "UNKNOWN";
6866
+ }
6867
+ function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
6868
+ if (!processingStartedAtMs) return false;
6869
+ return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
6870
+ }
6871
+ var STATUS_DISPLAY_LABELS = {
6872
+ CREATED: "created",
6873
+ AUTHORIZED: "authorized",
6874
+ SENDING: "sending",
6875
+ SENT: "confirming delivery",
6876
+ COMPLETED: "completed",
6877
+ FAILED: "failed"
6878
+ };
6879
+ function getStatusDisplayLabel(status) {
6880
+ return STATUS_DISPLAY_LABELS[status] ?? status;
6881
+ }
6882
+ function buildProcessingTimeoutMessage(status) {
6883
+ const label = getStatusDisplayLabel(status);
6884
+ return `Payment is taking longer than expected (status: ${label}). Please try again.`;
6885
+ }
6886
+
6887
+ // src/hooks/usePaymentEffects.ts
6888
+ function usePaymentEffects(deps) {
6889
+ const {
6890
+ state,
6891
+ dispatch,
6892
+ ready,
6893
+ authenticated,
6894
+ apiBaseUrl,
6895
+ depositAmount,
6896
+ useWalletConnectorProp,
6897
+ onComplete,
6898
+ onError,
6899
+ polling,
6900
+ authExecutor,
6901
+ reloadAccounts,
6902
+ activeOtpStatus,
6903
+ activeOtpErrorMessage,
6904
+ otpCode,
6905
+ handleVerifyLoginCode,
6906
+ setAuthInput,
6907
+ setOtpCode,
6908
+ mobileSetupFlowRef,
6909
+ handlingMobileReturnRef,
6910
+ setupAccountIdRef,
6911
+ reauthSessionIdRef,
6912
+ reauthTokenRef,
6913
+ loadingDataRef,
6914
+ pollingTransferIdRef,
6915
+ processingStartedAtRef,
6916
+ checkingPasskeyRef,
6917
+ pendingSelectSourceAction,
6918
+ selectSourceChoices,
6919
+ selectSourceRecommended,
6920
+ setSelectSourceChainName,
6921
+ setSelectSourceTokenSymbol,
6922
+ initializedSelectSourceActionRef,
6923
+ preSelectSourceStepRef,
6924
+ oneTapLimitSavedDuringSetupRef,
6925
+ handleAuthorizedMobileReturn
6926
+ } = deps;
6927
+ const { getAccessToken } = reactAuth.usePrivy();
6928
+ const onCompleteRef = react.useRef(onComplete);
6929
+ onCompleteRef.current = onComplete;
6930
+ const getAccessTokenRef = react.useRef(getAccessToken);
6931
+ getAccessTokenRef.current = getAccessToken;
6932
+ const pollingRef = react.useRef(polling);
6933
+ pollingRef.current = polling;
6934
+ const handleAuthorizedMobileReturnRef = react.useRef(handleAuthorizedMobileReturn);
6935
+ handleAuthorizedMobileReturnRef.current = handleAuthorizedMobileReturn;
6830
6936
  react.useEffect(() => {
6831
6937
  if (depositAmount != null) {
6832
6938
  dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
6833
6939
  }
6834
- }, [depositAmount]);
6940
+ }, [depositAmount, dispatch]);
6835
6941
  react.useEffect(() => {
6836
6942
  if (authenticated || state.step !== "otp-verify") return;
6837
6943
  if (activeOtpErrorMessage) dispatch({ type: "SET_ERROR", error: activeOtpErrorMessage });
6838
- }, [activeOtpErrorMessage, authenticated, state.step]);
6944
+ }, [activeOtpErrorMessage, authenticated, state.step, dispatch]);
6839
6945
  react.useEffect(() => {
6840
6946
  if (state.step === "otp-verify" && /^\d{6}$/.test(otpCode.trim()) && activeOtpStatus === "awaiting-code-input") {
6841
6947
  handleVerifyLoginCode();
@@ -7062,7 +7168,7 @@ function SwypePaymentInner({
7062
7168
  const load = async () => {
7063
7169
  dispatch({ type: "DATA_LOAD_START" });
7064
7170
  try {
7065
- const token = await getAccessToken();
7171
+ const token = await getAccessTokenRef.current();
7066
7172
  if (!token) throw new Error("Not authenticated");
7067
7173
  const [prov, accts, chn] = await Promise.all([
7068
7174
  fetchProviders(apiBaseUrl, token),
@@ -7116,7 +7222,6 @@ function SwypePaymentInner({
7116
7222
  state.step,
7117
7223
  state.accounts.length,
7118
7224
  apiBaseUrl,
7119
- getAccessToken,
7120
7225
  state.activeCredentialId,
7121
7226
  state.selectedAccountId,
7122
7227
  depositAmount
@@ -7131,7 +7236,7 @@ function SwypePaymentInner({
7131
7236
  clearMobileFlowState();
7132
7237
  dispatch({ type: "TRANSFER_FAILED", transfer: polling.transfer, error: "Transfer failed." });
7133
7238
  }
7134
- }, [polling.transfer, onComplete]);
7239
+ }, [polling.transfer, onComplete, dispatch]);
7135
7240
  react.useEffect(() => {
7136
7241
  if (state.step !== "processing") {
7137
7242
  processingStartedAtRef.current = null;
@@ -7157,7 +7262,7 @@ function SwypePaymentInner({
7157
7262
  }
7158
7263
  const timeoutId = window.setTimeout(handleTimeout, remainingMs);
7159
7264
  return () => window.clearTimeout(timeoutId);
7160
- }, [state.step, polling.transfer, state.transfer, polling.stopPolling, onError]);
7265
+ }, [state.step, polling.transfer, state.transfer, polling.stopPolling, onError, dispatch, processingStartedAtRef]);
7161
7266
  react.useEffect(() => {
7162
7267
  if (!state.mobileFlow) {
7163
7268
  handlingMobileReturnRef.current = false;
@@ -7167,7 +7272,7 @@ function SwypePaymentInner({
7167
7272
  const polledTransfer = polling.transfer;
7168
7273
  if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
7169
7274
  void handleAuthorizedMobileReturn(polledTransfer, mobileSetupFlowRef.current);
7170
- }, [state.mobileFlow, polling.transfer, handleAuthorizedMobileReturn]);
7275
+ }, [state.mobileFlow, polling.transfer, handleAuthorizedMobileReturn, handlingMobileReturnRef, mobileSetupFlowRef]);
7171
7276
  react.useEffect(() => {
7172
7277
  if (!state.mobileFlow || !mobileSetupFlowRef.current) return;
7173
7278
  if (state.step !== "open-wallet") return;
@@ -7238,7 +7343,18 @@ function SwypePaymentInner({
7238
7343
  window.clearInterval(intervalId);
7239
7344
  document.removeEventListener("visibilitychange", handleVisibility);
7240
7345
  };
7241
- }, [state.mobileFlow, state.step, state.activeCredentialId, apiBaseUrl, reloadAccounts]);
7346
+ }, [
7347
+ state.mobileFlow,
7348
+ state.step,
7349
+ state.activeCredentialId,
7350
+ apiBaseUrl,
7351
+ reloadAccounts,
7352
+ dispatch,
7353
+ mobileSetupFlowRef,
7354
+ setupAccountIdRef,
7355
+ reauthSessionIdRef,
7356
+ reauthTokenRef
7357
+ ]);
7242
7358
  react.useEffect(() => {
7243
7359
  if (!state.mobileFlow) return;
7244
7360
  if (handlingMobileReturnRef.current) return;
@@ -7252,7 +7368,14 @@ function SwypePaymentInner({
7252
7368
  };
7253
7369
  document.addEventListener("visibilitychange", handleVisibility);
7254
7370
  return () => document.removeEventListener("visibilitychange", handleVisibility);
7255
- }, [state.mobileFlow, state.transfer?.id, polling.isPolling, polling.startPolling]);
7371
+ }, [
7372
+ state.mobileFlow,
7373
+ state.transfer?.id,
7374
+ polling.isPolling,
7375
+ polling.startPolling,
7376
+ handlingMobileReturnRef,
7377
+ pollingTransferIdRef
7378
+ ]);
7256
7379
  react.useEffect(() => {
7257
7380
  if (!pendingSelectSourceAction) {
7258
7381
  initializedSelectSourceActionRef.current = null;
@@ -7261,20 +7384,6 @@ function SwypePaymentInner({
7261
7384
  return;
7262
7385
  }
7263
7386
  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
7387
  const hasRecommended = !!selectSourceRecommended && selectSourceChoices.some(
7279
7388
  (chain) => chain.chainName === selectSourceRecommended.chainName && chain.tokens.some((t) => t.tokenSymbol === selectSourceRecommended.tokenSymbol)
7280
7389
  );
@@ -7289,7 +7398,14 @@ function SwypePaymentInner({
7289
7398
  setSelectSourceTokenSymbol("USDC");
7290
7399
  }
7291
7400
  initializedSelectSourceActionRef.current = pendingSelectSourceAction.id;
7292
- }, [pendingSelectSourceAction, selectSourceChoices, selectSourceRecommended]);
7401
+ }, [
7402
+ pendingSelectSourceAction,
7403
+ selectSourceChoices,
7404
+ selectSourceRecommended,
7405
+ setSelectSourceChainName,
7406
+ setSelectSourceTokenSymbol,
7407
+ initializedSelectSourceActionRef
7408
+ ]);
7293
7409
  react.useEffect(() => {
7294
7410
  if (pendingSelectSourceAction && (state.step === "processing" || state.step === "open-wallet" || state.step === "setup-status")) {
7295
7411
  const isDesktop = shouldUseWalletConnector({
@@ -7297,9 +7413,8 @@ function SwypePaymentInner({
7297
7413
  userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
7298
7414
  });
7299
7415
  if (isDesktop && state.step === "setup-status") {
7300
- reloadAccounts().then(() => {
7301
- dispatch({ type: "NAVIGATE", step: "deposit" });
7302
- });
7416
+ preSelectSourceStepRef.current = state.step;
7417
+ dispatch({ type: "NAVIGATE", step: "setup" });
7303
7418
  return;
7304
7419
  }
7305
7420
  preSelectSourceStepRef.current = state.step;
@@ -7308,75 +7423,246 @@ function SwypePaymentInner({
7308
7423
  dispatch({ type: "NAVIGATE", step: preSelectSourceStepRef.current ?? "processing" });
7309
7424
  preSelectSourceStepRef.current = null;
7310
7425
  }
7311
- }, [pendingSelectSourceAction, state.step, authExecutor, useWalletConnectorProp, reloadAccounts]);
7426
+ }, [pendingSelectSourceAction, state.step, useWalletConnectorProp, dispatch, preSelectSourceStepRef, authExecutor]);
7312
7427
  const pendingOneTapSetupAction = authExecutor.pendingOneTapSetup;
7313
7428
  const preOneTapSetupStepRef = react.useRef(null);
7314
7429
  react.useEffect(() => {
7315
7430
  if (pendingOneTapSetupAction && state.step === "setup-status") {
7316
- preOneTapSetupStepRef.current = state.step;
7317
- reloadAccounts().then(() => {
7318
- dispatch({ type: "NAVIGATE", step: "setup" });
7319
- });
7431
+ if (oneTapLimitSavedDuringSetupRef.current) {
7432
+ oneTapLimitSavedDuringSetupRef.current = false;
7433
+ authExecutor.resolveOneTapSetup();
7434
+ } else {
7435
+ preOneTapSetupStepRef.current = state.step;
7436
+ reloadAccounts().then(() => {
7437
+ dispatch({ type: "NAVIGATE", step: "setup" });
7438
+ });
7439
+ }
7320
7440
  } else if (!pendingOneTapSetupAction && state.step === "setup" && preOneTapSetupStepRef.current) {
7321
7441
  dispatch({ type: "NAVIGATE", step: preOneTapSetupStepRef.current });
7322
7442
  preOneTapSetupStepRef.current = null;
7323
7443
  }
7324
- }, [pendingOneTapSetupAction, state.step, reloadAccounts]);
7444
+ }, [pendingOneTapSetupAction, state.step, reloadAccounts, authExecutor, dispatch, oneTapLimitSavedDuringSetupRef]);
7445
+ }
7446
+ function SwypePayment(props) {
7447
+ const resetKey = react.useRef(0);
7448
+ const handleBoundaryReset = react.useCallback(() => {
7449
+ resetKey.current += 1;
7450
+ }, []);
7451
+ return /* @__PURE__ */ jsxRuntime.jsx(PaymentErrorBoundary, { onReset: handleBoundaryReset, children: /* @__PURE__ */ jsxRuntime.jsx(SwypePaymentInner, { ...props }) }, resetKey.current);
7452
+ }
7453
+ function SwypePaymentInner({
7454
+ destination,
7455
+ onComplete,
7456
+ onError,
7457
+ useWalletConnector: useWalletConnectorProp,
7458
+ idempotencyKey,
7459
+ merchantAuthorization,
7460
+ merchantName,
7461
+ onBack,
7462
+ onDismiss,
7463
+ autoCloseSeconds
7464
+ }) {
7465
+ const { apiBaseUrl, depositAmount } = useSwypeConfig();
7466
+ const { ready, authenticated, logout, getAccessToken } = reactAuth.usePrivy();
7467
+ reactAuth.useLoginWithOAuth();
7468
+ const [state, dispatch] = react.useReducer(
7469
+ paymentReducer,
7470
+ {
7471
+ depositAmount,
7472
+ passkeyPopupNeeded: isSafari() && isInCrossOriginIframe(),
7473
+ activeCredentialId: typeof window === "undefined" ? null : window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY)
7474
+ },
7475
+ createInitialState
7476
+ );
7477
+ const authExecutor = useAuthorizationExecutor();
7478
+ const polling = useTransferPolling();
7479
+ const transferSigning = useTransferSigning();
7480
+ const mobileFlowRefs = {
7481
+ mobileSetupFlowRef: react.useRef(false),
7482
+ handlingMobileReturnRef: react.useRef(false),
7483
+ setupAccountIdRef: react.useRef(null),
7484
+ reauthSessionIdRef: react.useRef(null),
7485
+ reauthTokenRef: react.useRef(null),
7486
+ loadingDataRef: react.useRef(false)
7487
+ };
7488
+ const derived = useDerivedState(state);
7489
+ const auth = useAuthHandlers(dispatch, state.verificationTarget);
7490
+ const passkey = usePasskeyHandlers(
7491
+ dispatch,
7492
+ apiBaseUrl,
7493
+ state.accounts,
7494
+ state.knownCredentialIds,
7495
+ mobileFlowRefs.mobileSetupFlowRef
7496
+ );
7497
+ const transfer = useTransferHandlers({
7498
+ dispatch,
7499
+ getAccessToken,
7500
+ apiBaseUrl,
7501
+ depositAmount,
7502
+ destination,
7503
+ idempotencyKey,
7504
+ merchantAuthorization,
7505
+ onComplete,
7506
+ onError,
7507
+ polling,
7508
+ transferSigning,
7509
+ sourceType: derived.sourceType,
7510
+ sourceId: derived.sourceId,
7511
+ sourceTokenAddress: derived.selectedSource?.address,
7512
+ activeCredentialId: state.activeCredentialId,
7513
+ selectedAccountId: state.selectedAccountId,
7514
+ transfer: state.transfer,
7515
+ accounts: state.accounts
7516
+ });
7517
+ const mobileFlow = useMobileFlowHandlers(
7518
+ dispatch,
7519
+ polling,
7520
+ transfer.reloadAccounts,
7521
+ transfer.pollingTransferIdRef,
7522
+ state.transfer,
7523
+ mobileFlowRefs
7524
+ );
7525
+ const sourceSelection = useSourceSelectionHandlers(dispatch, authExecutor);
7526
+ const provider = useProviderHandlers({
7527
+ dispatch,
7528
+ getAccessToken,
7529
+ apiBaseUrl,
7530
+ depositAmount,
7531
+ useWalletConnectorProp,
7532
+ activeCredentialId: state.activeCredentialId,
7533
+ selectedAccountId: state.selectedAccountId,
7534
+ selectedWalletId: state.selectedWalletId,
7535
+ selectedTokenSymbol: state.selectedTokenSymbol,
7536
+ chains: state.chains,
7537
+ accounts: state.accounts,
7538
+ providers: state.providers,
7539
+ authExecutor,
7540
+ reloadAccounts: transfer.reloadAccounts,
7541
+ onError,
7542
+ mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
7543
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
7544
+ setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
7545
+ reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
7546
+ reauthTokenRef: mobileFlowRefs.reauthTokenRef
7547
+ });
7548
+ const oneTapSetup = useOneTapSetupHandlers({
7549
+ dispatch,
7550
+ getAccessToken,
7551
+ apiBaseUrl,
7552
+ authExecutor,
7553
+ selectSourceChainName: sourceSelection.selectSourceChainName,
7554
+ selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol
7555
+ });
7556
+ const handleNewPayment = react.useCallback(() => {
7557
+ clearMobileFlowState();
7558
+ transfer.processingStartedAtRef.current = null;
7559
+ transfer.pollingTransferIdRef.current = null;
7560
+ sourceSelection.preSelectSourceStepRef.current = null;
7561
+ oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
7562
+ dispatch({
7563
+ type: "NEW_PAYMENT",
7564
+ depositAmount,
7565
+ firstAccountId: state.accounts.length > 0 ? state.accounts[0].id : null
7566
+ });
7567
+ }, [depositAmount, state.accounts, transfer, sourceSelection, provider, oneTapSetup]);
7568
+ const handleLogout = react.useCallback(async () => {
7569
+ try {
7570
+ await logout();
7571
+ } catch {
7572
+ }
7573
+ clearMobileFlowState();
7574
+ if (typeof window !== "undefined") {
7575
+ window.localStorage.removeItem(ACTIVE_CREDENTIAL_STORAGE_KEY);
7576
+ }
7577
+ polling.stopPolling();
7578
+ sourceSelection.preSelectSourceStepRef.current = null;
7579
+ passkey.checkingPasskeyRef.current = false;
7580
+ oneTapSetup.oneTapLimitSavedDuringSetupRef.current = false;
7581
+ auth.setAuthInput("");
7582
+ auth.setOtpCode("");
7583
+ dispatch({ type: "LOGOUT", depositAmount });
7584
+ }, [logout, polling, depositAmount, auth, sourceSelection, provider, passkey, oneTapSetup]);
7585
+ usePaymentEffects({
7586
+ state,
7587
+ dispatch,
7588
+ ready,
7589
+ authenticated,
7590
+ apiBaseUrl,
7591
+ depositAmount,
7592
+ useWalletConnectorProp,
7593
+ onComplete,
7594
+ onError,
7595
+ polling,
7596
+ authExecutor,
7597
+ reloadAccounts: transfer.reloadAccounts,
7598
+ activeOtpStatus: auth.activeOtpStatus,
7599
+ activeOtpErrorMessage: auth.activeOtpErrorMessage,
7600
+ otpCode: auth.otpCode,
7601
+ handleVerifyLoginCode: auth.handleVerifyLoginCode,
7602
+ setAuthInput: auth.setAuthInput,
7603
+ setOtpCode: auth.setOtpCode,
7604
+ mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
7605
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef,
7606
+ setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
7607
+ reauthSessionIdRef: mobileFlowRefs.reauthSessionIdRef,
7608
+ reauthTokenRef: mobileFlowRefs.reauthTokenRef,
7609
+ loadingDataRef: mobileFlowRefs.loadingDataRef,
7610
+ pollingTransferIdRef: transfer.pollingTransferIdRef,
7611
+ processingStartedAtRef: transfer.processingStartedAtRef,
7612
+ checkingPasskeyRef: passkey.checkingPasskeyRef,
7613
+ pendingSelectSourceAction: sourceSelection.pendingSelectSourceAction,
7614
+ selectSourceChoices: sourceSelection.selectSourceChoices,
7615
+ selectSourceRecommended: sourceSelection.selectSourceRecommended,
7616
+ setSelectSourceChainName: sourceSelection.setSelectSourceChainName,
7617
+ setSelectSourceTokenSymbol: sourceSelection.setSelectSourceTokenSymbol,
7618
+ initializedSelectSourceActionRef: sourceSelection.initializedSelectSourceActionRef,
7619
+ preSelectSourceStepRef: sourceSelection.preSelectSourceStepRef,
7620
+ oneTapLimitSavedDuringSetupRef: oneTapSetup.oneTapLimitSavedDuringSetupRef,
7621
+ handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn
7622
+ });
7325
7623
  const handlers = react.useMemo(() => ({
7326
- onSendLoginCode: handleSendLoginCode,
7327
- onVerifyLoginCode: handleVerifyLoginCode,
7328
- onResendLoginCode: handleResendLoginCode,
7624
+ onSendLoginCode: auth.handleSendLoginCode,
7625
+ onVerifyLoginCode: auth.handleVerifyLoginCode,
7626
+ onResendLoginCode: auth.handleResendLoginCode,
7329
7627
  onBackFromOtp: () => {
7330
- setOtpCode("");
7628
+ auth.setOtpCode("");
7331
7629
  dispatch({ type: "BACK_TO_LOGIN" });
7332
7630
  },
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,
7631
+ onRegisterPasskey: passkey.handleRegisterPasskey,
7632
+ onCreatePasskeyViaPopup: passkey.handleCreatePasskeyViaPopup,
7633
+ onVerifyPasskeyViaPopup: passkey.handleVerifyPasskeyViaPopup,
7634
+ onSelectProvider: provider.handleSelectProvider,
7635
+ onContinueConnection: provider.handleContinueConnection,
7636
+ onSelectAccount: provider.handleSelectAccount,
7637
+ onPay: transfer.handlePay,
7638
+ onIncreaseLimit: provider.handleIncreaseLimit,
7639
+ onConfirmSign: transfer.handleConfirmSign,
7640
+ onRetryMobileStatus: mobileFlow.handleRetryMobileStatus,
7343
7641
  onLogout: handleLogout,
7344
7642
  onNewPayment: handleNewPayment,
7345
7643
  onNavigate: (step) => dispatch({ type: "NAVIGATE", step }),
7346
- onSetAuthInput: setAuthInput,
7644
+ onSetAuthInput: auth.setAuthInput,
7347
7645
  onSetOtpCode: (code) => {
7348
- setOtpCode(code);
7646
+ auth.setOtpCode(code);
7349
7647
  dispatch({ type: "SET_ERROR", error: null });
7350
7648
  },
7351
- onSelectSourceChainChange: handleSelectSourceChainChange,
7352
- onSetSelectSourceTokenSymbol: setSelectSourceTokenSymbol,
7353
- onConfirmSelectSource: handleConfirmSelectSource,
7354
- onSetupOneTap: handleSetupOneTap,
7355
- onSelectToken: handleNavigateToTokenPicker,
7356
- onSelectAuthorizedToken: handleSelectAuthorizedToken,
7357
- onAuthorizeToken: handleAuthorizeToken
7649
+ onSelectSourceChainChange: sourceSelection.handleSelectSourceChainChange,
7650
+ onSetSelectSourceTokenSymbol: sourceSelection.setSelectSourceTokenSymbol,
7651
+ onConfirmSelectSource: sourceSelection.handleConfirmSelectSource,
7652
+ onSetupOneTap: oneTapSetup.handleSetupOneTap,
7653
+ onSelectToken: provider.handleNavigateToTokenPicker,
7654
+ onSelectAuthorizedToken: provider.handleSelectAuthorizedToken,
7655
+ onAuthorizeToken: provider.handleAuthorizeToken
7358
7656
  }), [
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,
7657
+ auth,
7658
+ passkey,
7659
+ provider,
7660
+ transfer,
7661
+ mobileFlow,
7662
+ sourceSelection,
7663
+ oneTapSetup,
7372
7664
  handleLogout,
7373
- handleNewPayment,
7374
- handleSelectSourceChainChange,
7375
- handleConfirmSelectSource,
7376
- handleSetupOneTap,
7377
- handleNavigateToTokenPicker,
7378
- handleSelectAuthorizedToken,
7379
- handleAuthorizeToken
7665
+ handleNewPayment
7380
7666
  ]);
7381
7667
  return /* @__PURE__ */ jsxRuntime.jsx(
7382
7668
  StepRenderer,
@@ -7384,29 +7670,29 @@ function SwypePaymentInner({
7384
7670
  state,
7385
7671
  ready,
7386
7672
  authenticated,
7387
- activeOtpStatus,
7673
+ activeOtpStatus: auth.activeOtpStatus,
7388
7674
  pollingTransfer: polling.transfer,
7389
7675
  pollingError: polling.error,
7390
7676
  authExecutorError: authExecutor.error,
7391
7677
  transferSigningSigning: transferSigning.signing,
7392
7678
  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,
7679
+ pendingConnections: derived.pendingConnections,
7680
+ depositEligibleAccounts: derived.depositEligibleAccounts,
7681
+ sourceName: derived.sourceName,
7682
+ sourceAddress: derived.sourceAddress,
7683
+ sourceVerified: derived.sourceVerified,
7684
+ maxSourceBalance: derived.maxSourceBalance,
7685
+ tokenCount: derived.tokenCount,
7686
+ selectedAccount: derived.selectedAccount,
7687
+ selectedSource: derived.selectedSource,
7688
+ selectSourceChoices: sourceSelection.selectSourceChoices,
7689
+ selectSourceRecommended: sourceSelection.selectSourceRecommended,
7690
+ selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance,
7691
+ authInput: auth.authInput,
7692
+ otpCode: auth.otpCode,
7693
+ selectSourceChainName: sourceSelection.selectSourceChainName,
7694
+ selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
7695
+ savingOneTapLimit: oneTapSetup.savingOneTapLimit,
7410
7696
  merchantName,
7411
7697
  onBack,
7412
7698
  onDismiss,