@swype-org/react-sdk 0.1.133 → 0.1.143

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