@swype-org/react-sdk 0.1.132 → 0.1.142

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