@swype-org/react-sdk 0.1.300 → 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",
@@ -6666,6 +7188,10 @@ function resolveHeaderFlowPhase(screen, phase, authenticated) {
6666
7188
  return raw;
6667
7189
  }
6668
7190
  var DEFAULT_MIN_DEPOSIT_USD = 0.25;
7191
+ var GUEST_PREAUTH_LOGIN_HERO_TITLE = "Create an account to complete one tap setup";
7192
+ function isGuestPreauthLogin(state) {
7193
+ return state.isGuestFlow && (state.guestPreauthSessionId != null || state.guestPreauthAccountId != null);
7194
+ }
6669
7195
  function StepRenderer(props) {
6670
7196
  const screen = screenForPhase(props.flow.state.phase);
6671
7197
  const flowPhase = resolveHeaderFlowPhase(
@@ -6745,7 +7271,8 @@ function StepRendererContent({
6745
7271
  sending: activeOtpStatus === "sending-code",
6746
7272
  error: state.error,
6747
7273
  onBack,
6748
- merchantInitials: merchantName ? merchantName.slice(0, 2).toUpperCase() : void 0
7274
+ merchantInitials: merchantName ? merchantName.slice(0, 2).toUpperCase() : void 0,
7275
+ heroTitle: isGuestPreauthLogin(state) ? GUEST_PREAUTH_LOGIN_HERO_TITLE : void 0
6749
7276
  }
6750
7277
  );
6751
7278
  case "otp-verify":
@@ -6844,15 +7371,11 @@ function StepRendererContent({
6844
7371
  0
6845
7372
  );
6846
7373
  const effectiveTokenCount = tokenCount > 0 ? tokenCount : selectSourceTokenCount;
6847
- const effectiveSourceLabel = selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
6848
- const setupTokenOptions = selectedAccount ? selectedAccount.wallets.flatMap(
6849
- (w) => w.sources.filter((s) => s.balance.total.amount > 0).map((s) => ({
6850
- symbol: s.token.symbol,
6851
- chainName: w.chain.name,
6852
- balance: s.balance.available.amount,
6853
- walletId: w.id
6854
- }))
6855
- ) : 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(
6856
7379
  (chain) => chain.tokens.map((t) => ({
6857
7380
  symbol: t.tokenSymbol,
6858
7381
  chainName: chain.chainName,
@@ -6860,9 +7383,8 @@ function StepRendererContent({
6860
7383
  }))
6861
7384
  );
6862
7385
  const handleSetupSelectToken = (symbol, chainName, walletId) => {
6863
- if (walletId) {
6864
- handlers.onSelectAuthorizedToken(walletId, symbol);
6865
- } else {
7386
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7387
+ if (pendingSelectSource) {
6866
7388
  handlers.onSelectSourceChainChange(chainName);
6867
7389
  handlers.onSetSelectSourceTokenSymbol(symbol);
6868
7390
  }
@@ -6870,17 +7392,18 @@ function StepRendererContent({
6870
7392
  return /* @__PURE__ */ jsx(
6871
7393
  SetupScreen,
6872
7394
  {
6873
- 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,
6874
7396
  tokenCount: effectiveTokenCount,
6875
7397
  sourceName,
6876
7398
  onSetupOneTap: handlers.onSetupOneTap,
6877
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7399
+ onBack: handlers.onBackFromSubflow,
6878
7400
  onLogout: handlers.onLogout,
6879
7401
  onAdvanced: handlers.onSelectToken,
6880
7402
  selectedSourceLabel: effectiveSourceLabel,
6881
7403
  loading: savingOneTapLimit,
6882
7404
  error: state.error,
6883
- selectedTokenSymbol: selectedSource?.token.symbol ?? selectSourceTokenSymbol,
7405
+ selectedTokenSymbol: setupSelectedTokenSymbol,
7406
+ selectedChainName: setupSelectedChainName,
6884
7407
  tokenOptions: setupTokenOptions,
6885
7408
  onSelectToken: handleSetupSelectToken
6886
7409
  }
@@ -6902,6 +7425,7 @@ function StepRendererContent({
6902
7425
  case "deposit": {
6903
7426
  const parsedAmt = depositAmount != null ? depositAmount : 5;
6904
7427
  const minDepositFloor = depositAmount != null ? depositAmount : DEFAULT_MIN_DEPOSIT_USD;
7428
+ const depositTokenOptions = tokenOptionsForLinkedAccount(selectedAccount, state.chains);
6905
7429
  return /* @__PURE__ */ jsx(
6906
7430
  DepositScreen,
6907
7431
  {
@@ -6910,6 +7434,8 @@ function StepRendererContent({
6910
7434
  remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
6911
7435
  tokenCount,
6912
7436
  initialAmount: parsedAmt,
7437
+ quoteFee: forms.depositQuoteFee,
7438
+ quoteLoading: forms.depositQuoteLoading,
6913
7439
  processing: state.creatingTransfer,
6914
7440
  error: state.error,
6915
7441
  onDeposit: handlers.onPay,
@@ -6924,8 +7450,13 @@ function StepRendererContent({
6924
7450
  onAuthorizeAccount: handlers.onContinueConnection,
6925
7451
  onAddProvider: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
6926
7452
  onSelectToken: handlers.onSelectToken,
7453
+ tokenOptions: depositTokenOptions,
7454
+ onPickToken: (symbol, chainName, walletId) => {
7455
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7456
+ },
6927
7457
  selectedSourceLabel,
6928
7458
  selectedTokenSymbol: selectedSource?.token.symbol,
7459
+ selectedChainName: selectedWallet?.chain.name,
6929
7460
  minDepositFloor
6930
7461
  }
6931
7462
  );
@@ -6966,7 +7497,7 @@ function StepRendererContent({
6966
7497
  chains: state.chains,
6967
7498
  onSelectAuthorized: handlers.onSelectAuthorizedToken,
6968
7499
  onAuthorizeToken: handlers.onAuthorizeToken,
6969
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7500
+ onBack: handlers.onBackFromSubflow,
6970
7501
  onLogout: handlers.onLogout,
6971
7502
  depositAmount: depositAmount ?? void 0,
6972
7503
  selectedTokenSymbol: selectedSource?.token.symbol,
@@ -7045,7 +7576,7 @@ function StepRendererContent({
7045
7576
  })() : void 0,
7046
7577
  onDone: onDismiss ?? handlers.onNewPayment,
7047
7578
  onLogout: authenticated ? handlers.onLogout : void 0,
7048
- onPreauthorize: state.isGuestFlow ? handlers.onPreauthorize : void 0
7579
+ onPreauthorize: state.isGuestFlow && isDesktop ? handlers.onPreauthorize : void 0
7049
7580
  }
7050
7581
  );
7051
7582
  }
@@ -7444,6 +7975,9 @@ function useTransferHandlers(deps) {
7444
7975
  sourceTokenAddress,
7445
7976
  activeCredentialId,
7446
7977
  selectedAccountId,
7978
+ selectedWalletId,
7979
+ selectedTokenSymbol,
7980
+ quoteId,
7447
7981
  transfer,
7448
7982
  accounts
7449
7983
  } = deps;
@@ -7457,9 +7991,32 @@ function useTransferHandlers(deps) {
7457
7991
  fetchProviders(apiBaseUrl, token)
7458
7992
  ]);
7459
7993
  const parsedAmt = depositAmount != null ? depositAmount : 0;
7460
- const defaults = resolveDepositSelection(accts, parsedAmt, selectedAccountId);
7461
- dispatch({ type: "ACCOUNTS_RELOADED", accounts: accts, providers: prov, defaults });
7462
- }, [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
+ ]);
7463
8020
  const handlePay = useCallback(async (payAmount, sourceOverrides) => {
7464
8021
  const minUsd = effectiveMinTransferAmountUsd(depositAmount);
7465
8022
  if (isNaN(payAmount) || payAmount < minUsd) {
@@ -7503,7 +8060,8 @@ function useTransferHandlers(deps) {
7503
8060
  sourceId: effectiveSourceId,
7504
8061
  sourceTokenAddress,
7505
8062
  destination,
7506
- amount: payAmount
8063
+ amount: payAmount,
8064
+ ...quoteId ? { quoteId } : {}
7507
8065
  });
7508
8066
  dispatch({ type: "TRANSFER_CREATED", transfer: t });
7509
8067
  if (t.status === "COMPLETED") {
@@ -7535,6 +8093,7 @@ function useTransferHandlers(deps) {
7535
8093
  accounts,
7536
8094
  destination,
7537
8095
  apiBaseUrl,
8096
+ quoteId,
7538
8097
  getAccessToken,
7539
8098
  transferSigning,
7540
8099
  polling,
@@ -7585,20 +8144,20 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7585
8144
  const selectSourceAvailableBalance = useMemo(() => {
7586
8145
  if (!pendingSelectSourceAction) return 0;
7587
8146
  const options2 = pendingSelectSourceAction.metadata?.options ?? [];
7588
- const recommended = selectSourceRecommended;
7589
- if (recommended) {
7590
- const match = options2.find(
7591
- (opt) => opt.chainName === recommended.chainName && opt.tokenSymbol === recommended.tokenSymbol
7592
- );
7593
- if (match) return Number(match.rawBalance) / Math.pow(10, match.decimals);
7594
- }
7595
- let max = 0;
7596
- for (const opt of options2) {
7597
- const bal = Number(opt.rawBalance) / Math.pow(10, opt.decimals);
7598
- if (bal > max) max = bal;
7599
- }
7600
- return max;
7601
- }, [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
+ ]);
7602
8161
  const handleSelectSourceChainChange = useCallback(
7603
8162
  (chainName) => {
7604
8163
  setSelectSourceChainName(chainName);
@@ -7649,7 +8208,7 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7649
8208
  initializedSelectSourceActionRef
7650
8209
  };
7651
8210
  }
7652
- function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete) {
8211
+ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete, lastResumedAt) {
7653
8212
  const {
7654
8213
  mobileSetupFlowRef,
7655
8214
  handlingMobileReturnRef,
@@ -7660,6 +8219,8 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7660
8219
  onCompleteRef.current = onComplete;
7661
8220
  const guestPollingActiveRef = useRef(false);
7662
8221
  const guestPollingCleanupRef = useRef(null);
8222
+ const mobileFlowRef = useRef(guestCheckout.mobileFlow);
8223
+ mobileFlowRef.current = guestCheckout.mobileFlow;
7663
8224
  const guestTransferIdRef = useRef(guestCheckout.guestTransferId);
7664
8225
  guestTransferIdRef.current = guestCheckout.guestTransferId;
7665
8226
  const guestSessionTokenRef = useRef(guestCheckout.guestSessionToken);
@@ -7715,27 +8276,15 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7715
8276
  startGuestPolling
7716
8277
  ]);
7717
8278
  useEffect(() => {
7718
- const tryStartPolling = () => {
7719
- if (guestPollingActiveRef.current) return;
7720
- const transferId = guestTransferIdRef.current;
7721
- const token = guestSessionTokenRef.current;
7722
- if (transferId && token) {
7723
- startGuestPolling(transferId, token);
7724
- }
7725
- };
7726
- const handleVisibility = () => {
7727
- if (document.visibilityState === "visible") tryStartPolling();
7728
- };
7729
- const handlePageShow = (e) => {
7730
- if (e.persisted) tryStartPolling();
7731
- };
7732
- document.addEventListener("visibilitychange", handleVisibility);
7733
- window.addEventListener("pageshow", handlePageShow);
7734
- return () => {
7735
- document.removeEventListener("visibilitychange", handleVisibility);
7736
- window.removeEventListener("pageshow", handlePageShow);
7737
- };
7738
- }, [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]);
7739
8288
  const handleAuthorizedMobileReturn = useCallback(async (authorizedTransfer, isSetup) => {
7740
8289
  if (handlingMobileReturnRef.current) return;
7741
8290
  handlingMobileReturnRef.current = true;
@@ -7922,9 +8471,6 @@ function useProviderHandlers(deps) {
7922
8471
  useWalletConnector: useWalletConnectorProp,
7923
8472
  userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
7924
8473
  });
7925
- if (!isMobile) {
7926
- dispatch({ type: "PAY_STARTED" });
7927
- }
7928
8474
  try {
7929
8475
  let accountId;
7930
8476
  let sessionId;
@@ -7963,18 +8509,22 @@ function useProviderHandlers(deps) {
7963
8509
  triggerDeeplink(sessionUri);
7964
8510
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: sessionUri });
7965
8511
  } else {
8512
+ dispatch({
8513
+ type: "SELECT_ACCOUNT",
8514
+ accountId,
8515
+ walletId: null
8516
+ });
7966
8517
  await authExecutor.executeSessionById(sessionId);
7967
8518
  await reloadAccounts();
7968
8519
  }
7969
8520
  } catch (err) {
8521
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8522
+ return;
8523
+ }
7970
8524
  captureException(err);
7971
8525
  const msg = err instanceof Error ? err.message : "Failed to set up wallet";
7972
8526
  dispatch({ type: "PAY_ERROR", error: msg });
7973
8527
  onError?.(msg);
7974
- } finally {
7975
- if (!isMobile) {
7976
- dispatch({ type: "PAY_ENDED" });
7977
- }
7978
8528
  }
7979
8529
  }, [
7980
8530
  authenticated,
@@ -8037,7 +8587,12 @@ function useProviderHandlers(deps) {
8037
8587
  }
8038
8588
  const acct = accounts.find((a) => a.id === selectedAccountId);
8039
8589
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8040
- 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" });
8041
8596
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8042
8597
  }
8043
8598
  dispatch({ type: "SET_ERROR", error: null });
@@ -8057,10 +8612,6 @@ function useProviderHandlers(deps) {
8057
8612
  activeCredentialId,
8058
8613
  { tokenAddress: source?.address, chainId: evmChainId }
8059
8614
  );
8060
- const isMobile = !shouldUseWalletConnector({
8061
- useWalletConnector: useWalletConnectorProp,
8062
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8063
- });
8064
8615
  if (isMobile) {
8065
8616
  handlingMobileReturnRef.current = false;
8066
8617
  mobileSetupFlowRef.current = true;
@@ -8077,12 +8628,20 @@ function useProviderHandlers(deps) {
8077
8628
  });
8078
8629
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8079
8630
  triggerDeeplink(session.uri);
8631
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8080
8632
  } else {
8081
8633
  await authExecutor.executeSessionById(session.id);
8082
8634
  await reloadAccounts();
8635
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8083
8636
  }
8084
8637
  } catch (err) {
8638
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8639
+ return;
8640
+ }
8085
8641
  captureException(err);
8642
+ if (isMobile) {
8643
+ dispatch({ type: "RESTORE_SELECTION" });
8644
+ }
8086
8645
  const msg = err instanceof Error ? err.message : "Failed to increase limit";
8087
8646
  dispatch({ type: "SET_ERROR", error: msg });
8088
8647
  onError?.(msg);
@@ -8127,7 +8686,12 @@ function useProviderHandlers(deps) {
8127
8686
  }
8128
8687
  const acct = accounts.find((a) => a.id === selectedAccountId);
8129
8688
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8130
- 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" });
8131
8695
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8132
8696
  }
8133
8697
  dispatch({ type: "SET_ERROR", error: null });
@@ -8142,10 +8706,6 @@ function useProviderHandlers(deps) {
8142
8706
  activeCredentialId,
8143
8707
  { tokenAddress, chainId }
8144
8708
  );
8145
- const isMobile = !shouldUseWalletConnector({
8146
- useWalletConnector: useWalletConnectorProp,
8147
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8148
- });
8149
8709
  if (isMobile) {
8150
8710
  handlingMobileReturnRef.current = false;
8151
8711
  mobileSetupFlowRef.current = true;
@@ -8163,13 +8723,21 @@ function useProviderHandlers(deps) {
8163
8723
  });
8164
8724
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8165
8725
  triggerDeeplink(session.uri);
8726
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8166
8727
  } else {
8167
8728
  await authExecutor.executeSessionById(session.id);
8168
8729
  await reloadAccounts();
8169
8730
  dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
8731
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8170
8732
  }
8171
8733
  } catch (err) {
8734
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8735
+ return;
8736
+ }
8172
8737
  captureException(err);
8738
+ if (isMobile) {
8739
+ dispatch({ type: "RESTORE_SELECTION" });
8740
+ }
8173
8741
  const msg = err instanceof Error ? err.message : "Failed to authorize token";
8174
8742
  dispatch({ type: "SET_ERROR", error: msg });
8175
8743
  onError?.(msg);
@@ -9106,7 +9674,15 @@ function useDataLoadEffect(deps) {
9106
9674
  ]);
9107
9675
  if (cancelled) return;
9108
9676
  const parsedAmt = depositAmount != null ? depositAmount : 0;
9109
- 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
+ );
9110
9686
  const persisted = loadMobileFlowState();
9111
9687
  const clearMobile = persisted?.isSetup && accts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
9112
9688
  dispatch({
@@ -9115,7 +9691,8 @@ function useDataLoadEffect(deps) {
9115
9691
  accounts: accts,
9116
9692
  chains: chn,
9117
9693
  defaults,
9118
- clearMobileState: clearMobile
9694
+ clearMobileState: clearMobile,
9695
+ resetSelectedTokenSymbol
9119
9696
  });
9120
9697
  if (clearMobile) clearMobileFlowState();
9121
9698
  } catch (err) {
@@ -9144,6 +9721,8 @@ function useDataLoadEffect(deps) {
9144
9721
  apiBaseUrl,
9145
9722
  state.activeCredentialId,
9146
9723
  state.selectedAccountId,
9724
+ state.selectedWalletId,
9725
+ state.selectedTokenSymbol,
9147
9726
  depositAmount
9148
9727
  ]);
9149
9728
  useEffect(() => {
@@ -9372,25 +9951,16 @@ function useMobilePollingEffect(deps) {
9372
9951
  const poll = isReauth ? pollReauthorization : pollWalletActive;
9373
9952
  poll();
9374
9953
  const intervalId = window.setInterval(poll, POLL_INTERVAL_MS);
9375
- const handleVisibility = () => {
9376
- if (document.visibilityState === "visible" && !cancelled) poll();
9377
- };
9378
- const handlePageShow = (e) => {
9379
- if (e.persisted && !cancelled) poll();
9380
- };
9381
- document.addEventListener("visibilitychange", handleVisibility);
9382
- window.addEventListener("pageshow", handlePageShow);
9383
9954
  return () => {
9384
9955
  cancelled = true;
9385
9956
  window.clearInterval(intervalId);
9386
- document.removeEventListener("visibilitychange", handleVisibility);
9387
- window.removeEventListener("pageshow", handlePageShow);
9388
9957
  };
9389
9958
  }, [
9390
9959
  state.mobileFlow,
9391
9960
  state.isGuestFlow,
9392
9961
  state.guestPreauthSessionId,
9393
9962
  state.activeCredentialId,
9963
+ state.lastResumedAt,
9394
9964
  apiBaseUrl,
9395
9965
  reloadAccounts,
9396
9966
  dispatch,
@@ -9407,28 +9977,13 @@ function useMobilePollingEffect(deps) {
9407
9977
  const transferIdToResume = pollingTransferIdRef.current ?? state.transfer?.id;
9408
9978
  if (!transferIdToResume) return;
9409
9979
  if (!polling.isPolling) polling.startPolling(transferIdToResume);
9410
- const handleVisibility = () => {
9411
- if (document.visibilityState === "visible" && !handlingMobileReturnRef.current) {
9412
- polling.startPolling(transferIdToResume);
9413
- }
9414
- };
9415
- const handlePageShow = (e) => {
9416
- if (e.persisted && !handlingMobileReturnRef.current) {
9417
- polling.startPolling(transferIdToResume);
9418
- }
9419
- };
9420
- document.addEventListener("visibilitychange", handleVisibility);
9421
- window.addEventListener("pageshow", handlePageShow);
9422
- return () => {
9423
- document.removeEventListener("visibilitychange", handleVisibility);
9424
- window.removeEventListener("pageshow", handlePageShow);
9425
- };
9426
9980
  }, [
9427
9981
  state.mobileFlow,
9428
9982
  state.transfer?.id,
9429
9983
  state.isGuestFlow,
9430
9984
  state.guestPreauthSessionId,
9431
9985
  state.guestPreauthAccountId,
9986
+ state.lastResumedAt,
9432
9987
  polling.isPolling,
9433
9988
  polling.startPolling,
9434
9989
  handlingMobileReturnRef,
@@ -9439,6 +9994,7 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9439
9994
  const {
9440
9995
  dispatch,
9441
9996
  privyReady,
9997
+ lastResumedAt,
9442
9998
  mobileSetupFlowRef,
9443
9999
  setupAccountIdRef,
9444
10000
  startGuestAccountPolling
@@ -9449,68 +10005,43 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9449
10005
  console.info("[blink-sdk] guestPreauthMobileRestore: skipping, privyReady=false");
9450
10006
  return;
9451
10007
  }
9452
- const tryStart = (trigger) => {
9453
- if (startedRef.current) {
9454
- console.info("[blink-sdk] guestPreauthMobileRestore.tryStart: already started", { trigger });
9455
- return;
9456
- }
9457
- const persisted = loadMobileFlowState();
9458
- console.info("[blink-sdk] guestPreauthMobileRestore.tryStart", {
9459
- trigger,
9460
- hasPersistedState: !!persisted,
9461
- isGuestPreauth: persisted?.isGuestPreauth ?? false,
9462
- hasGuestSessionToken: !!persisted?.guestSessionToken,
9463
- hasSessionId: !!persisted?.sessionId,
9464
- accountId: persisted?.accountId ?? null
9465
- });
9466
- if (!persisted?.isGuestPreauth || !persisted.guestSessionToken || !persisted.sessionId) {
9467
- return;
9468
- }
9469
- startedRef.current = true;
9470
- mobileSetupFlowRef.current = true;
9471
- if (persisted.accountId) {
9472
- setupAccountIdRef.current = persisted.accountId;
9473
- }
9474
- if (persisted.accountId) {
9475
- dispatch({
9476
- type: "GUEST_PREAUTH_DETECTED",
9477
- accountId: persisted.accountId,
9478
- sessionId: persisted.sessionId
9479
- });
9480
- }
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) {
9481
10030
  dispatch({
9482
- type: "MOBILE_DEEPLINK_READY",
9483
- deeplinkUri: persisted.deeplinkUri
9484
- });
9485
- console.info("[blink-sdk] guestPreauthMobileRestore: starting guest account polling", {
9486
- trigger,
10031
+ type: "GUEST_PREAUTH_DETECTED",
10032
+ accountId: persisted.accountId,
9487
10033
  sessionId: persisted.sessionId
9488
10034
  });
9489
- startGuestAccountPolling(persisted.guestSessionToken, persisted.sessionId);
9490
- };
9491
- const onVisibility = () => {
9492
- console.info("[blink-sdk] guestPreauthMobileRestore: visibilitychange fired", {
9493
- visibilityState: document.visibilityState,
9494
- startedRef: startedRef.current
9495
- });
9496
- if (document.visibilityState === "visible") tryStart("visibilitychange");
9497
- };
9498
- const onPageShow = (e) => {
9499
- console.info("[blink-sdk] guestPreauthMobileRestore: pageshow fired", {
9500
- persisted: e.persisted,
9501
- visibilityState: document.visibilityState,
9502
- startedRef: startedRef.current
9503
- });
9504
- if (document.visibilityState === "visible") tryStart("pageshow");
9505
- };
9506
- tryStart("initial");
9507
- document.addEventListener("visibilitychange", onVisibility);
9508
- window.addEventListener("pageshow", onPageShow);
9509
- return () => {
9510
- document.removeEventListener("visibilitychange", onVisibility);
9511
- window.removeEventListener("pageshow", onPageShow);
9512
- };
9513
- }, [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]);
9514
10045
  }
9515
10046
  function useSelectSourceEffect(deps) {
9516
10047
  const {
@@ -9711,7 +10242,9 @@ function useGuestDesktopPreauthSessionEffect(deps) {
9711
10242
  function useGuestPreauthPhaseSyncEffect(deps) {
9712
10243
  const { state, dispatch, authExecutor, isDesktop, privyAuthenticated } = deps;
9713
10244
  useEffect(() => {
9714
- if (!state.guestPreauthorizing || !isDesktop) return;
10245
+ if (!isDesktop) return;
10246
+ const standardDesktopAuth = privyAuthenticated && state.activeCredentialId != null && !state.guestPreauthorizing;
10247
+ if (!state.guestPreauthorizing && !standardDesktopAuth) return;
9715
10248
  const pending = authExecutor.pendingSelectSource;
9716
10249
  if (pending) {
9717
10250
  if (state.phase.step === "token-picker") {
@@ -9735,7 +10268,7 @@ function useGuestPreauthPhaseSyncEffect(deps) {
9735
10268
  dispatch({ type: "SET_USER_INTENT", intent: { step: "one-tap-setup", action: null } });
9736
10269
  return;
9737
10270
  }
9738
- if (!privyAuthenticated && authExecutor.executing && state.guestPreauthAccountId) {
10271
+ if (state.guestPreauthorizing && !privyAuthenticated && authExecutor.executing && state.guestPreauthAccountId) {
9739
10272
  const accountId = state.guestPreauthAccountId;
9740
10273
  const p = state.phase;
9741
10274
  if (p.step === "wallet-setup" && p.mobile == null && p.guestDesktopExtension && p.accountId === accountId) {
@@ -9763,6 +10296,7 @@ function useGuestPreauthPhaseSyncEffect(deps) {
9763
10296
  state.guestPreauthorizing,
9764
10297
  state.phase,
9765
10298
  state.guestPreauthAccountId,
10299
+ state.activeCredentialId,
9766
10300
  isDesktop,
9767
10301
  privyAuthenticated,
9768
10302
  authExecutor.pendingSelectSource,
@@ -9811,7 +10345,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9811
10345
  useEffect(() => {
9812
10346
  if (!isDesktop || state.guestPreauthorizing) return;
9813
10347
  if (!state.privyAuthenticated || !state.activeCredentialId || !state.selectedAccountId) return;
9814
- const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && !authExecutor.pendingOneTapSetup;
10348
+ const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && (!authExecutor.pendingOneTapSetup || authExecutor.authorizationSessionStackDepth > 1);
9815
10349
  if (shouldPin && !state.standardDesktopInlineOpenWallet) {
9816
10350
  dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: true });
9817
10351
  return;
@@ -9829,6 +10363,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9829
10363
  authExecutor.executing,
9830
10364
  authExecutor.pendingSelectSource,
9831
10365
  authExecutor.pendingOneTapSetup,
10366
+ authExecutor.authorizationSessionStackDepth,
9832
10367
  dispatch
9833
10368
  ]);
9834
10369
  }
@@ -9847,7 +10382,84 @@ function useGuestAccountAutoPollingEffect(deps) {
9847
10382
  startPolling(guestSessionToken);
9848
10383
  }, [mobileFlow, isGuestFlow, guestSessionToken, isPolling, guestAccount, startPolling]);
9849
10384
  }
9850
- 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) {
9851
10463
  const { apiBaseUrl } = useBlinkConfig();
9852
10464
  const [guestAccount, setGuestAccount] = useState(null);
9853
10465
  const [isPolling, setIsPolling] = useState(false);
@@ -9945,36 +10557,71 @@ function useGuestAccountPolling(intervalMs = 3e3) {
9945
10557
  [poll, intervalMs, stopPolling]
9946
10558
  );
9947
10559
  useEffect(() => {
9948
- const handleVisibility = () => {
9949
- console.info("[blink-sdk] useGuestAccountPolling: visibilitychange fired", {
9950
- visibilityState: document.visibilityState,
9951
- hasToken: !!guestTokenRef.current,
9952
- hasSessionId: !!sessionIdRef.current
9953
- });
9954
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9955
- void poll();
9956
- }
9957
- };
9958
- const handlePageShow = (e) => {
9959
- console.info("[blink-sdk] useGuestAccountPolling: pageshow fired", {
9960
- persisted: e.persisted,
9961
- visibilityState: document.visibilityState,
9962
- hasToken: !!guestTokenRef.current,
9963
- 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" }
9964
10603
  });
9965
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9966
- 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);
9967
10615
  }
9968
- };
9969
- document.addEventListener("visibilitychange", handleVisibility);
9970
- window.addEventListener("pageshow", handlePageShow);
10616
+ }
10617
+ }, [enabled, walletId, sourceTokenAddress, destination, amount, apiBaseUrl, getAccessToken]);
10618
+ useEffect(() => {
10619
+ fetchQuote();
9971
10620
  return () => {
9972
- document.removeEventListener("visibilitychange", handleVisibility);
9973
- window.removeEventListener("pageshow", handlePageShow);
10621
+ abortRef.current?.abort();
9974
10622
  };
9975
- }, [poll]);
9976
- useEffect(() => () => stopPolling(), [stopPolling]);
9977
- return { guestAccount, error, isPolling, startPolling, stopPolling };
10623
+ }, [fetchQuote]);
10624
+ return { quoteId, quoteFee, quoteLoading };
9978
10625
  }
9979
10626
  function BlinkPayment(props) {
9980
10627
  const resetKey = useRef(0);
@@ -10014,7 +10661,7 @@ function BlinkPaymentInner({
10014
10661
  );
10015
10662
  const authExecutor = useAuthorizationExecutor();
10016
10663
  const polling = useTransferPolling();
10017
- const guestAccountPolling = useGuestAccountPolling();
10664
+ const guestAccountPolling = useGuestAccountPolling(3e3, state.lastResumedAt);
10018
10665
  const transferSigning = useTransferSigning();
10019
10666
  const mobileFlowRefs = {
10020
10667
  mobileSetupFlowRef: useRef(false),
@@ -10032,6 +10679,15 @@ function BlinkPaymentInner({
10032
10679
  state.accounts,
10033
10680
  state.knownCredentialIds
10034
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
+ });
10035
10691
  const transfer = useTransferHandlers({
10036
10692
  dispatch,
10037
10693
  getAccessToken,
@@ -10049,6 +10705,9 @@ function BlinkPaymentInner({
10049
10705
  sourceTokenAddress: derived.selectedSource?.address,
10050
10706
  activeCredentialId: state.activeCredentialId,
10051
10707
  selectedAccountId: state.selectedAccountId,
10708
+ selectedWalletId: state.selectedWalletId,
10709
+ selectedTokenSymbol: state.selectedTokenSymbol,
10710
+ quoteId: depositFee.quoteId,
10052
10711
  transfer: state.transfer,
10053
10712
  accounts: state.accounts
10054
10713
  });
@@ -10065,7 +10724,8 @@ function BlinkPaymentInner({
10065
10724
  guestTransferId: state.guestTransferId,
10066
10725
  guestSessionToken: state.guestSessionToken
10067
10726
  },
10068
- onComplete
10727
+ onComplete,
10728
+ state.lastResumedAt
10069
10729
  );
10070
10730
  const sourceSelection = useSourceSelectionHandlers(dispatch, authExecutor, {
10071
10731
  guestPreauthSessionId: state.guestPreauthSessionId,
@@ -10161,10 +10821,15 @@ function BlinkPaymentInner({
10161
10821
  dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
10162
10822
  }
10163
10823
  }, [depositAmount, dispatch]);
10824
+ usePageResumeEffect({
10825
+ dispatch,
10826
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef
10827
+ });
10164
10828
  usePrivySessionSyncEffect({ dispatch, ready, authenticated });
10165
10829
  useGuestPreauthMobileRestoreEffect({
10166
10830
  dispatch,
10167
10831
  privyReady: state.privyReady,
10832
+ lastResumedAt: state.lastResumedAt,
10168
10833
  mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
10169
10834
  setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
10170
10835
  startGuestAccountPolling: guestAccountPolling.startPolling
@@ -10177,25 +10842,24 @@ function BlinkPaymentInner({
10177
10842
  guestAccount: guestAccountPolling.guestAccount,
10178
10843
  startPolling: guestAccountPolling.startPolling
10179
10844
  });
10180
- const guestSessionTokenRef = useRef(state.guestSessionToken);
10181
- guestSessionTokenRef.current = state.guestSessionToken;
10182
- const guestPreauthSessionIdRef = useRef(state.guestPreauthSessionId);
10183
- guestPreauthSessionIdRef.current = state.guestPreauthSessionId;
10184
10845
  useEffect(() => {
10185
- const handleVisibility = () => {
10186
- if (document.visibilityState !== "visible") return;
10187
- const token = guestSessionTokenRef.current;
10188
- const sessionId = guestPreauthSessionIdRef.current;
10189
- if (!token) return;
10190
- if (guestAccountPolling.isPolling || guestAccountPolling.guestAccount) return;
10191
- console.info("[blink-sdk] warm-return: restarting guest account polling from React state", {
10192
- sessionId: sessionId ?? "(none \u2014 account-only mode)"
10193
- });
10194
- guestAccountPolling.startPolling(token, sessionId ?? void 0);
10195
- };
10196
- document.addEventListener("visibilitychange", handleVisibility);
10197
- return () => document.removeEventListener("visibilitychange", handleVisibility);
10198
- }, [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
+ ]);
10199
10863
  useEffect(() => {
10200
10864
  console.info("[blink-sdk] guestPreauthCompletion effect", {
10201
10865
  hasGuestAccount: !!guestAccountPolling.guestAccount,
@@ -10360,6 +11024,11 @@ function BlinkPaymentInner({
10360
11024
  onLogout: handleLogout,
10361
11025
  onNewPayment: handleNewPayment,
10362
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
+ },
10363
11032
  onSetAuthInput: auth.setAuthInput,
10364
11033
  onSetOtpCode: (code) => {
10365
11034
  auth.setOtpCode(code);
@@ -10393,7 +11062,8 @@ function BlinkPaymentInner({
10393
11062
  handleLogout,
10394
11063
  handleNewPayment,
10395
11064
  onDismiss,
10396
- dispatch
11065
+ dispatch,
11066
+ authExecutor
10397
11067
  ]);
10398
11068
  return /* @__PURE__ */ jsx(
10399
11069
  StepRenderer,
@@ -10442,13 +11112,16 @@ function BlinkPaymentInner({
10442
11112
  guestSettingSender: guestTransfer.settingSender,
10443
11113
  guestPendingEntry: guestTransfer.pendingGuestEntry,
10444
11114
  guestQuoteFee: guestTransfer.guestFee?.quote ?? null,
10445
- guestQuoteLoading: guestTransfer.guestQuoteLoading
11115
+ guestQuoteLoading: guestTransfer.guestQuoteLoading,
11116
+ depositQuoteId: depositFee.quoteId,
11117
+ depositQuoteFee: depositFee.quoteFee,
11118
+ depositQuoteLoading: depositFee.quoteLoading
10446
11119
  },
10447
11120
  handlers
10448
11121
  }
10449
11122
  );
10450
11123
  }
10451
11124
 
10452
- 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 };
10453
11126
  //# sourceMappingURL=index.js.map
10454
11127
  //# sourceMappingURL=index.js.map