@swype-org/react-sdk 0.1.301 → 0.1.329

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,7 +1,7 @@
1
1
  import { createContext, useRef, useState, useCallback, useMemo, useContext, useEffect, useReducer, Component } from 'react';
2
2
  import { PrivyProvider, usePrivy, useLoginWithOAuth, useLoginWithEmail, useLoginWithSms } from '@privy-io/react-auth';
3
3
  import { createConfig, http, WagmiProvider, useConfig, useConnect, useSwitchChain } from 'wagmi';
4
- import { mainnet, arbitrum, base, polygon, bsc } from 'wagmi/chains';
4
+ import { mainnet, arbitrum, base, polygon, bsc, baseSepolia } from 'wagmi/chains';
5
5
  import { injected } from 'wagmi/connectors';
6
6
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -75,14 +75,16 @@ function getTheme(mode) {
75
75
  }
76
76
  var BLINK_PRIVY_APP_ID = "cmlil87uv004n0ck0blwumwek";
77
77
  var wagmiConfig = createConfig({
78
- chains: [mainnet, arbitrum, base, polygon, bsc],
78
+ // baseSepolia: guest/testnet flows (e.g. smokebox e2e) call switchChain to the source chain from the API.
79
+ chains: [mainnet, arbitrum, base, polygon, bsc, baseSepolia],
79
80
  connectors: [injected({ shimDisconnect: true, unstable_shimAsyncInject: 2e3 })],
80
81
  transports: {
81
82
  [mainnet.id]: http(),
82
83
  [arbitrum.id]: http(),
83
84
  [base.id]: http(),
84
85
  [polygon.id]: http(),
85
- [bsc.id]: http()
86
+ [bsc.id]: http(),
87
+ [baseSepolia.id]: http()
86
88
  }
87
89
  });
88
90
  var BlinkContext = createContext(null);
@@ -492,6 +494,7 @@ __export(api_exports, {
492
494
  getGuestTransfer: () => getGuestTransfer,
493
495
  getTransferByGuestToken: () => getTransferByGuestToken,
494
496
  postGuestTransferFeeQuote: () => postGuestTransferFeeQuote,
497
+ postTransferQuote: () => postTransferQuote,
495
498
  putGuestTransferSender: () => putGuestTransferSender,
496
499
  registerPasskey: () => registerPasskey,
497
500
  reportActionCompletion: () => reportActionCompletion,
@@ -607,7 +610,8 @@ async function createTransfer(apiBaseUrl, token, params) {
607
610
  amount: {
608
611
  amount: params.amount,
609
612
  currency: params.currency ?? "USD"
610
- }
613
+ },
614
+ ...params.quoteId ? { quoteId: params.quoteId } : {}
611
615
  };
612
616
  const res = await fetch(`${apiBaseUrl}/v1/transfers`, {
613
617
  method: "POST",
@@ -620,6 +624,28 @@ async function createTransfer(apiBaseUrl, token, params) {
620
624
  if (!res.ok) await throwApiError(res);
621
625
  return await res.json();
622
626
  }
627
+ async function postTransferQuote(apiBaseUrl, bearerToken, params) {
628
+ const body = {
629
+ walletId: params.walletId,
630
+ ...params.sourceTokenAddress ? { sourceTokenAddress: params.sourceTokenAddress } : {},
631
+ destination: {
632
+ chainId: params.destination.chainId,
633
+ token: { address: params.destination.token.address },
634
+ address: params.destination.address
635
+ },
636
+ amount: params.amount
637
+ };
638
+ const res = await fetch(`${apiBaseUrl}/v1/transfers/quotes`, {
639
+ method: "POST",
640
+ headers: {
641
+ "Content-Type": "application/json",
642
+ Authorization: `Bearer ${bearerToken}`
643
+ },
644
+ body: JSON.stringify(body)
645
+ });
646
+ if (!res.ok) await throwApiError(res);
647
+ return await res.json();
648
+ }
623
649
  async function fetchMerchantPublicKey(apiBaseUrl, merchantId) {
624
650
  const res = await fetch(
625
651
  `${apiBaseUrl}/v1/merchants/${encodeURIComponent(merchantId)}/public-key`
@@ -961,6 +987,23 @@ function normalizeSignature(sig) {
961
987
  );
962
988
  }
963
989
 
990
+ // src/authorizationErrors.ts
991
+ var AuthorizationSessionCancelledError = class extends Error {
992
+ constructor() {
993
+ super("Authorization session cancelled");
994
+ this.name = "AuthorizationSessionCancelledError";
995
+ }
996
+ };
997
+ function isAuthorizationSessionCancelled(err) {
998
+ if (err instanceof AuthorizationSessionCancelledError) return true;
999
+ return typeof err === "object" && err !== null && "name" in err && err.name === "AuthorizationSessionCancelledError";
1000
+ }
1001
+ function isUserDismissedAuthorizationError(err) {
1002
+ if (!(err instanceof Error)) return false;
1003
+ const m = err.message.toLowerCase();
1004
+ return m.includes("authorization session aborted") || m.includes("aborted by user");
1005
+ }
1006
+
964
1007
  // src/hooks/authorizationExecutor.ts
965
1008
  var WALLET_CLIENT_MAX_ATTEMPTS = 25;
966
1009
  var WALLET_CLIENT_POLL_MS = 400;
@@ -1274,39 +1317,80 @@ function useAuthorizationExecutor(options) {
1274
1317
  const [error, setError] = useState(null);
1275
1318
  const [currentAction, setCurrentAction] = useState(null);
1276
1319
  const executingRef = useRef(false);
1320
+ const oneTapPauseActiveRef = useRef(false);
1321
+ const sessionStackRef = useRef([]);
1322
+ const [authorizationSessionStackDepth, setAuthorizationSessionStackDepth] = useState(0);
1277
1323
  const [pendingSelectSource, setPendingSelectSource] = useState(null);
1278
1324
  const selectSourceResolverRef = useRef(null);
1325
+ const selectSourceRejectRef = useRef(null);
1279
1326
  const sessionIdRef = useRef(null);
1280
1327
  const resolveSelectSource = useCallback((selection) => {
1281
1328
  if (selectSourceResolverRef.current) {
1282
1329
  selectSourceResolverRef.current(selection);
1283
1330
  selectSourceResolverRef.current = null;
1331
+ selectSourceRejectRef.current = null;
1284
1332
  setPendingSelectSource(null);
1285
1333
  }
1286
1334
  }, []);
1287
1335
  const waitForSelection = useCallback(
1288
- (action) => new Promise((resolve) => {
1289
- selectSourceResolverRef.current = resolve;
1336
+ (action) => new Promise((resolve, reject) => {
1337
+ selectSourceResolverRef.current = (selection) => {
1338
+ resolve(selection);
1339
+ selectSourceResolverRef.current = null;
1340
+ selectSourceRejectRef.current = null;
1341
+ };
1342
+ selectSourceRejectRef.current = reject;
1290
1343
  setPendingSelectSource(action);
1291
1344
  }),
1292
1345
  []
1293
1346
  );
1294
1347
  const [pendingOneTapSetup, setPendingOneTapSetup] = useState(null);
1295
1348
  const oneTapSetupResolverRef = useRef(null);
1349
+ const oneTapSetupRejectRef = useRef(null);
1296
1350
  const resolveOneTapSetup = useCallback(() => {
1297
1351
  if (oneTapSetupResolverRef.current) {
1298
1352
  oneTapSetupResolverRef.current();
1299
1353
  oneTapSetupResolverRef.current = null;
1354
+ oneTapSetupRejectRef.current = null;
1300
1355
  setPendingOneTapSetup(null);
1301
1356
  }
1302
1357
  }, []);
1303
1358
  const waitForOneTapSetup = useCallback(
1304
- (action) => new Promise((resolve) => {
1305
- oneTapSetupResolverRef.current = resolve;
1359
+ (action) => new Promise((resolve, reject) => {
1360
+ oneTapPauseActiveRef.current = true;
1361
+ oneTapSetupResolverRef.current = () => {
1362
+ oneTapPauseActiveRef.current = false;
1363
+ resolve();
1364
+ oneTapSetupResolverRef.current = null;
1365
+ oneTapSetupRejectRef.current = null;
1366
+ };
1367
+ oneTapSetupRejectRef.current = (reason) => {
1368
+ oneTapPauseActiveRef.current = false;
1369
+ reject(reason);
1370
+ };
1306
1371
  setPendingOneTapSetup(action);
1307
1372
  }),
1308
1373
  []
1309
1374
  );
1375
+ const cancelPendingExecution = useCallback(() => {
1376
+ if (selectSourceRejectRef.current) {
1377
+ selectSourceRejectRef.current(new AuthorizationSessionCancelledError());
1378
+ selectSourceRejectRef.current = null;
1379
+ selectSourceResolverRef.current = null;
1380
+ setPendingSelectSource(null);
1381
+ }
1382
+ if (oneTapSetupRejectRef.current) {
1383
+ oneTapPauseActiveRef.current = false;
1384
+ oneTapSetupRejectRef.current(new AuthorizationSessionCancelledError());
1385
+ oneTapSetupRejectRef.current = null;
1386
+ oneTapSetupResolverRef.current = null;
1387
+ setPendingOneTapSetup(null);
1388
+ }
1389
+ setError(null);
1390
+ setCurrentAction(null);
1391
+ setExecuting(false);
1392
+ executingRef.current = false;
1393
+ }, []);
1310
1394
  const dispatchAction = useCallback(
1311
1395
  async (action) => {
1312
1396
  setCurrentAction(action);
@@ -1341,20 +1425,26 @@ function useAuthorizationExecutor(options) {
1341
1425
  );
1342
1426
  const executeSessionById = useCallback(
1343
1427
  async (sessionId) => {
1344
- if (executingRef.current) return;
1345
- executingRef.current = true;
1346
1428
  if (!sessionId) {
1347
- executingRef.current = false;
1348
1429
  throw new Error("No authorization session id provided.");
1349
1430
  }
1350
1431
  if (!apiBaseUrl) {
1351
- executingRef.current = false;
1352
1432
  throw new Error("Missing apiBaseUrl. Provide useAuthorizationExecutor({ apiBaseUrl }) or wrap in <BlinkProvider>.");
1353
1433
  }
1354
- sessionIdRef.current = sessionId;
1355
- setExecuting(true);
1434
+ const allowNested = executingRef.current && oneTapPauseActiveRef.current;
1435
+ if (executingRef.current && !allowNested) {
1436
+ return;
1437
+ }
1438
+ const isNestedRun = allowNested;
1439
+ if (!isNestedRun) {
1440
+ executingRef.current = true;
1441
+ setExecuting(true);
1442
+ setResults([]);
1443
+ }
1356
1444
  setError(null);
1357
- setResults([]);
1445
+ sessionStackRef.current.push(sessionId);
1446
+ setAuthorizationSessionStackDepth(sessionStackRef.current.length);
1447
+ sessionIdRef.current = sessionId;
1358
1448
  try {
1359
1449
  let session = await fetchAuthorizationSession(apiBaseUrl, sessionId);
1360
1450
  const allResults = [];
@@ -1390,13 +1480,23 @@ function useAuthorizationExecutor(options) {
1390
1480
  pending = getPendingActions(session, completedIds);
1391
1481
  }
1392
1482
  } catch (err) {
1393
- const msg = err instanceof Error ? err.message : "Authorization failed";
1394
- setError(msg);
1483
+ if (isAuthorizationSessionCancelled(err)) {
1484
+ setError(null);
1485
+ } else {
1486
+ const msg = err instanceof Error ? err.message : "Authorization failed";
1487
+ setError(msg);
1488
+ }
1395
1489
  throw err;
1396
1490
  } finally {
1491
+ sessionStackRef.current.pop();
1492
+ const depth = sessionStackRef.current.length;
1493
+ setAuthorizationSessionStackDepth(depth);
1494
+ sessionIdRef.current = depth > 0 ? sessionStackRef.current[depth - 1] : null;
1397
1495
  setCurrentAction(null);
1398
- setExecuting(false);
1399
- executingRef.current = false;
1496
+ if (depth === 0) {
1497
+ setExecuting(false);
1498
+ executingRef.current = false;
1499
+ }
1400
1500
  }
1401
1501
  },
1402
1502
  [apiBaseUrl, dispatchAction]
@@ -1410,7 +1510,9 @@ function useAuthorizationExecutor(options) {
1410
1510
  resolveSelectSource,
1411
1511
  pendingOneTapSetup,
1412
1512
  resolveOneTapSetup,
1413
- executeSessionById
1513
+ executeSessionById,
1514
+ cancelPendingExecution,
1515
+ authorizationSessionStackDepth
1414
1516
  };
1415
1517
  }
1416
1518
  var TRANSFER_SIGN_MAX_POLLS = 60;
@@ -1640,6 +1742,37 @@ function getPreferredDepositWallet(account, transferAmount) {
1640
1742
  function getDepositEligibleAccounts(accounts) {
1641
1743
  return accounts.filter((account) => getAddressableWallets(account).length > 0);
1642
1744
  }
1745
+ function resolveDepositSelectionAfterRefresh(accounts, transferAmount, prev) {
1746
+ const { selectedAccountId, selectedWalletId, selectedTokenSymbol } = prev;
1747
+ if (selectedAccountId && selectedWalletId) {
1748
+ const acct = accounts.find((a) => a.id === selectedAccountId);
1749
+ const wallet = acct?.wallets.find((w) => w.id === selectedWalletId);
1750
+ if (wallet) {
1751
+ if (selectedTokenSymbol) {
1752
+ const hasToken = wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
1753
+ if (hasToken) {
1754
+ return {
1755
+ defaults: { accountId: selectedAccountId, walletId: selectedWalletId },
1756
+ resetSelectedTokenSymbol: false
1757
+ };
1758
+ }
1759
+ const fallback = resolveDepositSelection(accounts, transferAmount, selectedAccountId);
1760
+ return {
1761
+ defaults: fallback,
1762
+ resetSelectedTokenSymbol: true
1763
+ };
1764
+ }
1765
+ return {
1766
+ defaults: { accountId: selectedAccountId, walletId: selectedWalletId },
1767
+ resetSelectedTokenSymbol: false
1768
+ };
1769
+ }
1770
+ }
1771
+ return {
1772
+ defaults: resolveDepositSelection(accounts, transferAmount, selectedAccountId),
1773
+ resetSelectedTokenSymbol: false
1774
+ };
1775
+ }
1643
1776
  function resolveDepositSelection(accounts, transferAmount, selectedAccountId) {
1644
1777
  const eligibleAccounts = getDepositEligibleAccounts(accounts);
1645
1778
  if (eligibleAccounts.length === 0) return null;
@@ -1706,6 +1839,32 @@ function buildSelectSourceChoices(options) {
1706
1839
  tokens: chain.tokens.filter((t) => t.balance > 0).sort((a, b) => b.balance - a.balance)
1707
1840
  })).filter((chain) => chain.tokens.length > 0).sort((a, b) => b.balance - a.balance);
1708
1841
  }
1842
+ function resolveSelectSourceAvailableBalance(choices, options, chainName, tokenSymbol, recommended) {
1843
+ const chain = chainName.trim();
1844
+ const token = tokenSymbol.trim();
1845
+ if (chain && token) {
1846
+ const chainChoice = choices.find((c) => c.chainName === chain);
1847
+ const row = chainChoice?.tokens.find((t) => t.tokenSymbol === token);
1848
+ if (row !== void 0) return row.balance;
1849
+ const direct = options.find(
1850
+ (opt) => opt.chainName === chain && opt.tokenSymbol === token
1851
+ );
1852
+ if (direct) return parseRawBalance(direct.rawBalance, direct.decimals);
1853
+ return 0;
1854
+ }
1855
+ if (recommended) {
1856
+ const match = options.find(
1857
+ (opt) => opt.chainName === recommended.chainName && opt.tokenSymbol === recommended.tokenSymbol
1858
+ );
1859
+ if (match) return parseRawBalance(match.rawBalance, match.decimals);
1860
+ }
1861
+ let max = 0;
1862
+ for (const opt of options) {
1863
+ const bal = parseRawBalance(opt.rawBalance, opt.decimals);
1864
+ if (bal > max) max = bal;
1865
+ }
1866
+ return max;
1867
+ }
1709
1868
 
1710
1869
  // src/walletFlow.ts
1711
1870
  var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
@@ -1790,10 +1949,11 @@ function resolvePhase(state) {
1790
1949
  const walletPickerSwitchEligible = currentPhase.step === "wallet-picker" && currentPhase.reason === "switch" && !state.creatingTransfer && !(state.mobileFlow && state.deeplinkUri);
1791
1950
  const missingActivePasskeyCredential = state.passkeyConfigLoaded && !state.activeCredentialId;
1792
1951
  const shouldPromptPasskeyVerification = missingActivePasskeyCredential && state.knownCredentialIds.length > 0 && state.passkeyPopupNeeded;
1952
+ const guestPreauthClaimPending = state.guestPreauthAccountId != null && state.guestSessionToken != null && state.privyAuthenticated && state.activeCredentialId != null && !state.guestPreauthSetupCompletePending && !state.error;
1793
1953
  const branchGuestSetupComplete = state.guestPreauthSetupCompletePending && state.privyReady && state.privyAuthenticated;
1794
1954
  const branchKeepGuestPreauthPin = !branchGuestSetupComplete && guestPreauthPinsCurrentPhase;
1795
1955
  const branchGuestPostPayLogin = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && guestPostPayLogin;
1796
- const branchCompleted = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && transferCompleted && !state.verificationTarget && !needsPasskeyBootstrap;
1956
+ const branchCompleted = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && transferCompleted && !state.verificationTarget && !needsPasskeyBootstrap && !guestPreauthClaimPending && !state.loginRequested;
1797
1957
  const branchFailed = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && state.transfer?.status === "FAILED";
1798
1958
  const branchProcessing = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && (state.creatingTransfer || isTransferAwaitingCompletion(state.transfer));
1799
1959
  const branchStandardDesktopInlineOpenWallet = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && state.standardDesktopInlineOpenWallet && state.privyAuthenticated && state.activeCredentialId != null && state.selectedAccountId != null && !state.loginRequested && !state.guestPreauthorizing;
@@ -1809,7 +1969,7 @@ function resolvePhase(state) {
1809
1969
  const branchLoginRequested = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && state.loginRequested;
1810
1970
  const branchPasskeyVerify = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && shouldPromptPasskeyVerification;
1811
1971
  const branchPasskeyCreate = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && !branchPasskeyVerify && missingActivePasskeyCredential;
1812
- const branchGuestPreauthClaiming = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && !branchPasskeyVerify && !branchPasskeyCreate && state.guestPreauthAccountId != null && state.guestSessionToken != null && state.privyAuthenticated && state.activeCredentialId != null && !state.guestPreauthSetupCompletePending && !state.error;
1972
+ const branchGuestPreauthClaiming = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && !branchPasskeyVerify && !branchPasskeyCreate && guestPreauthClaimPending;
1813
1973
  const branchDataLoading = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && !branchPasskeyVerify && !branchPasskeyCreate && !branchGuestPreauthClaiming && state.loadingData && state.activeCredentialId != null && hasActiveWallet(state.accounts);
1814
1974
  const branchWalletPickerLink = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && !branchPasskeyVerify && !branchPasskeyCreate && !branchGuestPreauthClaiming && !branchDataLoading && state.activeCredentialId != null && !hasActiveWallet(state.accounts) && !state.mobileFlow;
1815
1975
  const branchDeposit = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && !branchPasskeyVerify && !branchPasskeyCreate && !branchGuestPreauthClaiming && !branchDataLoading && !branchWalletPickerLink && state.activeCredentialId != null && hasActiveWallet(state.accounts) && !state.loadingData;
@@ -1980,6 +2140,7 @@ function createInitialState(config) {
1980
2140
  selectedAccountId: null,
1981
2141
  selectedWalletId: null,
1982
2142
  selectedTokenSymbol: null,
2143
+ savedSelection: null,
1983
2144
  amount: config.depositAmount != null ? config.depositAmount.toString() : "",
1984
2145
  transfer: null,
1985
2146
  creatingTransfer: false,
@@ -2006,7 +2167,8 @@ function createInitialState(config) {
2006
2167
  standardDesktopInlineOpenWallet: false,
2007
2168
  guestPreauthSetupCompletePending: false,
2008
2169
  privyReady: false,
2009
- privyAuthenticated: false
2170
+ privyAuthenticated: false,
2171
+ lastResumedAt: 0
2010
2172
  };
2011
2173
  }
2012
2174
  function paymentReducer(state, action) {
@@ -2104,6 +2266,9 @@ function applyAction(state, action) {
2104
2266
  next.mobileFlow = false;
2105
2267
  next.deeplinkUri = null;
2106
2268
  }
2269
+ if (action.resetSelectedTokenSymbol) {
2270
+ next.selectedTokenSymbol = null;
2271
+ }
2107
2272
  return next;
2108
2273
  }
2109
2274
  case "DATA_LOAD_END":
@@ -2118,8 +2283,43 @@ function applyAction(state, action) {
2118
2283
  next.selectedAccountId = action.defaults.accountId;
2119
2284
  next.selectedWalletId = action.defaults.walletId;
2120
2285
  }
2286
+ if (action.resetSelectedTokenSymbol) {
2287
+ next.selectedTokenSymbol = null;
2288
+ }
2121
2289
  return next;
2122
2290
  }
2291
+ case "SAVE_SELECTION":
2292
+ return {
2293
+ ...state,
2294
+ savedSelection: {
2295
+ selectedProviderId: state.selectedProviderId,
2296
+ selectedAccountId: state.selectedAccountId,
2297
+ selectedWalletId: state.selectedWalletId,
2298
+ selectedTokenSymbol: state.selectedTokenSymbol
2299
+ }
2300
+ };
2301
+ case "RESTORE_SELECTION": {
2302
+ const snap = state.savedSelection;
2303
+ if (!snap) {
2304
+ return {
2305
+ ...state,
2306
+ error: null,
2307
+ increasingLimit: false
2308
+ };
2309
+ }
2310
+ return {
2311
+ ...state,
2312
+ selectedProviderId: snap.selectedProviderId,
2313
+ selectedAccountId: snap.selectedAccountId,
2314
+ selectedWalletId: snap.selectedWalletId,
2315
+ selectedTokenSymbol: snap.selectedTokenSymbol,
2316
+ savedSelection: null,
2317
+ error: null,
2318
+ increasingLimit: false
2319
+ };
2320
+ }
2321
+ case "DISCARD_SAVED_SELECTION":
2322
+ return { ...state, savedSelection: null };
2123
2323
  // ── Source selection ──────────────────────────────────────────
2124
2324
  case "SELECT_PROVIDER":
2125
2325
  return {
@@ -2414,7 +2614,8 @@ function applyAction(state, action) {
2414
2614
  oneTapLimitSavedDuringSetup: false,
2415
2615
  guestPreauthorizing: false,
2416
2616
  guestPreauthSetupCompletePending: false,
2417
- standardDesktopInlineOpenWallet: false
2617
+ standardDesktopInlineOpenWallet: false,
2618
+ savedSelection: null
2418
2619
  };
2419
2620
  case "LOGOUT":
2420
2621
  return {
@@ -2437,6 +2638,8 @@ function applyAction(state, action) {
2437
2638
  };
2438
2639
  case "SYNC_AMOUNT":
2439
2640
  return { ...state, amount: action.amount };
2641
+ case "PAGE_RESUMED":
2642
+ return { ...state, lastResumedAt: Date.now() };
2440
2643
  default:
2441
2644
  return state;
2442
2645
  }
@@ -2526,6 +2729,41 @@ function screenForPhase(phase) {
2526
2729
  return "guest-preauth-linking";
2527
2730
  }
2528
2731
  }
2732
+
2733
+ // src/depositTokenSelection.ts
2734
+ function tokenOptionsForLinkedAccount(selectedAccount, chains) {
2735
+ if (!selectedAccount) return [];
2736
+ return selectedAccount.wallets.flatMap((w) => {
2737
+ const evmChainId = chains.find((c) => c.name === w.chain.name)?.commonId ?? void 0;
2738
+ return w.sources.filter((s) => s.balance.total.amount > 0).map((s) => ({
2739
+ symbol: s.token.symbol,
2740
+ chainName: w.chain.name,
2741
+ balance: s.balance.available.amount,
2742
+ walletId: w.id,
2743
+ status: s.token.status,
2744
+ tokenAddress: s.address,
2745
+ chainId: evmChainId
2746
+ }));
2747
+ });
2748
+ }
2749
+ function handleInlineTokenSelection(handlers, chains, selectedAccount, symbol, chainName, walletId) {
2750
+ if (walletId && selectedAccount) {
2751
+ const wallet = selectedAccount.wallets.find((w) => w.id === walletId);
2752
+ if (wallet && wallet.chain.name === chainName) {
2753
+ const source = wallet.sources.find((s) => s.token.symbol === symbol);
2754
+ const evmChainId = chains.find((c) => c.name === chainName)?.commonId ?? void 0;
2755
+ const authorized = !source?.token.status || source.token.status === "AUTHORIZED";
2756
+ if (source && !authorized && source.address && evmChainId != null) {
2757
+ void handlers.onAuthorizeToken(walletId, source.address, evmChainId, symbol);
2758
+ return;
2759
+ }
2760
+ }
2761
+ handlers.onSelectAuthorizedToken(walletId, symbol);
2762
+ return;
2763
+ }
2764
+ handlers.onSelectSourceChainChange(chainName);
2765
+ handlers.onSetSelectSourceTokenSymbol(symbol);
2766
+ }
2529
2767
  var MUTED = "#7fa4b0";
2530
2768
  var LOGO_SIZE = 48;
2531
2769
  function BlinkLoadingScreen() {
@@ -4078,6 +4316,22 @@ var loginButtonStyle = (accentColor) => ({
4078
4316
  fontFamily: "inherit",
4079
4317
  textDecoration: "underline"
4080
4318
  });
4319
+
4320
+ // src/setupScreenSelection.ts
4321
+ function matchesSetupTokenSelection(opt, selectedTokenSymbol, selectedChainName, allOptions) {
4322
+ if (!selectedTokenSymbol || opt.symbol !== selectedTokenSymbol) {
4323
+ return false;
4324
+ }
4325
+ const chain = selectedChainName?.trim();
4326
+ if (chain) {
4327
+ return opt.chainName === chain;
4328
+ }
4329
+ const sameSymbol = allOptions.filter((o) => o.symbol === selectedTokenSymbol);
4330
+ if (sameSymbol.length === 1) {
4331
+ return opt.chainName === sameSymbol[0].chainName;
4332
+ }
4333
+ return false;
4334
+ }
4081
4335
  var DEFAULT_MAX = 1e7;
4082
4336
  var ABSOLUTE_MIN = 0.01;
4083
4337
  var PRESETS = [100, 250, 1e3];
@@ -4093,13 +4347,16 @@ function SetupScreen({
4093
4347
  loading,
4094
4348
  error,
4095
4349
  selectedTokenSymbol,
4350
+ selectedChainName,
4096
4351
  tokenOptions,
4097
4352
  onSelectToken
4098
4353
  }) {
4099
4354
  const { tokens } = useBlinkConfig();
4100
4355
  const effectiveMax = DEFAULT_MAX;
4101
4356
  const effectiveMin = Math.min(ABSOLUTE_MIN, effectiveMax);
4102
- const [limit, setLimit] = useState(() => Math.min(availableBalance, effectiveMax));
4357
+ const autoLimit = Math.min(Math.max(availableBalance, 0), effectiveMax);
4358
+ const [userChosenLimit, setUserChosenLimit] = useState(null);
4359
+ const limit = userChosenLimit ?? autoLimit;
4103
4360
  const [activePreset, setActivePreset] = useState(null);
4104
4361
  const [showAdvanced, setShowAdvanced] = useState(false);
4105
4362
  const [editing, setEditing] = useState(false);
@@ -4138,20 +4395,22 @@ function SetupScreen({
4138
4395
  const commitEdit = useCallback(() => {
4139
4396
  const parsed = parseFloat(inputValue);
4140
4397
  if (!isNaN(parsed)) {
4141
- setLimit(Math.min(effectiveMax, Math.max(effectiveMin, Math.round(parsed * 100) / 100)));
4398
+ setUserChosenLimit(
4399
+ Math.min(effectiveMax, Math.max(effectiveMin, Math.round(parsed * 100) / 100))
4400
+ );
4142
4401
  }
4143
4402
  setActivePreset(null);
4144
4403
  setEditing(false);
4145
4404
  }, [inputValue, effectiveMax, effectiveMin]);
4146
4405
  const selectPreset = (value) => {
4147
- setLimit(Math.min(value, effectiveMax));
4406
+ setUserChosenLimit(Math.min(value, effectiveMax));
4148
4407
  setActivePreset(value);
4149
4408
  };
4150
4409
  const selectMax = () => {
4151
- setLimit(Math.min(availableBalance, effectiveMax));
4410
+ setUserChosenLimit(autoLimit);
4152
4411
  setActivePreset("max");
4153
4412
  };
4154
- const selectedOption = tokenOptions?.find((o) => o.symbol === selectedTokenSymbol);
4413
+ const options = tokenOptions ?? [];
4155
4414
  return /* @__PURE__ */ jsxs(
4156
4415
  ScreenLayout,
4157
4416
  {
@@ -4184,12 +4443,7 @@ function SetupScreen({
4184
4443
  children: [
4185
4444
  /* @__PURE__ */ jsx("span", { style: tokenRowLabelStyle(tokens.text), children: "Token for one tap" }),
4186
4445
  /* @__PURE__ */ jsxs("div", { style: tokenRowRightStyle, children: [
4187
- selectedOption && /* @__PURE__ */ jsxs("span", { style: tokenRowSymbolStyle(tokens.textSecondary), children: [
4188
- selectedOption.symbol,
4189
- " \xB7 ",
4190
- selectedOption.chainName
4191
- ] }),
4192
- /* @__PURE__ */ jsx("div", { style: tokenIconWrapStyle, children: selectedTokenSymbol && TOKEN_LOGOS[selectedTokenSymbol] ? /* @__PURE__ */ jsx("img", { src: TOKEN_LOGOS[selectedTokenSymbol], alt: selectedTokenSymbol, width: 36, height: 36, style: { borderRadius: "50%" } }) : /* @__PURE__ */ jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", children: [
4446
+ /* @__PURE__ */ jsx("div", { style: tokenIconWrapStyle, children: selectedTokenSymbol && TOKEN_LOGOS[selectedTokenSymbol] ? /* @__PURE__ */ jsx("img", { src: TOKEN_LOGOS[selectedTokenSymbol], alt: "", width: 36, height: 36, style: { borderRadius: "50%" } }) : /* @__PURE__ */ jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", children: [
4193
4447
  /* @__PURE__ */ jsx("circle", { cx: "18", cy: "18", r: "18", fill: "#2DB84B" }),
4194
4448
  /* @__PURE__ */ jsx("text", { x: "18", y: "23", textAnchor: "middle", fontSize: "18", fill: "#fff", fontWeight: "700", children: "$" })
4195
4449
  ] }) }),
@@ -4201,14 +4455,21 @@ function SetupScreen({
4201
4455
  tokenDropdownOpen && tokenOptions && tokenOptions.length > 0 && /* @__PURE__ */ jsxs("div", { style: dropdownOuterStyle(tokens), children: [
4202
4456
  /* @__PURE__ */ jsx("div", { style: dropdownLabelStyle(tokens.textMuted), children: "Choose token" }),
4203
4457
  /* @__PURE__ */ jsx("div", { style: dropdownInnerStyle(tokens), children: tokenOptions.map((opt, index) => {
4204
- const selected = opt.symbol === selectedTokenSymbol;
4458
+ const selected = matchesSetupTokenSelection(
4459
+ opt,
4460
+ selectedTokenSymbol,
4461
+ selectedChainName,
4462
+ options
4463
+ );
4205
4464
  const isLast = index === tokenOptions.length - 1;
4465
+ const authorized = !opt.status || opt.status === "AUTHORIZED";
4206
4466
  return /* @__PURE__ */ jsxs(
4207
4467
  "button",
4208
4468
  {
4209
4469
  type: "button",
4210
4470
  onClick: () => handlePickToken(opt),
4211
4471
  style: dropdownRowStyle(tokens, selected, isLast),
4472
+ "aria-label": opt.balance != null ? `${opt.symbol} on ${opt.chainName}, $${opt.balance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} balance${!authorized ? ", not authorized" : ""}` : `${opt.symbol} on ${opt.chainName}${!authorized ? ", not authorized" : ""}`,
4212
4473
  children: [
4213
4474
  /* @__PURE__ */ jsxs("div", { style: dropdownRowLeftStyle, children: [
4214
4475
  /* @__PURE__ */ jsx("div", { style: dropdownTokenIconStyle(tokens, !!TOKEN_LOGOS[opt.symbol]), children: TOKEN_LOGOS[opt.symbol] ? /* @__PURE__ */ jsx("img", { src: TOKEN_LOGOS[opt.symbol], alt: opt.symbol, style: dropdownTokenLogoStyle }) : /* @__PURE__ */ jsx("span", { style: dropdownTokenFallbackStyle(tokens.textMuted), children: "$" }) }),
@@ -4218,9 +4479,12 @@ function SetupScreen({
4218
4479
  /* @__PURE__ */ jsx("span", { style: dropdownTokenDotStyle(tokens.textMuted), children: "\xB7" }),
4219
4480
  /* @__PURE__ */ jsx("span", { style: dropdownTokenChainStyle(tokens.textMuted), children: opt.chainName })
4220
4481
  ] }),
4221
- opt.balance != null && /* @__PURE__ */ jsxs("span", { style: dropdownTokenBalanceStyle(tokens.textMuted), children: [
4222
- "$",
4223
- opt.balance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
4482
+ /* @__PURE__ */ jsxs("div", { style: dropdownTokenBalanceRowStyle, children: [
4483
+ opt.balance != null && /* @__PURE__ */ jsxs("span", { style: dropdownTokenBalanceStyle(tokens.textMuted), children: [
4484
+ "$",
4485
+ opt.balance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
4486
+ ] }),
4487
+ !authorized && /* @__PURE__ */ jsx("span", { style: dropdownNotAuthorizedStyle(tokens.textMuted), children: "Not authorized" })
4224
4488
  ] })
4225
4489
  ] })
4226
4490
  ] }),
@@ -4340,11 +4604,6 @@ var tokenRowRightStyle = {
4340
4604
  gap: 6,
4341
4605
  flexShrink: 0
4342
4606
  };
4343
- var tokenRowSymbolStyle = (color) => ({
4344
- fontSize: "0.84rem",
4345
- fontWeight: 500,
4346
- color
4347
- });
4348
4607
  var advancedToggleStyle = (accentColor) => ({
4349
4608
  display: "block",
4350
4609
  margin: "4px auto 24px",
@@ -4492,10 +4751,20 @@ var dropdownTokenChainStyle = (color) => ({
4492
4751
  fontWeight: 400,
4493
4752
  color
4494
4753
  });
4754
+ var dropdownTokenBalanceRowStyle = {
4755
+ display: "flex",
4756
+ alignItems: "center",
4757
+ gap: 8
4758
+ };
4495
4759
  var dropdownTokenBalanceStyle = (color) => ({
4496
4760
  fontSize: "0.78rem",
4497
4761
  color
4498
4762
  });
4763
+ var dropdownNotAuthorizedStyle = (color) => ({
4764
+ fontSize: "0.7rem",
4765
+ fontWeight: 500,
4766
+ color
4767
+ });
4499
4768
  var dropdownRadioEmptyStyle = (borderColor) => ({
4500
4769
  width: 22,
4501
4770
  height: 22,
@@ -4610,6 +4879,38 @@ var waitHintStyle = (color) => ({
4610
4879
  color,
4611
4880
  margin: 0
4612
4881
  });
4882
+
4883
+ // src/feeFormat.ts
4884
+ function isPreciseMoneyNonPositive(fee) {
4885
+ const raw = fee.value.trim();
4886
+ if (!/^-?\d+(\.\d*)?$/.test(raw)) return false;
4887
+ const n = Number(raw);
4888
+ return Number.isFinite(n) && n <= 0;
4889
+ }
4890
+ function formatNonPositiveFeeDisplay(fee) {
4891
+ if (fee.currency === "USD") return "Under $0.01";
4892
+ return `Less than 0.01 ${fee.currency}`;
4893
+ }
4894
+ function formatPreciseMoneyForDisplay(fee) {
4895
+ const raw = fee.value.trim();
4896
+ if (fee.currency === "USD") {
4897
+ if (!/^\d+(\.\d*)?$/.test(raw)) {
4898
+ return `$${raw}`;
4899
+ }
4900
+ const [whole, frac = ""] = raw.split(".");
4901
+ const dec = `${frac}00`.slice(0, 2);
4902
+ const intFmt = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
4903
+ return `$${intFmt}.${dec}`;
4904
+ }
4905
+ return `${raw} ${fee.currency}`;
4906
+ }
4907
+ function formatUsdTwoDecimals(value) {
4908
+ const n = Number(value);
4909
+ return (Number.isFinite(n) ? n : 0).toLocaleString("en-US", {
4910
+ minimumFractionDigits: 2,
4911
+ maximumFractionDigits: 2
4912
+ });
4913
+ }
4613
4914
  function DepositScreen({
4614
4915
  merchantName,
4615
4916
  availableBalance,
@@ -4617,6 +4918,8 @@ function DepositScreen({
4617
4918
  tokenCount,
4618
4919
  initialAmount,
4619
4920
  minDepositFloor,
4921
+ quoteFee,
4922
+ quoteLoading,
4620
4923
  processing,
4621
4924
  error,
4622
4925
  onDeposit,
@@ -4631,13 +4934,19 @@ function DepositScreen({
4631
4934
  onAuthorizeAccount,
4632
4935
  onAddProvider,
4633
4936
  onSelectToken,
4937
+ tokenOptions,
4938
+ onPickToken,
4634
4939
  selectedSourceLabel,
4635
- selectedTokenSymbol
4940
+ selectedTokenSymbol,
4941
+ selectedChainName
4636
4942
  }) {
4637
4943
  const { tokens } = useBlinkConfig();
4638
4944
  const amount = initialAmount;
4639
4945
  const [accountPickerOpen, setAccountPickerOpen] = useState(false);
4946
+ const [tokenPickerOpen, setTokenPickerOpen] = useState(false);
4640
4947
  const pickerRef = useRef(null);
4948
+ const tokenPickerRef = useRef(null);
4949
+ const hasTokenDropdown = tokenOptions != null && tokenOptions.length > 0 && onPickToken != null;
4641
4950
  useEffect(() => {
4642
4951
  if (!accountPickerOpen) return;
4643
4952
  const handleClick = (e) => {
@@ -4648,6 +4957,28 @@ function DepositScreen({
4648
4957
  document.addEventListener("mousedown", handleClick);
4649
4958
  return () => document.removeEventListener("mousedown", handleClick);
4650
4959
  }, [accountPickerOpen]);
4960
+ useEffect(() => {
4961
+ if (!tokenPickerOpen) return;
4962
+ const handleMouseDown = (e) => {
4963
+ if (tokenPickerRef.current && !tokenPickerRef.current.contains(e.target)) {
4964
+ setTokenPickerOpen(false);
4965
+ }
4966
+ };
4967
+ document.addEventListener("mousedown", handleMouseDown);
4968
+ return () => document.removeEventListener("mousedown", handleMouseDown);
4969
+ }, [tokenPickerOpen]);
4970
+ const handleTokenBadgeClick = useCallback(() => {
4971
+ if (hasTokenDropdown) {
4972
+ setTokenPickerOpen((v) => !v);
4973
+ setAccountPickerOpen(false);
4974
+ } else {
4975
+ onSelectToken?.();
4976
+ }
4977
+ }, [hasTokenDropdown, onSelectToken]);
4978
+ const handlePickToken = useCallback((opt) => {
4979
+ onPickToken?.(opt.symbol, opt.chainName, opt.walletId);
4980
+ setTokenPickerOpen(false);
4981
+ }, [onPickToken]);
4651
4982
  const selectedAccount = accounts?.find((a) => a.id === selectedAccountId);
4652
4983
  const selectedProviderName = selectedAccount?.name ?? "Wallet";
4653
4984
  const selectedProviderLogo = KNOWN_LOGOS[selectedProviderName.toLowerCase()];
@@ -4659,36 +4990,95 @@ function DepositScreen({
4659
4990
  ScreenLayout,
4660
4991
  {
4661
4992
  footer: /* @__PURE__ */ jsxs(Fragment, { children: [
4662
- !accountPickerOpen && (exceedsLimit && onIncreaseLimit ? /* @__PURE__ */ jsx(PrimaryButton, { onClick: onIncreaseLimit, loading: increasingLimit, children: "Increase limit" }) : /* @__PURE__ */ jsx(PrimaryButton, { onClick: () => onDeposit(amount), disabled: !canDeposit, loading: processing, children: "Confirm" })),
4993
+ !accountPickerOpen && !tokenPickerOpen && (exceedsLimit && onIncreaseLimit ? /* @__PURE__ */ jsx(PrimaryButton, { onClick: onIncreaseLimit, loading: increasingLimit, children: "Increase limit" }) : /* @__PURE__ */ jsx(PrimaryButton, { onClick: () => onDeposit(amount), disabled: !canDeposit, loading: processing, children: "Confirm" })),
4663
4994
  /* @__PURE__ */ jsx(PoweredByFooter, {})
4664
4995
  ] }),
4665
4996
  children: [
4666
4997
  /* @__PURE__ */ jsx(ScreenHeader, { onBack, onLogout }),
4667
4998
  /* @__PURE__ */ jsxs("div", { ref: pickerRef, style: depositCardWrapStyle, children: [
4668
- /* @__PURE__ */ jsxs("div", { children: [
4999
+ /* @__PURE__ */ jsxs("div", { style: depositCardStyle(tokens), children: [
4669
5000
  /* @__PURE__ */ jsx("div", { style: depositLabelStyle(tokens.textMuted), children: "Depositing" }),
4670
- /* @__PURE__ */ jsxs("div", { style: amountRowStyle, children: [
4671
- /* @__PURE__ */ jsxs("div", { style: amountValueStyle(tokens.text), children: [
4672
- "$",
4673
- amount.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5001
+ /* @__PURE__ */ jsxs("div", { ref: tokenPickerRef, children: [
5002
+ /* @__PURE__ */ jsxs("div", { style: amountRowStyle, children: [
5003
+ /* @__PURE__ */ jsxs("div", { style: amountValueStyle(tokens.text), children: [
5004
+ "$",
5005
+ formatUsdTwoDecimals(amount)
5006
+ ] }),
5007
+ /* @__PURE__ */ jsxs(
5008
+ "button",
5009
+ {
5010
+ type: "button",
5011
+ "data-testid": "deposit-token-badge",
5012
+ onClick: handleTokenBadgeClick,
5013
+ style: tokenIconButtonStyle(hasTokenDropdown || !!onSelectToken),
5014
+ "aria-expanded": hasTokenDropdown ? tokenPickerOpen : void 0,
5015
+ "aria-haspopup": hasTokenDropdown ? "listbox" : void 0,
5016
+ children: [
5017
+ /* @__PURE__ */ jsx("div", { style: tokenIconWrapStyle2, children: selectedTokenSymbol && TOKEN_LOGOS[selectedTokenSymbol] ? /* @__PURE__ */ jsx("img", { src: TOKEN_LOGOS[selectedTokenSymbol], alt: selectedTokenSymbol, width: 36, height: 36, style: { borderRadius: "50%" } }) : /* @__PURE__ */ jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", children: [
5018
+ /* @__PURE__ */ jsx("circle", { cx: "18", cy: "18", r: "18", fill: "#2DB84B" }),
5019
+ /* @__PURE__ */ jsx("text", { x: "18", y: "23", textAnchor: "middle", fontSize: "18", fill: "#fff", fontWeight: "700", children: "$" })
5020
+ ] }) }),
5021
+ /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", style: { opacity: 0.5 }, children: tokenPickerOpen ? /* @__PURE__ */ jsx("path", { d: "M18 15l-6-6-6 6", stroke: tokens.textMuted, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) : /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6", stroke: tokens.textMuted, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
5022
+ ]
5023
+ }
5024
+ )
4674
5025
  ] }),
4675
- /* @__PURE__ */ jsxs(
4676
- "button",
4677
- {
4678
- type: "button",
4679
- onClick: onSelectToken,
4680
- style: tokenIconButtonStyle(!!onSelectToken),
4681
- children: [
4682
- /* @__PURE__ */ jsx("div", { style: tokenIconWrapStyle2, children: selectedTokenSymbol && TOKEN_LOGOS[selectedTokenSymbol] ? /* @__PURE__ */ jsx("img", { src: TOKEN_LOGOS[selectedTokenSymbol], alt: selectedTokenSymbol, width: 36, height: 36, style: { borderRadius: "50%" } }) : /* @__PURE__ */ jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", children: [
4683
- /* @__PURE__ */ jsx("circle", { cx: "18", cy: "18", r: "18", fill: "#2DB84B" }),
4684
- /* @__PURE__ */ jsx("text", { x: "18", y: "23", textAnchor: "middle", fontSize: "18", fill: "#fff", fontWeight: "700", children: "$" })
4685
- ] }) }),
4686
- /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", style: { opacity: 0.5 }, children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6", stroke: tokens.textMuted, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
4687
- ]
4688
- }
4689
- )
5026
+ tokenPickerOpen && tokenOptions && tokenOptions.length > 0 && /* @__PURE__ */ jsxs("div", { style: tokenDropdownStyle(tokens), children: [
5027
+ /* @__PURE__ */ jsx("div", { style: tokenDropdownLabelStyle(tokens.textMuted), children: "Choose token" }),
5028
+ /* @__PURE__ */ jsx("div", { style: tokenDropdownInnerStyle(tokens), children: tokenOptions.map((opt, index) => {
5029
+ const selected = opt.symbol === selectedTokenSymbol && (!selectedChainName || opt.chainName === selectedChainName);
5030
+ const isLast = index === tokenOptions.length - 1;
5031
+ const authorized = !opt.status || opt.status === "AUTHORIZED";
5032
+ return /* @__PURE__ */ jsxs(
5033
+ "button",
5034
+ {
5035
+ type: "button",
5036
+ onClick: () => handlePickToken(opt),
5037
+ style: tokenDropdownRowStyle(tokens, selected, isLast),
5038
+ "aria-label": opt.balance != null ? `${opt.symbol} on ${opt.chainName}, $${formatUsdTwoDecimals(opt.balance)} balance${!authorized ? ", not authorized" : ""}` : `${opt.symbol} on ${opt.chainName}${!authorized ? ", not authorized" : ""}`,
5039
+ children: [
5040
+ /* @__PURE__ */ jsxs("div", { style: tokenDropdownRowLeftStyle, children: [
5041
+ /* @__PURE__ */ jsx("div", { style: tokenDropdownIconStyle(tokens, !!TOKEN_LOGOS[opt.symbol]), children: TOKEN_LOGOS[opt.symbol] ? /* @__PURE__ */ jsx("img", { src: TOKEN_LOGOS[opt.symbol], alt: opt.symbol, style: tokenDropdownLogoStyle }) : /* @__PURE__ */ jsx("span", { style: tokenDropdownFallbackStyle(tokens.textMuted), children: "$" }) }),
5042
+ /* @__PURE__ */ jsxs("div", { style: tokenDropdownInfoStyle, children: [
5043
+ /* @__PURE__ */ jsxs("div", { style: tokenDropdownNameRowStyle, children: [
5044
+ /* @__PURE__ */ jsx("span", { style: tokenDropdownSymbolStyle(tokens.text), children: opt.symbol }),
5045
+ /* @__PURE__ */ jsx("span", { style: tokenDropdownDotStyle(tokens.textMuted), children: "\xB7" }),
5046
+ /* @__PURE__ */ jsx("span", { style: tokenDropdownChainStyle(tokens.textMuted), children: opt.chainName })
5047
+ ] }),
5048
+ /* @__PURE__ */ jsxs("div", { style: tokenDropdownBalanceRowStyle, children: [
5049
+ opt.balance != null && /* @__PURE__ */ jsxs("span", { style: tokenDropdownBalanceStyle(tokens.textMuted), children: [
5050
+ "$",
5051
+ formatUsdTwoDecimals(opt.balance)
5052
+ ] }),
5053
+ !authorized && /* @__PURE__ */ jsx("span", { style: tokenDropdownNotAuthStyle(tokens.textMuted), children: "Not authorized" })
5054
+ ] })
5055
+ ] })
5056
+ ] }),
5057
+ selected ? /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", children: [
5058
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "11", fill: tokens.success }),
5059
+ /* @__PURE__ */ jsx("path", { d: "M7 11l3 3 5-5", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
5060
+ ] }) : /* @__PURE__ */ jsx("div", { style: tokenDropdownRadioStyle(tokens.border) })
5061
+ ]
5062
+ },
5063
+ `${opt.chainName}-${opt.symbol}`
5064
+ );
5065
+ }) })
5066
+ ] })
4690
5067
  ] }),
4691
- !accountPickerOpen && /* @__PURE__ */ jsxs(
5068
+ !accountPickerOpen && !tokenPickerOpen && (() => {
5069
+ if (quoteLoading) {
5070
+ return /* @__PURE__ */ jsx("div", { style: feeRowContainerStyle, "aria-live": "polite", children: /* @__PURE__ */ jsx("span", { style: feeRowLabelStyle(tokens.textMuted), children: "Getting fee estimate\u2026" }) });
5071
+ }
5072
+ if (quoteFee) {
5073
+ const feeText = isPreciseMoneyNonPositive(quoteFee) ? formatNonPositiveFeeDisplay(quoteFee) : formatPreciseMoneyForDisplay(quoteFee);
5074
+ return /* @__PURE__ */ jsxs("div", { style: feeRowContainerStyle, "aria-live": "polite", children: [
5075
+ /* @__PURE__ */ jsx("span", { style: feeRowLabelStyle(tokens.textMuted), children: "Fee estimate: " }),
5076
+ /* @__PURE__ */ jsx("span", { style: feeRowAmountStyle(tokens.textSecondary), children: feeText })
5077
+ ] });
5078
+ }
5079
+ return null;
5080
+ })(),
5081
+ !accountPickerOpen && !tokenPickerOpen && /* @__PURE__ */ jsxs(
4692
5082
  "button",
4693
5083
  {
4694
5084
  type: "button",
@@ -4698,7 +5088,7 @@ function DepositScreen({
4698
5088
  selectedProviderLogo ? /* @__PURE__ */ jsx("img", { src: selectedProviderLogo, alt: selectedProviderName, style: providerLogoStyle }) : /* @__PURE__ */ jsx("div", { style: providerFallbackStyle(tokens.textMuted), children: selectedProviderName.charAt(0) }),
4699
5089
  /* @__PURE__ */ jsxs("span", { style: walletBalanceStyle(tokens.text), children: [
4700
5090
  "$",
4701
- totalAccountBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5091
+ formatUsdTwoDecimals(totalAccountBalance)
4702
5092
  ] }),
4703
5093
  /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", style: { opacity: 0.4 }, children: /* @__PURE__ */ jsx("path", { d: "M7 10l5-5 5 5M7 14l5 5 5-5", stroke: tokens.textMuted, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
4704
5094
  ]
@@ -4739,7 +5129,7 @@ function DepositScreen({
4739
5129
  /* @__PURE__ */ jsx("span", { style: accountAddressStyle(tokens.text), children: truncatedAddress ?? account.nickname ?? account.name }),
4740
5130
  /* @__PURE__ */ jsxs("span", { style: accountBalanceTextStyle(tokens.textMuted), children: [
4741
5131
  "$",
4742
- accountBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5132
+ formatUsdTwoDecimals(accountBalance)
4743
5133
  ] })
4744
5134
  ] })
4745
5135
  ] }),
@@ -4775,21 +5165,21 @@ function DepositScreen({
4775
5165
  " ",
4776
5166
  /* @__PURE__ */ jsxs("strong", { style: { color: tokens.text }, children: [
4777
5167
  "$",
4778
- remainingLimit.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5168
+ formatUsdTwoDecimals(remainingLimit)
4779
5169
  ] }),
4780
5170
  exceedsLimit && /* @__PURE__ */ jsxs("p", { style: limitExceededHintStyle(tokens.warning), children: [
4781
5171
  "Your deposit of $",
4782
- amount.toFixed(2),
5172
+ formatUsdTwoDecimals(amount),
4783
5173
  " exceeds your One-Tap limit of $",
4784
- remainingLimit?.toFixed(2) ?? "0.00",
5174
+ remainingLimit != null ? formatUsdTwoDecimals(remainingLimit) : "0.00",
4785
5175
  ". Increase your limit to continue."
4786
5176
  ] })
4787
5177
  ] }),
4788
5178
  !accountPickerOpen && isLowBalance && /* @__PURE__ */ jsxs(WarningBanner, { title: "Not enough funds", children: [
4789
5179
  "Your wallet balance is $",
4790
- availableBalance.toFixed(2),
5180
+ formatUsdTwoDecimals(availableBalance),
4791
5181
  " \u2014 you need at least $",
4792
- minDepositFloor.toFixed(2),
5182
+ formatUsdTwoDecimals(minDepositFloor),
4793
5183
  " to deposit via One-Tap."
4794
5184
  ] }),
4795
5185
  error && /* @__PURE__ */ jsx("div", { style: errorBannerStyle6(tokens), children: error })
@@ -4801,6 +5191,11 @@ var depositCardWrapStyle = {
4801
5191
  position: "relative",
4802
5192
  marginBottom: 20
4803
5193
  };
5194
+ var depositCardStyle = (tokens) => ({
5195
+ border: `1px solid ${tokens.border}`,
5196
+ borderRadius: tokens.radiusLg,
5197
+ padding: "16px 20px"
5198
+ });
4804
5199
  var depositLabelStyle = (color) => ({
4805
5200
  fontSize: "0.75rem",
4806
5201
  fontWeight: 500,
@@ -4834,6 +5229,116 @@ var tokenIconWrapStyle2 = {
4834
5229
  width: 36,
4835
5230
  height: 36
4836
5231
  };
5232
+ var tokenDropdownStyle = (t) => ({
5233
+ marginTop: 4,
5234
+ marginBottom: 12,
5235
+ background: t.bgCard,
5236
+ border: `1px solid ${t.border}`,
5237
+ borderRadius: t.radiusLg,
5238
+ boxShadow: t.shadowLg,
5239
+ padding: "12px 14px 14px"
5240
+ });
5241
+ var tokenDropdownLabelStyle = (color) => ({
5242
+ fontSize: "0.78rem",
5243
+ fontWeight: 500,
5244
+ color,
5245
+ marginBottom: 8
5246
+ });
5247
+ var tokenDropdownInnerStyle = (t) => ({
5248
+ background: t.bgInput,
5249
+ border: `1px solid ${t.border}`,
5250
+ borderRadius: t.radiusLg,
5251
+ overflow: "hidden"
5252
+ });
5253
+ var tokenDropdownRowStyle = (t, isSelected, isLast) => ({
5254
+ display: "flex",
5255
+ alignItems: "center",
5256
+ justifyContent: "space-between",
5257
+ width: "100%",
5258
+ padding: "14px 16px",
5259
+ background: isSelected ? `${t.accent}18` : "transparent",
5260
+ border: "none",
5261
+ borderBottom: isLast ? "none" : `1px solid ${t.border}`,
5262
+ cursor: "pointer",
5263
+ fontFamily: "inherit",
5264
+ textAlign: "left",
5265
+ outline: "none"
5266
+ });
5267
+ var tokenDropdownRowLeftStyle = {
5268
+ display: "flex",
5269
+ alignItems: "center",
5270
+ gap: 12,
5271
+ minWidth: 0,
5272
+ flex: 1
5273
+ };
5274
+ var tokenDropdownIconStyle = (t, hasLogo) => ({
5275
+ width: 36,
5276
+ height: 36,
5277
+ borderRadius: "50%",
5278
+ border: hasLogo ? "none" : `1.5px solid ${t.border}`,
5279
+ display: "flex",
5280
+ alignItems: "center",
5281
+ justifyContent: "center",
5282
+ flexShrink: 0,
5283
+ overflow: "hidden"
5284
+ });
5285
+ var tokenDropdownLogoStyle = {
5286
+ width: 36,
5287
+ height: 36,
5288
+ borderRadius: "50%",
5289
+ objectFit: "cover"
5290
+ };
5291
+ var tokenDropdownFallbackStyle = (color) => ({
5292
+ fontSize: "1rem",
5293
+ fontWeight: 700,
5294
+ color
5295
+ });
5296
+ var tokenDropdownInfoStyle = {
5297
+ display: "flex",
5298
+ flexDirection: "column",
5299
+ gap: 2,
5300
+ minWidth: 0
5301
+ };
5302
+ var tokenDropdownNameRowStyle = {
5303
+ display: "flex",
5304
+ alignItems: "center",
5305
+ gap: 4
5306
+ };
5307
+ var tokenDropdownSymbolStyle = (color) => ({
5308
+ fontSize: "0.92rem",
5309
+ fontWeight: 600,
5310
+ color
5311
+ });
5312
+ var tokenDropdownDotStyle = (color) => ({
5313
+ fontSize: "0.8rem",
5314
+ color
5315
+ });
5316
+ var tokenDropdownChainStyle = (color) => ({
5317
+ fontSize: "0.84rem",
5318
+ fontWeight: 400,
5319
+ color
5320
+ });
5321
+ var tokenDropdownBalanceRowStyle = {
5322
+ display: "flex",
5323
+ alignItems: "center",
5324
+ gap: 8
5325
+ };
5326
+ var tokenDropdownBalanceStyle = (color) => ({
5327
+ fontSize: "0.78rem",
5328
+ color
5329
+ });
5330
+ var tokenDropdownNotAuthStyle = (color) => ({
5331
+ fontSize: "0.7rem",
5332
+ fontWeight: 500,
5333
+ color
5334
+ });
5335
+ var tokenDropdownRadioStyle = (borderColor) => ({
5336
+ width: 22,
5337
+ height: 22,
5338
+ borderRadius: "50%",
5339
+ border: `2px solid ${borderColor}`,
5340
+ flexShrink: 0
5341
+ });
4837
5342
  var walletBalanceRowStyle = {
4838
5343
  display: "flex",
4839
5344
  alignItems: "center",
@@ -4995,9 +5500,20 @@ var limitExceededHintStyle = (color) => ({
4995
5500
  margin: "12px 0 2px",
4996
5501
  lineHeight: 1.5
4997
5502
  });
5503
+ var feeRowContainerStyle = {
5504
+ fontSize: "0.84rem",
5505
+ fontWeight: 500,
5506
+ marginTop: 6,
5507
+ marginBottom: 4
5508
+ };
5509
+ var feeRowLabelStyle = (color) => ({ color });
5510
+ var feeRowAmountStyle = (color) => ({
5511
+ color,
5512
+ fontVariantNumeric: "tabular-nums"
5513
+ });
4998
5514
  function SuccessScreen({
4999
5515
  amount,
5000
- currency,
5516
+ currency: _currency,
5001
5517
  succeeded,
5002
5518
  error,
5003
5519
  merchantName,
@@ -5010,22 +5526,30 @@ function SuccessScreen({
5010
5526
  onPreauthorize
5011
5527
  }) {
5012
5528
  const { tokens } = useBlinkConfig();
5529
+ const isGuestDepositSuccess = succeeded && onPreauthorize != null;
5013
5530
  return /* @__PURE__ */ jsxs(
5014
5531
  ScreenLayout,
5015
5532
  {
5016
5533
  footer: /* @__PURE__ */ jsxs(Fragment, { children: [
5017
- succeeded && onPreauthorize ? /* @__PURE__ */ jsxs(Fragment, { children: [
5018
- /* @__PURE__ */ jsx(PrimaryButton, { onClick: onPreauthorize, children: "Preauthorize future transfers" }),
5019
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onDone, style: skipButtonStyle(tokens.textMuted), children: "Skip this time" })
5534
+ isGuestDepositSuccess ? /* @__PURE__ */ jsxs(Fragment, { children: [
5535
+ /* @__PURE__ */ jsx(PrimaryButton, { onClick: onPreauthorize, children: "Set up one tap" }),
5536
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onDone, style: skipButtonStyle(tokens.textMuted), children: "Return to app" })
5020
5537
  ] }) : /* @__PURE__ */ jsx(PrimaryButton, { onClick: onDone, children: succeeded ? "Done" : "Try again" }),
5021
5538
  onManageAccount && /* @__PURE__ */ jsx("button", { type: "button", onClick: onManageAccount, style: manageStyle(tokens.textMuted), children: "Manage Blink account \u2192" }),
5022
5539
  /* @__PURE__ */ jsx(PoweredByFooter, {})
5023
5540
  ] }),
5024
5541
  children: [
5025
5542
  /* @__PURE__ */ jsx(ScreenHeader, { onLogout }),
5026
- /* @__PURE__ */ jsxs("div", { style: contentStyle6, children: [
5027
- succeeded ? /* @__PURE__ */ jsxs(Fragment, { children: [
5028
- /* @__PURE__ */ jsx(IconCircle, { variant: "success", size: 64, children: /* @__PURE__ */ jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z", fill: tokens.success }) }) }),
5543
+ /* @__PURE__ */ jsxs("div", { style: screenContentStyle, children: [
5544
+ isGuestDepositSuccess ? /* @__PURE__ */ jsxs("div", { style: contentStyleCompact, children: [
5545
+ /* @__PURE__ */ jsxs("h2", { style: headingStyle7(tokens.text), children: [
5546
+ "$",
5547
+ amount.toFixed(2),
5548
+ " deposited"
5549
+ ] }),
5550
+ /* @__PURE__ */ jsx("p", { style: { ...subtitleStyle7(tokens.text), fontWeight: 600, margin: "0 0 8px" }, children: "Next time, do it in one tap" }),
5551
+ /* @__PURE__ */ jsx("p", { style: subtitleStyle7(tokens.textSecondary), children: "Set up one tap for this wallet and skip the extra steps." })
5552
+ ] }) : succeeded ? /* @__PURE__ */ jsxs("div", { style: contentStyleCompact, children: [
5029
5553
  /* @__PURE__ */ jsxs("h2", { style: headingStyle7(tokens.text), children: [
5030
5554
  "$",
5031
5555
  amount.toFixed(2),
@@ -5037,10 +5561,10 @@ function SuccessScreen({
5037
5561
  ] })
5038
5562
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5039
5563
  /* @__PURE__ */ jsx(IconCircle, { variant: "error", size: 64, children: /* @__PURE__ */ jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z", fill: tokens.error }) }) }),
5040
- /* @__PURE__ */ jsx("h2", { style: headingStyle7(tokens.text), children: "Transfer failed" }),
5041
- error && /* @__PURE__ */ jsx("p", { style: subtitleStyle7(tokens.error), children: error })
5564
+ /* @__PURE__ */ jsx("h2", { style: failureHeadingStyle(tokens.text), children: "Transfer failed" }),
5565
+ error && /* @__PURE__ */ jsx("p", { style: failureSubtitleStyle(tokens.error), children: error })
5042
5566
  ] }),
5043
- /* @__PURE__ */ jsxs("div", { style: summaryCardStyle(tokens), children: [
5567
+ !isGuestDepositSuccess && /* @__PURE__ */ jsxs("div", { style: summaryCardStyle(tokens), children: [
5044
5568
  sourceName && /* @__PURE__ */ jsxs("div", { style: summaryRowStyle, children: [
5045
5569
  /* @__PURE__ */ jsx("span", { style: summaryLabelStyle(tokens.textMuted), children: "From" }),
5046
5570
  /* @__PURE__ */ jsx("span", { style: summaryValueStyle(tokens.text), children: sourceName })
@@ -5057,7 +5581,7 @@ function SuccessScreen({
5057
5581
  ] })
5058
5582
  ] })
5059
5583
  ] }),
5060
- succeeded && onIncreaseLimits && /* @__PURE__ */ jsxs("div", { style: upsellCardStyle(tokens), children: [
5584
+ succeeded && onIncreaseLimits && !isGuestDepositSuccess && /* @__PURE__ */ jsxs("div", { style: upsellCardStyle(tokens), children: [
5061
5585
  /* @__PURE__ */ jsxs("div", { style: upsellHeaderStyle, children: [
5062
5586
  /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { marginRight: 6 }, children: /* @__PURE__ */ jsx("path", { d: "M7 14l5-5 5 5", stroke: tokens.accent, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
5063
5587
  /* @__PURE__ */ jsx("strong", { children: "Want higher limits?" })
@@ -5070,21 +5594,42 @@ function SuccessScreen({
5070
5594
  }
5071
5595
  );
5072
5596
  }
5073
- var contentStyle6 = {
5597
+ var screenContentStyle = {
5074
5598
  flex: 1,
5075
5599
  display: "flex",
5076
5600
  flexDirection: "column",
5077
5601
  alignItems: "center",
5078
5602
  paddingTop: 16
5079
5603
  };
5604
+ var contentStyleCompact = {
5605
+ textAlign: "center",
5606
+ display: "flex",
5607
+ flexDirection: "column",
5608
+ alignItems: "center",
5609
+ width: "100%"
5610
+ };
5080
5611
  var headingStyle7 = (color) => ({
5612
+ fontSize: "1.45rem",
5613
+ fontWeight: 700,
5614
+ letterSpacing: "-0.02em",
5615
+ color,
5616
+ margin: "20px 0 8px"
5617
+ });
5618
+ var subtitleStyle7 = (color) => ({
5619
+ fontSize: "0.9rem",
5620
+ color,
5621
+ margin: "0 0 28px",
5622
+ lineHeight: 1.5,
5623
+ maxWidth: 280
5624
+ });
5625
+ var failureHeadingStyle = (color) => ({
5081
5626
  fontSize: "1.5rem",
5082
5627
  fontWeight: 700,
5083
5628
  letterSpacing: "-0.02em",
5084
5629
  color,
5085
5630
  margin: "20px 0 4px"
5086
5631
  });
5087
- var subtitleStyle7 = (color) => ({
5632
+ var failureSubtitleStyle = (color) => ({
5088
5633
  fontSize: "0.9rem",
5089
5634
  color,
5090
5635
  margin: "0 0 20px"
@@ -5600,7 +6145,7 @@ function TransferStatusScreen({
5600
6145
  const steps = buildSteps(phase);
5601
6146
  return /* @__PURE__ */ jsxs(ScreenLayout, { footer: /* @__PURE__ */ jsx(PoweredByFooter, {}), children: [
5602
6147
  /* @__PURE__ */ jsx(ScreenHeader, { onLogout }),
5603
- /* @__PURE__ */ jsxs("div", { style: contentStyle7, children: [
6148
+ /* @__PURE__ */ jsxs("div", { style: contentStyle6, children: [
5604
6149
  /* @__PURE__ */ jsx(Spinner, { size: 64 }),
5605
6150
  /* @__PURE__ */ jsx("h2", { style: headingStyle9(tokens.text), children: "Depositing your money..." }),
5606
6151
  error && /* @__PURE__ */ jsx("div", { style: errorBannerStyle7(tokens), children: error }),
@@ -5608,7 +6153,7 @@ function TransferStatusScreen({
5608
6153
  ] })
5609
6154
  ] });
5610
6155
  }
5611
- var contentStyle7 = {
6156
+ var contentStyle6 = {
5612
6157
  flex: 1,
5613
6158
  display: "flex",
5614
6159
  flexDirection: "column",
@@ -5678,7 +6223,7 @@ function OpenWalletScreen({
5678
6223
  ] }),
5679
6224
  children: [
5680
6225
  /* @__PURE__ */ jsx(ScreenHeader, { onLogout }),
5681
- /* @__PURE__ */ jsxs("div", { style: contentStyle8, children: [
6226
+ /* @__PURE__ */ jsxs("div", { style: contentStyle7, children: [
5682
6227
  /* @__PURE__ */ jsx("div", { style: logoCircleStyle(tokens.bgInput), children: logoSrc ? /* @__PURE__ */ jsx("img", { src: logoSrc, alt: displayName, style: logoStyle2 }) : /* @__PURE__ */ jsx(Spinner, { size: 32 }) }),
5683
6228
  /* @__PURE__ */ jsxs("h2", { style: headingStyle10(tokens.text), children: [
5684
6229
  "Setting up ",
@@ -5711,7 +6256,7 @@ function OpenWalletScreen({
5711
6256
  ] }),
5712
6257
  children: [
5713
6258
  /* @__PURE__ */ jsx(ScreenHeader, { onBack, onLogout }),
5714
- /* @__PURE__ */ jsxs("div", { style: contentStyle8, children: [
6259
+ /* @__PURE__ */ jsxs("div", { style: contentStyle7, children: [
5715
6260
  /* @__PURE__ */ jsx("div", { style: logoCircleStyle(tokens.bgInput), children: logoSrc ? /* @__PURE__ */ jsx("img", { src: logoSrc, alt: displayName, style: logoStyle2 }) : /* @__PURE__ */ jsx(Spinner, { size: 32 }) }),
5716
6261
  /* @__PURE__ */ jsx("h2", { style: headingStyle10(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
5717
6262
  /* @__PURE__ */ jsx("p", { style: subtitleStyle10(tokens.textSecondary), children: loading ? "Creating transfer and preparing your wallet link..." : `Continue in ${displayName} to authorize this connection.` }),
@@ -5724,7 +6269,7 @@ function OpenWalletScreen({
5724
6269
  }
5725
6270
  );
5726
6271
  }
5727
- var contentStyle8 = {
6272
+ var contentStyle7 = {
5728
6273
  flex: 1,
5729
6274
  display: "flex",
5730
6275
  flexDirection: "column",
@@ -5810,7 +6355,7 @@ function ConfirmSignScreen({
5810
6355
  ] }),
5811
6356
  children: [
5812
6357
  /* @__PURE__ */ jsx(ScreenHeader, { onLogout }),
5813
- /* @__PURE__ */ jsxs("div", { style: contentStyle9, children: [
6358
+ /* @__PURE__ */ jsxs("div", { style: contentStyle8, children: [
5814
6359
  logoSrc ? /* @__PURE__ */ jsx("img", { src: logoSrc, alt: displayName, style: logoStyle3 }) : /* @__PURE__ */ jsx(Spinner, { size: 48 }),
5815
6360
  /* @__PURE__ */ jsx("h2", { style: headingStyle11(tokens.text), children: "Wallet authorized" }),
5816
6361
  /* @__PURE__ */ jsxs("p", { style: subtitleStyle11(tokens.textSecondary), children: [
@@ -5826,7 +6371,7 @@ function ConfirmSignScreen({
5826
6371
  }
5827
6372
  );
5828
6373
  }
5829
- var contentStyle9 = {
6374
+ var contentStyle8 = {
5830
6375
  flex: 1,
5831
6376
  display: "flex",
5832
6377
  flexDirection: "column",
@@ -6121,29 +6666,6 @@ var selectCircleSelectedStyle = (color) => ({
6121
6666
  function entryKey(entry) {
6122
6667
  return `${entry.sourceChainId}-${entry.tokenAddress.toLowerCase()}`;
6123
6668
  }
6124
- function isPreciseMoneyNonPositive(fee) {
6125
- const raw = fee.value.trim();
6126
- if (!/^-?\d+(\.\d*)?$/.test(raw)) return false;
6127
- const n = Number(raw);
6128
- return Number.isFinite(n) && n <= 0;
6129
- }
6130
- function formatNonPositiveFeeDisplay(fee) {
6131
- if (fee.currency === "USD") return "Under $0.01";
6132
- return `Less than 0.01 ${fee.currency}`;
6133
- }
6134
- function formatPreciseMoneyForDisplay(fee) {
6135
- const raw = fee.value.trim();
6136
- if (fee.currency === "USD") {
6137
- if (!/^\d+(\.\d*)?$/.test(raw)) {
6138
- return `$${raw}`;
6139
- }
6140
- const [whole, frac = ""] = raw.split(".");
6141
- const dec = `${frac}00`.slice(0, 2);
6142
- const intFmt = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
6143
- return `$${intFmt}.${dec}`;
6144
- }
6145
- return `${raw} ${fee.currency}`;
6146
- }
6147
6669
  function GuestTokenPickerScreen({
6148
6670
  entries,
6149
6671
  loading,
@@ -6183,13 +6705,13 @@ function GuestTokenPickerScreen({
6183
6705
  const canConfirm = Boolean(quoteFee && pendingEntry && !quoteLoading);
6184
6706
  const feeLine = (() => {
6185
6707
  if (quoteLoading && pendingEntry) {
6186
- return /* @__PURE__ */ jsx("div", { style: feeRowContainerStyle, "aria-live": "polite", children: /* @__PURE__ */ jsx("span", { style: feeRowLabelStyle(t.textMuted), children: "Getting fee estimate\u2026" }) });
6708
+ return /* @__PURE__ */ jsx("div", { style: feeRowContainerStyle2, "aria-live": "polite", children: /* @__PURE__ */ jsx("span", { style: feeRowLabelStyle2(t.textMuted), children: "Getting fee estimate\u2026" }) });
6187
6709
  }
6188
6710
  if (quoteFee) {
6189
6711
  const feeText = isPreciseMoneyNonPositive(quoteFee) ? formatNonPositiveFeeDisplay(quoteFee) : formatPreciseMoneyForDisplay(quoteFee);
6190
- return /* @__PURE__ */ jsxs("div", { style: feeRowContainerStyle, "aria-live": "polite", children: [
6191
- /* @__PURE__ */ jsx("span", { style: feeRowLabelStyle(t.textMuted), children: "Fee estimate: " }),
6192
- /* @__PURE__ */ jsx("span", { style: feeRowAmountStyle(t.textSecondary), children: feeText })
6712
+ return /* @__PURE__ */ jsxs("div", { style: feeRowContainerStyle2, "aria-live": "polite", children: [
6713
+ /* @__PURE__ */ jsx("span", { style: feeRowLabelStyle2(t.textMuted), children: "Fee estimate: " }),
6714
+ /* @__PURE__ */ jsx("span", { style: feeRowAmountStyle2(t.textSecondary), children: feeText })
6193
6715
  ] });
6194
6716
  }
6195
6717
  return null;
@@ -6211,7 +6733,7 @@ function GuestTokenPickerScreen({
6211
6733
  depositAmount != null && /* @__PURE__ */ jsxs("div", { style: depositAmountStyle2(t.text), children: [
6212
6734
  "$",
6213
6735
  depositAmount.toLocaleString("en-US", {
6214
- minimumFractionDigits: 0,
6736
+ minimumFractionDigits: 2,
6215
6737
  maximumFractionDigits: 2
6216
6738
  })
6217
6739
  ] }),
@@ -6293,7 +6815,7 @@ function GuestTokenPickerScreen({
6293
6815
  ] }),
6294
6816
  tokenListOpen && entries.length > 0 && /* @__PURE__ */ jsxs("div", { style: tokenDropdownOuterStyle(t), children: [
6295
6817
  /* @__PURE__ */ jsx("div", { style: accountDropdownLabelStyle2(t.textMuted), children: "Choose token" }),
6296
- /* @__PURE__ */ jsx("div", { style: tokenDropdownInnerStyle(t), children: entries.map((entry, index) => {
6818
+ /* @__PURE__ */ jsx("div", { style: tokenDropdownInnerStyle2(t), children: entries.map((entry, index) => {
6297
6819
  const selected = pendingKey === entryKey(entry);
6298
6820
  const isLast = index === entries.length - 1;
6299
6821
  return /* @__PURE__ */ jsxs(
@@ -6394,13 +6916,13 @@ var depositAmountStyle2 = (color) => ({
6394
6916
  color,
6395
6917
  lineHeight: 1.05
6396
6918
  });
6397
- var feeRowContainerStyle = {
6919
+ var feeRowContainerStyle2 = {
6398
6920
  fontSize: "0.84rem",
6399
6921
  fontWeight: 500,
6400
6922
  marginTop: 6
6401
6923
  };
6402
- var feeRowLabelStyle = (color) => ({ color });
6403
- var feeRowAmountStyle = (color) => ({
6924
+ var feeRowLabelStyle2 = (color) => ({ color });
6925
+ var feeRowAmountStyle2 = (color) => ({
6404
6926
  color,
6405
6927
  fontVariantNumeric: "tabular-nums"
6406
6928
  });
@@ -6446,7 +6968,7 @@ var accountDropdownLabelStyle2 = (color) => ({
6446
6968
  color,
6447
6969
  marginBottom: 8
6448
6970
  });
6449
- var tokenDropdownInnerStyle = (tokens) => ({
6971
+ var tokenDropdownInnerStyle2 = (tokens) => ({
6450
6972
  background: tokens.bgInput,
6451
6973
  border: `1px solid ${tokens.border}`,
6452
6974
  borderRadius: tokens.radiusLg,
@@ -6558,7 +7080,7 @@ function GuestPreauthSetupCompleteScreen({
6558
7080
  ] }),
6559
7081
  children: [
6560
7082
  /* @__PURE__ */ jsx(ScreenHeader, { onLogout }),
6561
- /* @__PURE__ */ jsxs("div", { style: contentStyle10, children: [
7083
+ /* @__PURE__ */ jsxs("div", { style: contentStyle9, children: [
6562
7084
  /* @__PURE__ */ jsx(IconCircle, { variant: "success", size: 64, children: /* @__PURE__ */ jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
6563
7085
  "path",
6564
7086
  {
@@ -6567,13 +7089,13 @@ function GuestPreauthSetupCompleteScreen({
6567
7089
  }
6568
7090
  ) }) }),
6569
7091
  /* @__PURE__ */ jsx("h2", { style: headingStyle12(tokens.text), children: "Setup complete" }),
6570
- /* @__PURE__ */ jsx("p", { style: subtitleStyle12(tokens.textSecondary), children: "Your account is linked and ready. You can close this window or make another deposit." })
7092
+ /* @__PURE__ */ jsx("p", { style: subtitleStyle12(tokens.textSecondary), children: "Your account is linked and ready. You can close this window." })
6571
7093
  ] })
6572
7094
  ]
6573
7095
  }
6574
7096
  );
6575
7097
  }
6576
- var contentStyle10 = {
7098
+ var contentStyle9 = {
6577
7099
  display: "flex",
6578
7100
  flexDirection: "column",
6579
7101
  alignItems: "center",
@@ -6602,14 +7124,14 @@ function GuestPreauthLinkingScreen({ onLogout }) {
6602
7124
  const { tokens } = useBlinkConfig();
6603
7125
  return /* @__PURE__ */ jsxs(ScreenLayout, { children: [
6604
7126
  /* @__PURE__ */ jsx(ScreenHeader, { onLogout }),
6605
- /* @__PURE__ */ jsxs("div", { style: contentStyle11, children: [
7127
+ /* @__PURE__ */ jsxs("div", { style: contentStyle10, children: [
6606
7128
  /* @__PURE__ */ jsx(Spinner, { size: 48 }),
6607
7129
  /* @__PURE__ */ jsx("h2", { style: headingStyle13(tokens.text), children: "Setting up your account..." }),
6608
7130
  /* @__PURE__ */ jsx("p", { style: subtitleStyle13(tokens.textSecondary), children: "Linking your wallet to your Blink account. This usually takes a few seconds." })
6609
7131
  ] })
6610
7132
  ] });
6611
7133
  }
6612
- var contentStyle11 = {
7134
+ var contentStyle10 = {
6613
7135
  flex: 1,
6614
7136
  display: "flex",
6615
7137
  flexDirection: "column",
@@ -6849,15 +7371,11 @@ function StepRendererContent({
6849
7371
  0
6850
7372
  );
6851
7373
  const effectiveTokenCount = tokenCount > 0 ? tokenCount : selectSourceTokenCount;
6852
- const effectiveSourceLabel = selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
6853
- const setupTokenOptions = selectedAccount ? selectedAccount.wallets.flatMap(
6854
- (w) => w.sources.filter((s) => s.balance.total.amount > 0).map((s) => ({
6855
- symbol: s.token.symbol,
6856
- chainName: w.chain.name,
6857
- balance: s.balance.available.amount,
6858
- walletId: w.id
6859
- }))
6860
- ) : selectSourceChoices.flatMap(
7374
+ const setupFromPendingSelectSource = pendingSelectSource != null;
7375
+ const setupSelectedTokenSymbol = setupFromPendingSelectSource ? selectSourceTokenSymbol || selectSourceRecommended?.tokenSymbol || "" : selectedSource?.token.symbol ?? selectSourceTokenSymbol;
7376
+ const setupSelectedChainName = setupFromPendingSelectSource ? selectSourceChainName || selectSourceRecommended?.chainName || "" : selectedWallet?.chain.name ?? selectSourceChainName;
7377
+ const effectiveSourceLabel = setupFromPendingSelectSource ? setupSelectedTokenSymbol && setupSelectedChainName ? `${setupSelectedTokenSymbol} on ${setupSelectedChainName}` : void 0 : selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
7378
+ const setupTokenOptions = selectedAccount ? tokenOptionsForLinkedAccount(selectedAccount, state.chains) : selectSourceChoices.flatMap(
6861
7379
  (chain) => chain.tokens.map((t) => ({
6862
7380
  symbol: t.tokenSymbol,
6863
7381
  chainName: chain.chainName,
@@ -6865,9 +7383,8 @@ function StepRendererContent({
6865
7383
  }))
6866
7384
  );
6867
7385
  const handleSetupSelectToken = (symbol, chainName, walletId) => {
6868
- if (walletId) {
6869
- handlers.onSelectAuthorizedToken(walletId, symbol);
6870
- } else {
7386
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7387
+ if (pendingSelectSource) {
6871
7388
  handlers.onSelectSourceChainChange(chainName);
6872
7389
  handlers.onSetSelectSourceTokenSymbol(symbol);
6873
7390
  }
@@ -6875,17 +7392,18 @@ function StepRendererContent({
6875
7392
  return /* @__PURE__ */ jsx(
6876
7393
  SetupScreen,
6877
7394
  {
6878
- availableBalance: selectedSource ? selectedSource.balance.available.amount : selectSourceAvailableBalance > 0 ? selectSourceAvailableBalance : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : maxSourceBalance,
7395
+ availableBalance: selectedSource ? selectedSource.balance.available.amount : setupFromPendingSelectSource ? selectSourceAvailableBalance : selectSourceAvailableBalance > 0 ? selectSourceAvailableBalance : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : maxSourceBalance,
6879
7396
  tokenCount: effectiveTokenCount,
6880
7397
  sourceName,
6881
7398
  onSetupOneTap: handlers.onSetupOneTap,
6882
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7399
+ onBack: handlers.onBackFromSubflow,
6883
7400
  onLogout: handlers.onLogout,
6884
7401
  onAdvanced: handlers.onSelectToken,
6885
7402
  selectedSourceLabel: effectiveSourceLabel,
6886
7403
  loading: savingOneTapLimit,
6887
7404
  error: state.error,
6888
- selectedTokenSymbol: selectedSource?.token.symbol ?? selectSourceTokenSymbol,
7405
+ selectedTokenSymbol: setupSelectedTokenSymbol,
7406
+ selectedChainName: setupSelectedChainName,
6889
7407
  tokenOptions: setupTokenOptions,
6890
7408
  onSelectToken: handleSetupSelectToken
6891
7409
  }
@@ -6907,6 +7425,7 @@ function StepRendererContent({
6907
7425
  case "deposit": {
6908
7426
  const parsedAmt = depositAmount != null ? depositAmount : 5;
6909
7427
  const minDepositFloor = depositAmount != null ? depositAmount : DEFAULT_MIN_DEPOSIT_USD;
7428
+ const depositTokenOptions = tokenOptionsForLinkedAccount(selectedAccount, state.chains);
6910
7429
  return /* @__PURE__ */ jsx(
6911
7430
  DepositScreen,
6912
7431
  {
@@ -6915,6 +7434,8 @@ function StepRendererContent({
6915
7434
  remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
6916
7435
  tokenCount,
6917
7436
  initialAmount: parsedAmt,
7437
+ quoteFee: forms.depositQuoteFee,
7438
+ quoteLoading: forms.depositQuoteLoading,
6918
7439
  processing: state.creatingTransfer,
6919
7440
  error: state.error,
6920
7441
  onDeposit: handlers.onPay,
@@ -6929,8 +7450,13 @@ function StepRendererContent({
6929
7450
  onAuthorizeAccount: handlers.onContinueConnection,
6930
7451
  onAddProvider: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
6931
7452
  onSelectToken: handlers.onSelectToken,
7453
+ tokenOptions: depositTokenOptions,
7454
+ onPickToken: (symbol, chainName, walletId) => {
7455
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7456
+ },
6932
7457
  selectedSourceLabel,
6933
7458
  selectedTokenSymbol: selectedSource?.token.symbol,
7459
+ selectedChainName: selectedWallet?.chain.name,
6934
7460
  minDepositFloor
6935
7461
  }
6936
7462
  );
@@ -6971,7 +7497,7 @@ function StepRendererContent({
6971
7497
  chains: state.chains,
6972
7498
  onSelectAuthorized: handlers.onSelectAuthorizedToken,
6973
7499
  onAuthorizeToken: handlers.onAuthorizeToken,
6974
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7500
+ onBack: handlers.onBackFromSubflow,
6975
7501
  onLogout: handlers.onLogout,
6976
7502
  depositAmount: depositAmount ?? void 0,
6977
7503
  selectedTokenSymbol: selectedSource?.token.symbol,
@@ -7050,7 +7576,7 @@ function StepRendererContent({
7050
7576
  })() : void 0,
7051
7577
  onDone: onDismiss ?? handlers.onNewPayment,
7052
7578
  onLogout: authenticated ? handlers.onLogout : void 0,
7053
- onPreauthorize: state.isGuestFlow ? handlers.onPreauthorize : void 0
7579
+ onPreauthorize: state.isGuestFlow && isDesktop ? handlers.onPreauthorize : void 0
7054
7580
  }
7055
7581
  );
7056
7582
  }
@@ -7449,6 +7975,9 @@ function useTransferHandlers(deps) {
7449
7975
  sourceTokenAddress,
7450
7976
  activeCredentialId,
7451
7977
  selectedAccountId,
7978
+ selectedWalletId,
7979
+ selectedTokenSymbol,
7980
+ quoteId,
7452
7981
  transfer,
7453
7982
  accounts
7454
7983
  } = deps;
@@ -7462,9 +7991,32 @@ function useTransferHandlers(deps) {
7462
7991
  fetchProviders(apiBaseUrl, token)
7463
7992
  ]);
7464
7993
  const parsedAmt = depositAmount != null ? depositAmount : 0;
7465
- const defaults = resolveDepositSelection(accts, parsedAmt, selectedAccountId);
7466
- dispatch({ type: "ACCOUNTS_RELOADED", accounts: accts, providers: prov, defaults });
7467
- }, [getAccessToken, activeCredentialId, selectedAccountId, apiBaseUrl, depositAmount, dispatch]);
7994
+ const { defaults, resetSelectedTokenSymbol } = resolveDepositSelectionAfterRefresh(
7995
+ accts,
7996
+ parsedAmt,
7997
+ {
7998
+ selectedAccountId,
7999
+ selectedWalletId,
8000
+ selectedTokenSymbol
8001
+ }
8002
+ );
8003
+ dispatch({
8004
+ type: "ACCOUNTS_RELOADED",
8005
+ accounts: accts,
8006
+ providers: prov,
8007
+ defaults,
8008
+ resetSelectedTokenSymbol
8009
+ });
8010
+ }, [
8011
+ getAccessToken,
8012
+ activeCredentialId,
8013
+ selectedAccountId,
8014
+ selectedWalletId,
8015
+ selectedTokenSymbol,
8016
+ apiBaseUrl,
8017
+ depositAmount,
8018
+ dispatch
8019
+ ]);
7468
8020
  const handlePay = useCallback(async (payAmount, sourceOverrides) => {
7469
8021
  const minUsd = effectiveMinTransferAmountUsd(depositAmount);
7470
8022
  if (isNaN(payAmount) || payAmount < minUsd) {
@@ -7508,7 +8060,8 @@ function useTransferHandlers(deps) {
7508
8060
  sourceId: effectiveSourceId,
7509
8061
  sourceTokenAddress,
7510
8062
  destination,
7511
- amount: payAmount
8063
+ amount: payAmount,
8064
+ ...quoteId ? { quoteId } : {}
7512
8065
  });
7513
8066
  dispatch({ type: "TRANSFER_CREATED", transfer: t });
7514
8067
  if (t.status === "COMPLETED") {
@@ -7540,6 +8093,7 @@ function useTransferHandlers(deps) {
7540
8093
  accounts,
7541
8094
  destination,
7542
8095
  apiBaseUrl,
8096
+ quoteId,
7543
8097
  getAccessToken,
7544
8098
  transferSigning,
7545
8099
  polling,
@@ -7590,20 +8144,20 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7590
8144
  const selectSourceAvailableBalance = useMemo(() => {
7591
8145
  if (!pendingSelectSourceAction) return 0;
7592
8146
  const options2 = pendingSelectSourceAction.metadata?.options ?? [];
7593
- const recommended = selectSourceRecommended;
7594
- if (recommended) {
7595
- const match = options2.find(
7596
- (opt) => opt.chainName === recommended.chainName && opt.tokenSymbol === recommended.tokenSymbol
7597
- );
7598
- if (match) return Number(match.rawBalance) / Math.pow(10, match.decimals);
7599
- }
7600
- let max = 0;
7601
- for (const opt of options2) {
7602
- const bal = Number(opt.rawBalance) / Math.pow(10, opt.decimals);
7603
- if (bal > max) max = bal;
7604
- }
7605
- return max;
7606
- }, [pendingSelectSourceAction, selectSourceRecommended]);
8147
+ return resolveSelectSourceAvailableBalance(
8148
+ selectSourceChoices,
8149
+ options2,
8150
+ selectSourceChainName,
8151
+ selectSourceTokenSymbol,
8152
+ selectSourceRecommended
8153
+ );
8154
+ }, [
8155
+ pendingSelectSourceAction,
8156
+ selectSourceChoices,
8157
+ selectSourceChainName,
8158
+ selectSourceTokenSymbol,
8159
+ selectSourceRecommended
8160
+ ]);
7607
8161
  const handleSelectSourceChainChange = useCallback(
7608
8162
  (chainName) => {
7609
8163
  setSelectSourceChainName(chainName);
@@ -7654,7 +8208,7 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7654
8208
  initializedSelectSourceActionRef
7655
8209
  };
7656
8210
  }
7657
- function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete) {
8211
+ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete, lastResumedAt) {
7658
8212
  const {
7659
8213
  mobileSetupFlowRef,
7660
8214
  handlingMobileReturnRef,
@@ -7665,6 +8219,8 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7665
8219
  onCompleteRef.current = onComplete;
7666
8220
  const guestPollingActiveRef = useRef(false);
7667
8221
  const guestPollingCleanupRef = useRef(null);
8222
+ const mobileFlowRef = useRef(guestCheckout.mobileFlow);
8223
+ mobileFlowRef.current = guestCheckout.mobileFlow;
7668
8224
  const guestTransferIdRef = useRef(guestCheckout.guestTransferId);
7669
8225
  guestTransferIdRef.current = guestCheckout.guestTransferId;
7670
8226
  const guestSessionTokenRef = useRef(guestCheckout.guestSessionToken);
@@ -7720,27 +8276,15 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7720
8276
  startGuestPolling
7721
8277
  ]);
7722
8278
  useEffect(() => {
7723
- const tryStartPolling = () => {
7724
- if (guestPollingActiveRef.current) return;
7725
- const transferId = guestTransferIdRef.current;
7726
- const token = guestSessionTokenRef.current;
7727
- if (transferId && token) {
7728
- startGuestPolling(transferId, token);
7729
- }
7730
- };
7731
- const handleVisibility = () => {
7732
- if (document.visibilityState === "visible") tryStartPolling();
7733
- };
7734
- const handlePageShow = (e) => {
7735
- if (e.persisted) tryStartPolling();
7736
- };
7737
- document.addEventListener("visibilitychange", handleVisibility);
7738
- window.addEventListener("pageshow", handlePageShow);
7739
- return () => {
7740
- document.removeEventListener("visibilitychange", handleVisibility);
7741
- window.removeEventListener("pageshow", handlePageShow);
7742
- };
7743
- }, [startGuestPolling]);
8279
+ if (!lastResumedAt) return;
8280
+ if (guestPollingActiveRef.current) return;
8281
+ if (!mobileFlowRef.current) return;
8282
+ const transferId = guestTransferIdRef.current;
8283
+ const token = guestSessionTokenRef.current;
8284
+ if (transferId && token) {
8285
+ startGuestPolling(transferId, token);
8286
+ }
8287
+ }, [lastResumedAt, startGuestPolling]);
7744
8288
  const handleAuthorizedMobileReturn = useCallback(async (authorizedTransfer, isSetup) => {
7745
8289
  if (handlingMobileReturnRef.current) return;
7746
8290
  handlingMobileReturnRef.current = true;
@@ -7974,6 +8518,9 @@ function useProviderHandlers(deps) {
7974
8518
  await reloadAccounts();
7975
8519
  }
7976
8520
  } catch (err) {
8521
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8522
+ return;
8523
+ }
7977
8524
  captureException(err);
7978
8525
  const msg = err instanceof Error ? err.message : "Failed to set up wallet";
7979
8526
  dispatch({ type: "PAY_ERROR", error: msg });
@@ -8040,7 +8587,12 @@ function useProviderHandlers(deps) {
8040
8587
  }
8041
8588
  const acct = accounts.find((a) => a.id === selectedAccountId);
8042
8589
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8043
- if (matchedProvider) {
8590
+ const isMobile = !shouldUseWalletConnector({
8591
+ useWalletConnector: useWalletConnectorProp,
8592
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8593
+ });
8594
+ if (matchedProvider && isMobile) {
8595
+ dispatch({ type: "SAVE_SELECTION" });
8044
8596
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8045
8597
  }
8046
8598
  dispatch({ type: "SET_ERROR", error: null });
@@ -8060,10 +8612,6 @@ function useProviderHandlers(deps) {
8060
8612
  activeCredentialId,
8061
8613
  { tokenAddress: source?.address, chainId: evmChainId }
8062
8614
  );
8063
- const isMobile = !shouldUseWalletConnector({
8064
- useWalletConnector: useWalletConnectorProp,
8065
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8066
- });
8067
8615
  if (isMobile) {
8068
8616
  handlingMobileReturnRef.current = false;
8069
8617
  mobileSetupFlowRef.current = true;
@@ -8080,12 +8628,20 @@ function useProviderHandlers(deps) {
8080
8628
  });
8081
8629
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8082
8630
  triggerDeeplink(session.uri);
8631
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8083
8632
  } else {
8084
8633
  await authExecutor.executeSessionById(session.id);
8085
8634
  await reloadAccounts();
8635
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8086
8636
  }
8087
8637
  } catch (err) {
8638
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8639
+ return;
8640
+ }
8088
8641
  captureException(err);
8642
+ if (isMobile) {
8643
+ dispatch({ type: "RESTORE_SELECTION" });
8644
+ }
8089
8645
  const msg = err instanceof Error ? err.message : "Failed to increase limit";
8090
8646
  dispatch({ type: "SET_ERROR", error: msg });
8091
8647
  onError?.(msg);
@@ -8130,7 +8686,12 @@ function useProviderHandlers(deps) {
8130
8686
  }
8131
8687
  const acct = accounts.find((a) => a.id === selectedAccountId);
8132
8688
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8133
- if (matchedProvider) {
8689
+ const isMobile = !shouldUseWalletConnector({
8690
+ useWalletConnector: useWalletConnectorProp,
8691
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8692
+ });
8693
+ if (matchedProvider && isMobile) {
8694
+ dispatch({ type: "SAVE_SELECTION" });
8134
8695
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8135
8696
  }
8136
8697
  dispatch({ type: "SET_ERROR", error: null });
@@ -8145,10 +8706,6 @@ function useProviderHandlers(deps) {
8145
8706
  activeCredentialId,
8146
8707
  { tokenAddress, chainId }
8147
8708
  );
8148
- const isMobile = !shouldUseWalletConnector({
8149
- useWalletConnector: useWalletConnectorProp,
8150
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8151
- });
8152
8709
  if (isMobile) {
8153
8710
  handlingMobileReturnRef.current = false;
8154
8711
  mobileSetupFlowRef.current = true;
@@ -8166,13 +8723,21 @@ function useProviderHandlers(deps) {
8166
8723
  });
8167
8724
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8168
8725
  triggerDeeplink(session.uri);
8726
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8169
8727
  } else {
8170
8728
  await authExecutor.executeSessionById(session.id);
8171
8729
  await reloadAccounts();
8172
8730
  dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
8731
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8173
8732
  }
8174
8733
  } catch (err) {
8734
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8735
+ return;
8736
+ }
8175
8737
  captureException(err);
8738
+ if (isMobile) {
8739
+ dispatch({ type: "RESTORE_SELECTION" });
8740
+ }
8176
8741
  const msg = err instanceof Error ? err.message : "Failed to authorize token";
8177
8742
  dispatch({ type: "SET_ERROR", error: msg });
8178
8743
  onError?.(msg);
@@ -9109,7 +9674,15 @@ function useDataLoadEffect(deps) {
9109
9674
  ]);
9110
9675
  if (cancelled) return;
9111
9676
  const parsedAmt = depositAmount != null ? depositAmount : 0;
9112
- const defaults = resolveDepositSelection(accts, parsedAmt, state.selectedAccountId);
9677
+ const { defaults, resetSelectedTokenSymbol } = resolveDepositSelectionAfterRefresh(
9678
+ accts,
9679
+ parsedAmt,
9680
+ {
9681
+ selectedAccountId: state.selectedAccountId,
9682
+ selectedWalletId: state.selectedWalletId,
9683
+ selectedTokenSymbol: state.selectedTokenSymbol
9684
+ }
9685
+ );
9113
9686
  const persisted = loadMobileFlowState();
9114
9687
  const clearMobile = persisted?.isSetup && accts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
9115
9688
  dispatch({
@@ -9118,7 +9691,8 @@ function useDataLoadEffect(deps) {
9118
9691
  accounts: accts,
9119
9692
  chains: chn,
9120
9693
  defaults,
9121
- clearMobileState: clearMobile
9694
+ clearMobileState: clearMobile,
9695
+ resetSelectedTokenSymbol
9122
9696
  });
9123
9697
  if (clearMobile) clearMobileFlowState();
9124
9698
  } catch (err) {
@@ -9147,6 +9721,8 @@ function useDataLoadEffect(deps) {
9147
9721
  apiBaseUrl,
9148
9722
  state.activeCredentialId,
9149
9723
  state.selectedAccountId,
9724
+ state.selectedWalletId,
9725
+ state.selectedTokenSymbol,
9150
9726
  depositAmount
9151
9727
  ]);
9152
9728
  useEffect(() => {
@@ -9375,25 +9951,16 @@ function useMobilePollingEffect(deps) {
9375
9951
  const poll = isReauth ? pollReauthorization : pollWalletActive;
9376
9952
  poll();
9377
9953
  const intervalId = window.setInterval(poll, POLL_INTERVAL_MS);
9378
- const handleVisibility = () => {
9379
- if (document.visibilityState === "visible" && !cancelled) poll();
9380
- };
9381
- const handlePageShow = (e) => {
9382
- if (e.persisted && !cancelled) poll();
9383
- };
9384
- document.addEventListener("visibilitychange", handleVisibility);
9385
- window.addEventListener("pageshow", handlePageShow);
9386
9954
  return () => {
9387
9955
  cancelled = true;
9388
9956
  window.clearInterval(intervalId);
9389
- document.removeEventListener("visibilitychange", handleVisibility);
9390
- window.removeEventListener("pageshow", handlePageShow);
9391
9957
  };
9392
9958
  }, [
9393
9959
  state.mobileFlow,
9394
9960
  state.isGuestFlow,
9395
9961
  state.guestPreauthSessionId,
9396
9962
  state.activeCredentialId,
9963
+ state.lastResumedAt,
9397
9964
  apiBaseUrl,
9398
9965
  reloadAccounts,
9399
9966
  dispatch,
@@ -9410,28 +9977,13 @@ function useMobilePollingEffect(deps) {
9410
9977
  const transferIdToResume = pollingTransferIdRef.current ?? state.transfer?.id;
9411
9978
  if (!transferIdToResume) return;
9412
9979
  if (!polling.isPolling) polling.startPolling(transferIdToResume);
9413
- const handleVisibility = () => {
9414
- if (document.visibilityState === "visible" && !handlingMobileReturnRef.current) {
9415
- polling.startPolling(transferIdToResume);
9416
- }
9417
- };
9418
- const handlePageShow = (e) => {
9419
- if (e.persisted && !handlingMobileReturnRef.current) {
9420
- polling.startPolling(transferIdToResume);
9421
- }
9422
- };
9423
- document.addEventListener("visibilitychange", handleVisibility);
9424
- window.addEventListener("pageshow", handlePageShow);
9425
- return () => {
9426
- document.removeEventListener("visibilitychange", handleVisibility);
9427
- window.removeEventListener("pageshow", handlePageShow);
9428
- };
9429
9980
  }, [
9430
9981
  state.mobileFlow,
9431
9982
  state.transfer?.id,
9432
9983
  state.isGuestFlow,
9433
9984
  state.guestPreauthSessionId,
9434
9985
  state.guestPreauthAccountId,
9986
+ state.lastResumedAt,
9435
9987
  polling.isPolling,
9436
9988
  polling.startPolling,
9437
9989
  handlingMobileReturnRef,
@@ -9442,6 +9994,7 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9442
9994
  const {
9443
9995
  dispatch,
9444
9996
  privyReady,
9997
+ lastResumedAt,
9445
9998
  mobileSetupFlowRef,
9446
9999
  setupAccountIdRef,
9447
10000
  startGuestAccountPolling
@@ -9452,68 +10005,43 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9452
10005
  console.info("[blink-sdk] guestPreauthMobileRestore: skipping, privyReady=false");
9453
10006
  return;
9454
10007
  }
9455
- const tryStart = (trigger) => {
9456
- if (startedRef.current) {
9457
- console.info("[blink-sdk] guestPreauthMobileRestore.tryStart: already started", { trigger });
9458
- return;
9459
- }
9460
- const persisted = loadMobileFlowState();
9461
- console.info("[blink-sdk] guestPreauthMobileRestore.tryStart", {
9462
- trigger,
9463
- hasPersistedState: !!persisted,
9464
- isGuestPreauth: persisted?.isGuestPreauth ?? false,
9465
- hasGuestSessionToken: !!persisted?.guestSessionToken,
9466
- hasSessionId: !!persisted?.sessionId,
9467
- accountId: persisted?.accountId ?? null
9468
- });
9469
- if (!persisted?.isGuestPreauth || !persisted.guestSessionToken || !persisted.sessionId) {
9470
- return;
9471
- }
9472
- startedRef.current = true;
9473
- mobileSetupFlowRef.current = true;
9474
- if (persisted.accountId) {
9475
- setupAccountIdRef.current = persisted.accountId;
9476
- }
9477
- if (persisted.accountId) {
9478
- dispatch({
9479
- type: "GUEST_PREAUTH_DETECTED",
9480
- accountId: persisted.accountId,
9481
- sessionId: persisted.sessionId
9482
- });
9483
- }
10008
+ if (startedRef.current) {
10009
+ console.info("[blink-sdk] guestPreauthMobileRestore: already started");
10010
+ return;
10011
+ }
10012
+ const persisted = loadMobileFlowState();
10013
+ console.info("[blink-sdk] guestPreauthMobileRestore.tryStart", {
10014
+ trigger: lastResumedAt ? "page-resume" : "initial",
10015
+ hasPersistedState: !!persisted,
10016
+ isGuestPreauth: persisted?.isGuestPreauth ?? false,
10017
+ hasGuestSessionToken: !!persisted?.guestSessionToken,
10018
+ hasSessionId: !!persisted?.sessionId,
10019
+ accountId: persisted?.accountId ?? null
10020
+ });
10021
+ if (!persisted?.isGuestPreauth || !persisted.guestSessionToken || !persisted.sessionId) {
10022
+ return;
10023
+ }
10024
+ startedRef.current = true;
10025
+ mobileSetupFlowRef.current = true;
10026
+ if (persisted.accountId) {
10027
+ setupAccountIdRef.current = persisted.accountId;
10028
+ }
10029
+ if (persisted.accountId) {
9484
10030
  dispatch({
9485
- type: "MOBILE_DEEPLINK_READY",
9486
- deeplinkUri: persisted.deeplinkUri
9487
- });
9488
- console.info("[blink-sdk] guestPreauthMobileRestore: starting guest account polling", {
9489
- trigger,
10031
+ type: "GUEST_PREAUTH_DETECTED",
10032
+ accountId: persisted.accountId,
9490
10033
  sessionId: persisted.sessionId
9491
10034
  });
9492
- startGuestAccountPolling(persisted.guestSessionToken, persisted.sessionId);
9493
- };
9494
- const onVisibility = () => {
9495
- console.info("[blink-sdk] guestPreauthMobileRestore: visibilitychange fired", {
9496
- visibilityState: document.visibilityState,
9497
- startedRef: startedRef.current
9498
- });
9499
- if (document.visibilityState === "visible") tryStart("visibilitychange");
9500
- };
9501
- const onPageShow = (e) => {
9502
- console.info("[blink-sdk] guestPreauthMobileRestore: pageshow fired", {
9503
- persisted: e.persisted,
9504
- visibilityState: document.visibilityState,
9505
- startedRef: startedRef.current
9506
- });
9507
- if (document.visibilityState === "visible") tryStart("pageshow");
9508
- };
9509
- tryStart("initial");
9510
- document.addEventListener("visibilitychange", onVisibility);
9511
- window.addEventListener("pageshow", onPageShow);
9512
- return () => {
9513
- document.removeEventListener("visibilitychange", onVisibility);
9514
- window.removeEventListener("pageshow", onPageShow);
9515
- };
9516
- }, [privyReady, dispatch, mobileSetupFlowRef, setupAccountIdRef, startGuestAccountPolling]);
10035
+ }
10036
+ dispatch({
10037
+ type: "MOBILE_DEEPLINK_READY",
10038
+ deeplinkUri: persisted.deeplinkUri
10039
+ });
10040
+ console.info("[blink-sdk] guestPreauthMobileRestore: starting guest account polling", {
10041
+ sessionId: persisted.sessionId
10042
+ });
10043
+ startGuestAccountPolling(persisted.guestSessionToken, persisted.sessionId);
10044
+ }, [privyReady, lastResumedAt, dispatch, mobileSetupFlowRef, setupAccountIdRef, startGuestAccountPolling]);
9517
10045
  }
9518
10046
  function useSelectSourceEffect(deps) {
9519
10047
  const {
@@ -9817,7 +10345,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9817
10345
  useEffect(() => {
9818
10346
  if (!isDesktop || state.guestPreauthorizing) return;
9819
10347
  if (!state.privyAuthenticated || !state.activeCredentialId || !state.selectedAccountId) return;
9820
- const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && !authExecutor.pendingOneTapSetup;
10348
+ const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && (!authExecutor.pendingOneTapSetup || authExecutor.authorizationSessionStackDepth > 1);
9821
10349
  if (shouldPin && !state.standardDesktopInlineOpenWallet) {
9822
10350
  dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: true });
9823
10351
  return;
@@ -9835,6 +10363,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9835
10363
  authExecutor.executing,
9836
10364
  authExecutor.pendingSelectSource,
9837
10365
  authExecutor.pendingOneTapSetup,
10366
+ authExecutor.authorizationSessionStackDepth,
9838
10367
  dispatch
9839
10368
  ]);
9840
10369
  }
@@ -9853,7 +10382,84 @@ function useGuestAccountAutoPollingEffect(deps) {
9853
10382
  startPolling(guestSessionToken);
9854
10383
  }, [mobileFlow, isGuestFlow, guestSessionToken, isPolling, guestAccount, startPolling]);
9855
10384
  }
9856
- function useGuestAccountPolling(intervalMs = 3e3) {
10385
+ var WATCHDOG_INTERVAL_MS = 1e3;
10386
+ var WATCHDOG_THRESHOLD_MS = 3e3;
10387
+ var DEDUP_COOLDOWN_MS = 500;
10388
+ function usePageResume(callback) {
10389
+ const callbackRef = useRef(callback);
10390
+ callbackRef.current = callback;
10391
+ useEffect(() => {
10392
+ let lastTickAt = Date.now();
10393
+ let lastFiredAt = 0;
10394
+ const fire = (source, frozenDurationMs) => {
10395
+ const now = Date.now();
10396
+ if (now - lastFiredAt < DEDUP_COOLDOWN_MS) return;
10397
+ lastFiredAt = now;
10398
+ callbackRef.current({ frozenDurationMs, source });
10399
+ };
10400
+ const watchdogId = window.setInterval(() => {
10401
+ const now = Date.now();
10402
+ const elapsed = now - lastTickAt;
10403
+ lastTickAt = now;
10404
+ if (elapsed > WATCHDOG_THRESHOLD_MS) {
10405
+ fire("watchdog", elapsed);
10406
+ }
10407
+ }, WATCHDOG_INTERVAL_MS);
10408
+ const handleVisibility = () => {
10409
+ const now = Date.now();
10410
+ if (document.visibilityState === "visible") {
10411
+ fire("visibilitychange", now - lastTickAt);
10412
+ }
10413
+ lastTickAt = now;
10414
+ };
10415
+ const handlePageShow = (_e) => {
10416
+ const now = Date.now();
10417
+ if (document.visibilityState === "visible") {
10418
+ fire("pageshow", now - lastTickAt);
10419
+ }
10420
+ lastTickAt = now;
10421
+ };
10422
+ const handleFocus = () => {
10423
+ const now = Date.now();
10424
+ const elapsed = now - lastTickAt;
10425
+ lastTickAt = now;
10426
+ if (elapsed > WATCHDOG_THRESHOLD_MS) {
10427
+ fire("focus", elapsed);
10428
+ }
10429
+ };
10430
+ document.addEventListener("visibilitychange", handleVisibility);
10431
+ window.addEventListener("pageshow", handlePageShow);
10432
+ window.addEventListener("focus", handleFocus);
10433
+ return () => {
10434
+ window.clearInterval(watchdogId);
10435
+ document.removeEventListener("visibilitychange", handleVisibility);
10436
+ window.removeEventListener("pageshow", handlePageShow);
10437
+ window.removeEventListener("focus", handleFocus);
10438
+ };
10439
+ }, []);
10440
+ }
10441
+ var STUCK_REF_THRESHOLD_MS = 5e3;
10442
+ function usePageResumeEffect(deps) {
10443
+ const { dispatch, handlingMobileReturnRef } = deps;
10444
+ const dispatchRef = useRef(dispatch);
10445
+ dispatchRef.current = dispatch;
10446
+ usePageResume((info) => {
10447
+ console.info("[blink-sdk] PAGE_RESUMED", {
10448
+ source: info.source,
10449
+ frozenDurationMs: info.frozenDurationMs
10450
+ });
10451
+ dispatchRef.current({ type: "PAGE_RESUMED", frozenDurationMs: info.frozenDurationMs });
10452
+ if (info.frozenDurationMs > STUCK_REF_THRESHOLD_MS) {
10453
+ if (handlingMobileReturnRef.current) {
10454
+ console.info("[blink-sdk] PAGE_RESUMED: clearing stuck handlingMobileReturnRef", {
10455
+ frozenDurationMs: info.frozenDurationMs
10456
+ });
10457
+ handlingMobileReturnRef.current = false;
10458
+ }
10459
+ }
10460
+ });
10461
+ }
10462
+ function useGuestAccountPolling(intervalMs = 3e3, lastResumedAt = 0) {
9857
10463
  const { apiBaseUrl } = useBlinkConfig();
9858
10464
  const [guestAccount, setGuestAccount] = useState(null);
9859
10465
  const [isPolling, setIsPolling] = useState(false);
@@ -9951,36 +10557,71 @@ function useGuestAccountPolling(intervalMs = 3e3) {
9951
10557
  [poll, intervalMs, stopPolling]
9952
10558
  );
9953
10559
  useEffect(() => {
9954
- const handleVisibility = () => {
9955
- console.info("[blink-sdk] useGuestAccountPolling: visibilitychange fired", {
9956
- visibilityState: document.visibilityState,
9957
- hasToken: !!guestTokenRef.current,
9958
- hasSessionId: !!sessionIdRef.current
9959
- });
9960
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9961
- void poll();
9962
- }
9963
- };
9964
- const handlePageShow = (e) => {
9965
- console.info("[blink-sdk] useGuestAccountPolling: pageshow fired", {
9966
- persisted: e.persisted,
9967
- visibilityState: document.visibilityState,
9968
- hasToken: !!guestTokenRef.current,
9969
- hasSessionId: !!sessionIdRef.current
10560
+ if (!lastResumedAt || !guestTokenRef.current) return;
10561
+ console.info("[blink-sdk] useGuestAccountPolling: page resumed, triggering poll", {
10562
+ lastResumedAt,
10563
+ hasToken: !!guestTokenRef.current,
10564
+ hasSessionId: !!sessionIdRef.current
10565
+ });
10566
+ void poll();
10567
+ }, [lastResumedAt, poll]);
10568
+ useEffect(() => () => stopPolling(), [stopPolling]);
10569
+ return { guestAccount, error, isPolling, startPolling, stopPolling };
10570
+ }
10571
+ function useDepositFeeEstimate({
10572
+ apiBaseUrl,
10573
+ getAccessToken,
10574
+ walletId,
10575
+ sourceTokenAddress,
10576
+ destination,
10577
+ amount,
10578
+ enabled
10579
+ }) {
10580
+ const [quoteId, setQuoteId] = useState(null);
10581
+ const [quoteFee, setQuoteFee] = useState(null);
10582
+ const [quoteLoading, setQuoteLoading] = useState(false);
10583
+ const abortRef = useRef(null);
10584
+ const fetchQuote = useCallback(async () => {
10585
+ if (!enabled || !walletId || !sourceTokenAddress) {
10586
+ setQuoteId(null);
10587
+ setQuoteFee(null);
10588
+ setQuoteLoading(false);
10589
+ return;
10590
+ }
10591
+ abortRef.current?.abort();
10592
+ const controller = new AbortController();
10593
+ abortRef.current = controller;
10594
+ setQuoteLoading(true);
10595
+ try {
10596
+ const token = await getAccessToken();
10597
+ if (!token || controller.signal.aborted) return;
10598
+ const quote = await postTransferQuote(apiBaseUrl, token, {
10599
+ walletId,
10600
+ sourceTokenAddress,
10601
+ destination,
10602
+ amount: { amount, currency: "USD" }
9970
10603
  });
9971
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9972
- void poll();
10604
+ if (controller.signal.aborted) return;
10605
+ setQuoteId(quote.id);
10606
+ setQuoteFee(quote.fee);
10607
+ } catch (err) {
10608
+ if (controller.signal.aborted) return;
10609
+ console.warn("[blink-sdk] Fee quote failed:", err);
10610
+ setQuoteId(null);
10611
+ setQuoteFee(null);
10612
+ } finally {
10613
+ if (!controller.signal.aborted) {
10614
+ setQuoteLoading(false);
9973
10615
  }
9974
- };
9975
- document.addEventListener("visibilitychange", handleVisibility);
9976
- window.addEventListener("pageshow", handlePageShow);
10616
+ }
10617
+ }, [enabled, walletId, sourceTokenAddress, destination, amount, apiBaseUrl, getAccessToken]);
10618
+ useEffect(() => {
10619
+ fetchQuote();
9977
10620
  return () => {
9978
- document.removeEventListener("visibilitychange", handleVisibility);
9979
- window.removeEventListener("pageshow", handlePageShow);
10621
+ abortRef.current?.abort();
9980
10622
  };
9981
- }, [poll]);
9982
- useEffect(() => () => stopPolling(), [stopPolling]);
9983
- return { guestAccount, error, isPolling, startPolling, stopPolling };
10623
+ }, [fetchQuote]);
10624
+ return { quoteId, quoteFee, quoteLoading };
9984
10625
  }
9985
10626
  function BlinkPayment(props) {
9986
10627
  const resetKey = useRef(0);
@@ -10020,7 +10661,7 @@ function BlinkPaymentInner({
10020
10661
  );
10021
10662
  const authExecutor = useAuthorizationExecutor();
10022
10663
  const polling = useTransferPolling();
10023
- const guestAccountPolling = useGuestAccountPolling();
10664
+ const guestAccountPolling = useGuestAccountPolling(3e3, state.lastResumedAt);
10024
10665
  const transferSigning = useTransferSigning();
10025
10666
  const mobileFlowRefs = {
10026
10667
  mobileSetupFlowRef: useRef(false),
@@ -10038,6 +10679,15 @@ function BlinkPaymentInner({
10038
10679
  state.accounts,
10039
10680
  state.knownCredentialIds
10040
10681
  );
10682
+ const depositFee = useDepositFeeEstimate({
10683
+ apiBaseUrl,
10684
+ getAccessToken,
10685
+ walletId: state.selectedWalletId,
10686
+ sourceTokenAddress: derived.selectedSource?.address,
10687
+ destination,
10688
+ amount: depositAmount ?? 5,
10689
+ enabled: state.phase.step === "deposit" && !state.isGuestFlow && authenticated
10690
+ });
10041
10691
  const transfer = useTransferHandlers({
10042
10692
  dispatch,
10043
10693
  getAccessToken,
@@ -10055,6 +10705,9 @@ function BlinkPaymentInner({
10055
10705
  sourceTokenAddress: derived.selectedSource?.address,
10056
10706
  activeCredentialId: state.activeCredentialId,
10057
10707
  selectedAccountId: state.selectedAccountId,
10708
+ selectedWalletId: state.selectedWalletId,
10709
+ selectedTokenSymbol: state.selectedTokenSymbol,
10710
+ quoteId: depositFee.quoteId,
10058
10711
  transfer: state.transfer,
10059
10712
  accounts: state.accounts
10060
10713
  });
@@ -10071,7 +10724,8 @@ function BlinkPaymentInner({
10071
10724
  guestTransferId: state.guestTransferId,
10072
10725
  guestSessionToken: state.guestSessionToken
10073
10726
  },
10074
- onComplete
10727
+ onComplete,
10728
+ state.lastResumedAt
10075
10729
  );
10076
10730
  const sourceSelection = useSourceSelectionHandlers(dispatch, authExecutor, {
10077
10731
  guestPreauthSessionId: state.guestPreauthSessionId,
@@ -10167,10 +10821,15 @@ function BlinkPaymentInner({
10167
10821
  dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
10168
10822
  }
10169
10823
  }, [depositAmount, dispatch]);
10824
+ usePageResumeEffect({
10825
+ dispatch,
10826
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef
10827
+ });
10170
10828
  usePrivySessionSyncEffect({ dispatch, ready, authenticated });
10171
10829
  useGuestPreauthMobileRestoreEffect({
10172
10830
  dispatch,
10173
10831
  privyReady: state.privyReady,
10832
+ lastResumedAt: state.lastResumedAt,
10174
10833
  mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
10175
10834
  setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
10176
10835
  startGuestAccountPolling: guestAccountPolling.startPolling
@@ -10183,25 +10842,24 @@ function BlinkPaymentInner({
10183
10842
  guestAccount: guestAccountPolling.guestAccount,
10184
10843
  startPolling: guestAccountPolling.startPolling
10185
10844
  });
10186
- const guestSessionTokenRef = useRef(state.guestSessionToken);
10187
- guestSessionTokenRef.current = state.guestSessionToken;
10188
- const guestPreauthSessionIdRef = useRef(state.guestPreauthSessionId);
10189
- guestPreauthSessionIdRef.current = state.guestPreauthSessionId;
10190
10845
  useEffect(() => {
10191
- const handleVisibility = () => {
10192
- if (document.visibilityState !== "visible") return;
10193
- const token = guestSessionTokenRef.current;
10194
- const sessionId = guestPreauthSessionIdRef.current;
10195
- if (!token) return;
10196
- if (guestAccountPolling.isPolling || guestAccountPolling.guestAccount) return;
10197
- console.info("[blink-sdk] warm-return: restarting guest account polling from React state", {
10198
- sessionId: sessionId ?? "(none \u2014 account-only mode)"
10199
- });
10200
- guestAccountPolling.startPolling(token, sessionId ?? void 0);
10201
- };
10202
- document.addEventListener("visibilitychange", handleVisibility);
10203
- return () => document.removeEventListener("visibilitychange", handleVisibility);
10204
- }, [guestAccountPolling.isPolling, guestAccountPolling.guestAccount, guestAccountPolling.startPolling]);
10846
+ if (!state.lastResumedAt) return;
10847
+ const token = state.guestSessionToken;
10848
+ const sessionId = state.guestPreauthSessionId;
10849
+ if (!token) return;
10850
+ if (guestAccountPolling.isPolling || guestAccountPolling.guestAccount) return;
10851
+ console.info("[blink-sdk] warm-return: restarting guest account polling from React state", {
10852
+ sessionId: sessionId ?? "(none \u2014 account-only mode)"
10853
+ });
10854
+ guestAccountPolling.startPolling(token, sessionId ?? void 0);
10855
+ }, [
10856
+ state.lastResumedAt,
10857
+ state.guestSessionToken,
10858
+ state.guestPreauthSessionId,
10859
+ guestAccountPolling.isPolling,
10860
+ guestAccountPolling.guestAccount,
10861
+ guestAccountPolling.startPolling
10862
+ ]);
10205
10863
  useEffect(() => {
10206
10864
  console.info("[blink-sdk] guestPreauthCompletion effect", {
10207
10865
  hasGuestAccount: !!guestAccountPolling.guestAccount,
@@ -10366,6 +11024,11 @@ function BlinkPaymentInner({
10366
11024
  onLogout: handleLogout,
10367
11025
  onNewPayment: handleNewPayment,
10368
11026
  onSetPhase: (phase) => dispatch({ type: "SET_USER_INTENT", intent: phase }),
11027
+ onBackFromSubflow: () => {
11028
+ authExecutor.cancelPendingExecution();
11029
+ dispatch({ type: "RESTORE_SELECTION" });
11030
+ dispatch({ type: "SET_USER_INTENT", intent: { step: "deposit" } });
11031
+ },
10369
11032
  onSetAuthInput: auth.setAuthInput,
10370
11033
  onSetOtpCode: (code) => {
10371
11034
  auth.setOtpCode(code);
@@ -10399,7 +11062,8 @@ function BlinkPaymentInner({
10399
11062
  handleLogout,
10400
11063
  handleNewPayment,
10401
11064
  onDismiss,
10402
- dispatch
11065
+ dispatch,
11066
+ authExecutor
10403
11067
  ]);
10404
11068
  return /* @__PURE__ */ jsx(
10405
11069
  StepRenderer,
@@ -10448,13 +11112,16 @@ function BlinkPaymentInner({
10448
11112
  guestSettingSender: guestTransfer.settingSender,
10449
11113
  guestPendingEntry: guestTransfer.pendingGuestEntry,
10450
11114
  guestQuoteFee: guestTransfer.guestFee?.quote ?? null,
10451
- guestQuoteLoading: guestTransfer.guestQuoteLoading
11115
+ guestQuoteLoading: guestTransfer.guestQuoteLoading,
11116
+ depositQuoteId: depositFee.quoteId,
11117
+ depositQuoteFee: depositFee.quoteFee,
11118
+ depositQuoteLoading: depositFee.quoteLoading
10452
11119
  },
10453
11120
  handlers
10454
11121
  }
10455
11122
  );
10456
11123
  }
10457
11124
 
10458
- export { AdvancedSourceScreen, BLINK_LOGO, BLINK_MASCOT, BlinkLoadingScreen, BlinkPayment, BlinkProvider, ConfirmSignScreen, DepositScreen, FlowPhaseProvider, GuestPreauthLinkingScreen, GuestPreauthSetupCompleteScreen, GuestTokenPickerScreen, IconCircle, InfoBanner, LoginScreen, OpenWalletScreen, OtpVerifyScreen, OutlineButton, PasskeyIframeBlockedError, PasskeyScreen, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, SetupStatusScreen, Spinner, StepList, SuccessScreen, TokenPickerScreen, TransferStatusScreen, VerifyPasskeyScreen, WalletPickerScreen, api_exports as blinkApi, buildPasskeyPopupOptions, createPasskeyCredential, createPasskeyViaPopup, darkTheme, deviceHasPasskey, findDevicePasskey, findDevicePasskeyViaPopup, getTheme, guestEntryMatchingRecommended, lightTheme, mapGuestPickerEntries, resolvePasskeyRpId, screenForPhase, useAuthorizationExecutor, useBlinkConfig, useBlinkDepositAmount, useTransferPolling, useTransferSigning };
11125
+ export { AdvancedSourceScreen, AuthorizationSessionCancelledError, BLINK_LOGO, BLINK_MASCOT, BlinkLoadingScreen, BlinkPayment, BlinkProvider, ConfirmSignScreen, DepositScreen, FlowPhaseProvider, GuestPreauthLinkingScreen, GuestPreauthSetupCompleteScreen, GuestTokenPickerScreen, IconCircle, InfoBanner, LoginScreen, OpenWalletScreen, OtpVerifyScreen, OutlineButton, PasskeyIframeBlockedError, PasskeyScreen, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SelectSourceScreen, SettingsMenu, SetupScreen, SetupStatusScreen, Spinner, StepList, SuccessScreen, TokenPickerScreen, TransferStatusScreen, VerifyPasskeyScreen, WalletPickerScreen, api_exports as blinkApi, buildPasskeyPopupOptions, createPasskeyCredential, createPasskeyViaPopup, darkTheme, deviceHasPasskey, findDevicePasskey, findDevicePasskeyViaPopup, getTheme, guestEntryMatchingRecommended, isAuthorizationSessionCancelled, isUserDismissedAuthorizationError, lightTheme, mapGuestPickerEntries, resolvePasskeyRpId, screenForPhase, useAuthorizationExecutor, useBlinkConfig, useBlinkDepositAmount, useTransferPolling, useTransferSigning };
10459
11126
  //# sourceMappingURL=index.js.map
10460
11127
  //# sourceMappingURL=index.js.map