@swype-org/react-sdk 0.1.301 → 0.1.333

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -78,14 +78,16 @@ function getTheme(mode) {
78
78
  }
79
79
  var BLINK_PRIVY_APP_ID = "cmlil87uv004n0ck0blwumwek";
80
80
  var wagmiConfig = wagmi.createConfig({
81
- chains: [chains.mainnet, chains.arbitrum, chains.base, chains.polygon, chains.bsc],
81
+ // baseSepolia: guest/testnet flows (e.g. smokebox e2e) call switchChain to the source chain from the API.
82
+ chains: [chains.mainnet, chains.arbitrum, chains.base, chains.polygon, chains.bsc, chains.baseSepolia],
82
83
  connectors: [connectors.injected({ shimDisconnect: true, unstable_shimAsyncInject: 2e3 })],
83
84
  transports: {
84
85
  [chains.mainnet.id]: wagmi.http(),
85
86
  [chains.arbitrum.id]: wagmi.http(),
86
87
  [chains.base.id]: wagmi.http(),
87
88
  [chains.polygon.id]: wagmi.http(),
88
- [chains.bsc.id]: wagmi.http()
89
+ [chains.bsc.id]: wagmi.http(),
90
+ [chains.baseSepolia.id]: wagmi.http()
89
91
  }
90
92
  });
91
93
  var BlinkContext = react.createContext(null);
@@ -495,6 +497,7 @@ __export(api_exports, {
495
497
  getGuestTransfer: () => getGuestTransfer,
496
498
  getTransferByGuestToken: () => getTransferByGuestToken,
497
499
  postGuestTransferFeeQuote: () => postGuestTransferFeeQuote,
500
+ postTransferQuote: () => postTransferQuote,
498
501
  putGuestTransferSender: () => putGuestTransferSender,
499
502
  registerPasskey: () => registerPasskey,
500
503
  reportActionCompletion: () => reportActionCompletion,
@@ -610,7 +613,8 @@ async function createTransfer(apiBaseUrl, token, params) {
610
613
  amount: {
611
614
  amount: params.amount,
612
615
  currency: params.currency ?? "USD"
613
- }
616
+ },
617
+ ...params.quoteId ? { quoteId: params.quoteId } : {}
614
618
  };
615
619
  const res = await fetch(`${apiBaseUrl}/v1/transfers`, {
616
620
  method: "POST",
@@ -623,6 +627,28 @@ async function createTransfer(apiBaseUrl, token, params) {
623
627
  if (!res.ok) await throwApiError(res);
624
628
  return await res.json();
625
629
  }
630
+ async function postTransferQuote(apiBaseUrl, bearerToken, params) {
631
+ const body = {
632
+ walletId: params.walletId,
633
+ ...params.sourceTokenAddress ? { sourceTokenAddress: params.sourceTokenAddress } : {},
634
+ destination: {
635
+ chainId: params.destination.chainId,
636
+ token: { address: params.destination.token.address },
637
+ address: params.destination.address
638
+ },
639
+ amount: params.amount
640
+ };
641
+ const res = await fetch(`${apiBaseUrl}/v1/transfers/quotes`, {
642
+ method: "POST",
643
+ headers: {
644
+ "Content-Type": "application/json",
645
+ Authorization: `Bearer ${bearerToken}`
646
+ },
647
+ body: JSON.stringify(body)
648
+ });
649
+ if (!res.ok) await throwApiError(res);
650
+ return await res.json();
651
+ }
626
652
  async function fetchMerchantPublicKey(apiBaseUrl, merchantId) {
627
653
  const res = await fetch(
628
654
  `${apiBaseUrl}/v1/merchants/${encodeURIComponent(merchantId)}/public-key`
@@ -964,6 +990,23 @@ function normalizeSignature(sig) {
964
990
  );
965
991
  }
966
992
 
993
+ // src/authorizationErrors.ts
994
+ var AuthorizationSessionCancelledError = class extends Error {
995
+ constructor() {
996
+ super("Authorization session cancelled");
997
+ this.name = "AuthorizationSessionCancelledError";
998
+ }
999
+ };
1000
+ function isAuthorizationSessionCancelled(err) {
1001
+ if (err instanceof AuthorizationSessionCancelledError) return true;
1002
+ return typeof err === "object" && err !== null && "name" in err && err.name === "AuthorizationSessionCancelledError";
1003
+ }
1004
+ function isUserDismissedAuthorizationError(err) {
1005
+ if (!(err instanceof Error)) return false;
1006
+ const m = err.message.toLowerCase();
1007
+ return m.includes("authorization session aborted") || m.includes("aborted by user");
1008
+ }
1009
+
967
1010
  // src/hooks/authorizationExecutor.ts
968
1011
  var WALLET_CLIENT_MAX_ATTEMPTS = 25;
969
1012
  var WALLET_CLIENT_POLL_MS = 400;
@@ -1277,39 +1320,80 @@ function useAuthorizationExecutor(options) {
1277
1320
  const [error, setError] = react.useState(null);
1278
1321
  const [currentAction, setCurrentAction] = react.useState(null);
1279
1322
  const executingRef = react.useRef(false);
1323
+ const oneTapPauseActiveRef = react.useRef(false);
1324
+ const sessionStackRef = react.useRef([]);
1325
+ const [authorizationSessionStackDepth, setAuthorizationSessionStackDepth] = react.useState(0);
1280
1326
  const [pendingSelectSource, setPendingSelectSource] = react.useState(null);
1281
1327
  const selectSourceResolverRef = react.useRef(null);
1328
+ const selectSourceRejectRef = react.useRef(null);
1282
1329
  const sessionIdRef = react.useRef(null);
1283
1330
  const resolveSelectSource = react.useCallback((selection) => {
1284
1331
  if (selectSourceResolverRef.current) {
1285
1332
  selectSourceResolverRef.current(selection);
1286
1333
  selectSourceResolverRef.current = null;
1334
+ selectSourceRejectRef.current = null;
1287
1335
  setPendingSelectSource(null);
1288
1336
  }
1289
1337
  }, []);
1290
1338
  const waitForSelection = react.useCallback(
1291
- (action) => new Promise((resolve) => {
1292
- selectSourceResolverRef.current = resolve;
1339
+ (action) => new Promise((resolve, reject) => {
1340
+ selectSourceResolverRef.current = (selection) => {
1341
+ resolve(selection);
1342
+ selectSourceResolverRef.current = null;
1343
+ selectSourceRejectRef.current = null;
1344
+ };
1345
+ selectSourceRejectRef.current = reject;
1293
1346
  setPendingSelectSource(action);
1294
1347
  }),
1295
1348
  []
1296
1349
  );
1297
1350
  const [pendingOneTapSetup, setPendingOneTapSetup] = react.useState(null);
1298
1351
  const oneTapSetupResolverRef = react.useRef(null);
1352
+ const oneTapSetupRejectRef = react.useRef(null);
1299
1353
  const resolveOneTapSetup = react.useCallback(() => {
1300
1354
  if (oneTapSetupResolverRef.current) {
1301
1355
  oneTapSetupResolverRef.current();
1302
1356
  oneTapSetupResolverRef.current = null;
1357
+ oneTapSetupRejectRef.current = null;
1303
1358
  setPendingOneTapSetup(null);
1304
1359
  }
1305
1360
  }, []);
1306
1361
  const waitForOneTapSetup = react.useCallback(
1307
- (action) => new Promise((resolve) => {
1308
- oneTapSetupResolverRef.current = resolve;
1362
+ (action) => new Promise((resolve, reject) => {
1363
+ oneTapPauseActiveRef.current = true;
1364
+ oneTapSetupResolverRef.current = () => {
1365
+ oneTapPauseActiveRef.current = false;
1366
+ resolve();
1367
+ oneTapSetupResolverRef.current = null;
1368
+ oneTapSetupRejectRef.current = null;
1369
+ };
1370
+ oneTapSetupRejectRef.current = (reason) => {
1371
+ oneTapPauseActiveRef.current = false;
1372
+ reject(reason);
1373
+ };
1309
1374
  setPendingOneTapSetup(action);
1310
1375
  }),
1311
1376
  []
1312
1377
  );
1378
+ const cancelPendingExecution = react.useCallback(() => {
1379
+ if (selectSourceRejectRef.current) {
1380
+ selectSourceRejectRef.current(new AuthorizationSessionCancelledError());
1381
+ selectSourceRejectRef.current = null;
1382
+ selectSourceResolverRef.current = null;
1383
+ setPendingSelectSource(null);
1384
+ }
1385
+ if (oneTapSetupRejectRef.current) {
1386
+ oneTapPauseActiveRef.current = false;
1387
+ oneTapSetupRejectRef.current(new AuthorizationSessionCancelledError());
1388
+ oneTapSetupRejectRef.current = null;
1389
+ oneTapSetupResolverRef.current = null;
1390
+ setPendingOneTapSetup(null);
1391
+ }
1392
+ setError(null);
1393
+ setCurrentAction(null);
1394
+ setExecuting(false);
1395
+ executingRef.current = false;
1396
+ }, []);
1313
1397
  const dispatchAction = react.useCallback(
1314
1398
  async (action) => {
1315
1399
  setCurrentAction(action);
@@ -1344,20 +1428,26 @@ function useAuthorizationExecutor(options) {
1344
1428
  );
1345
1429
  const executeSessionById = react.useCallback(
1346
1430
  async (sessionId) => {
1347
- if (executingRef.current) return;
1348
- executingRef.current = true;
1349
1431
  if (!sessionId) {
1350
- executingRef.current = false;
1351
1432
  throw new Error("No authorization session id provided.");
1352
1433
  }
1353
1434
  if (!apiBaseUrl) {
1354
- executingRef.current = false;
1355
1435
  throw new Error("Missing apiBaseUrl. Provide useAuthorizationExecutor({ apiBaseUrl }) or wrap in <BlinkProvider>.");
1356
1436
  }
1357
- sessionIdRef.current = sessionId;
1358
- setExecuting(true);
1437
+ const allowNested = executingRef.current && oneTapPauseActiveRef.current;
1438
+ if (executingRef.current && !allowNested) {
1439
+ return;
1440
+ }
1441
+ const isNestedRun = allowNested;
1442
+ if (!isNestedRun) {
1443
+ executingRef.current = true;
1444
+ setExecuting(true);
1445
+ setResults([]);
1446
+ }
1359
1447
  setError(null);
1360
- setResults([]);
1448
+ sessionStackRef.current.push(sessionId);
1449
+ setAuthorizationSessionStackDepth(sessionStackRef.current.length);
1450
+ sessionIdRef.current = sessionId;
1361
1451
  try {
1362
1452
  let session = await fetchAuthorizationSession(apiBaseUrl, sessionId);
1363
1453
  const allResults = [];
@@ -1393,13 +1483,23 @@ function useAuthorizationExecutor(options) {
1393
1483
  pending = getPendingActions(session, completedIds);
1394
1484
  }
1395
1485
  } catch (err) {
1396
- const msg = err instanceof Error ? err.message : "Authorization failed";
1397
- setError(msg);
1486
+ if (isAuthorizationSessionCancelled(err)) {
1487
+ setError(null);
1488
+ } else {
1489
+ const msg = err instanceof Error ? err.message : "Authorization failed";
1490
+ setError(msg);
1491
+ }
1398
1492
  throw err;
1399
1493
  } finally {
1494
+ sessionStackRef.current.pop();
1495
+ const depth = sessionStackRef.current.length;
1496
+ setAuthorizationSessionStackDepth(depth);
1497
+ sessionIdRef.current = depth > 0 ? sessionStackRef.current[depth - 1] : null;
1400
1498
  setCurrentAction(null);
1401
- setExecuting(false);
1402
- executingRef.current = false;
1499
+ if (depth === 0) {
1500
+ setExecuting(false);
1501
+ executingRef.current = false;
1502
+ }
1403
1503
  }
1404
1504
  },
1405
1505
  [apiBaseUrl, dispatchAction]
@@ -1413,7 +1513,9 @@ function useAuthorizationExecutor(options) {
1413
1513
  resolveSelectSource,
1414
1514
  pendingOneTapSetup,
1415
1515
  resolveOneTapSetup,
1416
- executeSessionById
1516
+ executeSessionById,
1517
+ cancelPendingExecution,
1518
+ authorizationSessionStackDepth
1417
1519
  };
1418
1520
  }
1419
1521
  var TRANSFER_SIGN_MAX_POLLS = 60;
@@ -1643,6 +1745,37 @@ function getPreferredDepositWallet(account, transferAmount) {
1643
1745
  function getDepositEligibleAccounts(accounts) {
1644
1746
  return accounts.filter((account) => getAddressableWallets(account).length > 0);
1645
1747
  }
1748
+ function resolveDepositSelectionAfterRefresh(accounts, transferAmount, prev) {
1749
+ const { selectedAccountId, selectedWalletId, selectedTokenSymbol } = prev;
1750
+ if (selectedAccountId && selectedWalletId) {
1751
+ const acct = accounts.find((a) => a.id === selectedAccountId);
1752
+ const wallet = acct?.wallets.find((w) => w.id === selectedWalletId);
1753
+ if (wallet) {
1754
+ if (selectedTokenSymbol) {
1755
+ const hasToken = wallet.sources.some((s) => s.token.symbol === selectedTokenSymbol);
1756
+ if (hasToken) {
1757
+ return {
1758
+ defaults: { accountId: selectedAccountId, walletId: selectedWalletId },
1759
+ resetSelectedTokenSymbol: false
1760
+ };
1761
+ }
1762
+ const fallback = resolveDepositSelection(accounts, transferAmount, selectedAccountId);
1763
+ return {
1764
+ defaults: fallback,
1765
+ resetSelectedTokenSymbol: true
1766
+ };
1767
+ }
1768
+ return {
1769
+ defaults: { accountId: selectedAccountId, walletId: selectedWalletId },
1770
+ resetSelectedTokenSymbol: false
1771
+ };
1772
+ }
1773
+ }
1774
+ return {
1775
+ defaults: resolveDepositSelection(accounts, transferAmount, selectedAccountId),
1776
+ resetSelectedTokenSymbol: false
1777
+ };
1778
+ }
1646
1779
  function resolveDepositSelection(accounts, transferAmount, selectedAccountId) {
1647
1780
  const eligibleAccounts = getDepositEligibleAccounts(accounts);
1648
1781
  if (eligibleAccounts.length === 0) return null;
@@ -1709,6 +1842,32 @@ function buildSelectSourceChoices(options) {
1709
1842
  tokens: chain.tokens.filter((t) => t.balance > 0).sort((a, b) => b.balance - a.balance)
1710
1843
  })).filter((chain) => chain.tokens.length > 0).sort((a, b) => b.balance - a.balance);
1711
1844
  }
1845
+ function resolveSelectSourceAvailableBalance(choices, options, chainName, tokenSymbol, recommended) {
1846
+ const chain = chainName.trim();
1847
+ const token = tokenSymbol.trim();
1848
+ if (chain && token) {
1849
+ const chainChoice = choices.find((c) => c.chainName === chain);
1850
+ const row = chainChoice?.tokens.find((t) => t.tokenSymbol === token);
1851
+ if (row !== void 0) return row.balance;
1852
+ const direct = options.find(
1853
+ (opt) => opt.chainName === chain && opt.tokenSymbol === token
1854
+ );
1855
+ if (direct) return parseRawBalance(direct.rawBalance, direct.decimals);
1856
+ return 0;
1857
+ }
1858
+ if (recommended) {
1859
+ const match = options.find(
1860
+ (opt) => opt.chainName === recommended.chainName && opt.tokenSymbol === recommended.tokenSymbol
1861
+ );
1862
+ if (match) return parseRawBalance(match.rawBalance, match.decimals);
1863
+ }
1864
+ let max = 0;
1865
+ for (const opt of options) {
1866
+ const bal = parseRawBalance(opt.rawBalance, opt.decimals);
1867
+ if (bal > max) max = bal;
1868
+ }
1869
+ return max;
1870
+ }
1712
1871
 
1713
1872
  // src/walletFlow.ts
1714
1873
  var MOBILE_USER_AGENT_PATTERN = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
@@ -1792,11 +1951,12 @@ function resolvePhase(state) {
1792
1951
  const isFundingSourceSubflow = !state.loginRequested && (currentPhase.step === "token-picker" || currentPhase.step === "one-tap-setup" || currentPhase.step === "select-source" || currentPhase.step === "confirm-sign" || currentPhase.step === "guest-token-picker" && guestTokenPickerEligible);
1793
1952
  const walletPickerSwitchEligible = currentPhase.step === "wallet-picker" && currentPhase.reason === "switch" && !state.creatingTransfer && !(state.mobileFlow && state.deeplinkUri);
1794
1953
  const missingActivePasskeyCredential = state.passkeyConfigLoaded && !state.activeCredentialId;
1795
- const shouldPromptPasskeyVerification = missingActivePasskeyCredential && state.knownCredentialIds.length > 0 && state.passkeyPopupNeeded;
1954
+ const shouldPromptPasskeyVerification = missingActivePasskeyCredential && state.knownCredentialIds.length > 0;
1955
+ const guestPreauthClaimPending = state.guestPreauthAccountId != null && state.guestSessionToken != null && state.privyAuthenticated && state.activeCredentialId != null && !state.guestPreauthSetupCompletePending && !state.error;
1796
1956
  const branchGuestSetupComplete = state.guestPreauthSetupCompletePending && state.privyReady && state.privyAuthenticated;
1797
1957
  const branchKeepGuestPreauthPin = !branchGuestSetupComplete && guestPreauthPinsCurrentPhase;
1798
1958
  const branchGuestPostPayLogin = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && guestPostPayLogin;
1799
- const branchCompleted = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && transferCompleted && !state.verificationTarget && !needsPasskeyBootstrap;
1959
+ const branchCompleted = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && transferCompleted && !state.verificationTarget && !needsPasskeyBootstrap && !guestPreauthClaimPending && !state.loginRequested;
1800
1960
  const branchFailed = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && state.transfer?.status === "FAILED";
1801
1961
  const branchProcessing = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && (state.creatingTransfer || isTransferAwaitingCompletion(state.transfer));
1802
1962
  const branchStandardDesktopInlineOpenWallet = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && state.standardDesktopInlineOpenWallet && state.privyAuthenticated && state.activeCredentialId != null && state.selectedAccountId != null && !state.loginRequested && !state.guestPreauthorizing;
@@ -1812,7 +1972,7 @@ function resolvePhase(state) {
1812
1972
  const branchLoginRequested = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && state.loginRequested;
1813
1973
  const branchPasskeyVerify = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && shouldPromptPasskeyVerification;
1814
1974
  const branchPasskeyCreate = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && !branchPasskeyVerify && missingActivePasskeyCredential;
1815
- 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;
1975
+ const branchGuestPreauthClaiming = !branchGuestSetupComplete && !branchKeepGuestPreauthPin && !branchGuestPostPayLogin && !branchCompleted && !branchFailed && !branchProcessing && !branchKeepFundingSubflow && !branchMobileWalletSetup && !branchStandardDesktopInlineOpenWallet && !branchKeepGuestPreauthDesktopOpenWallet && !branchKeepGuestWalletSetup && !branchGuestTokenPicker && !branchKeepWalletPickerSwitch && !branchInitializingPrivy && !branchInitializingPasskeyConfig && !branchOtpVerify && !branchLoginRequested && !branchPasskeyVerify && !branchPasskeyCreate && guestPreauthClaimPending;
1816
1976
  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);
1817
1977
  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;
1818
1978
  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;
@@ -1983,6 +2143,7 @@ function createInitialState(config) {
1983
2143
  selectedAccountId: null,
1984
2144
  selectedWalletId: null,
1985
2145
  selectedTokenSymbol: null,
2146
+ savedSelection: null,
1986
2147
  amount: config.depositAmount != null ? config.depositAmount.toString() : "",
1987
2148
  transfer: null,
1988
2149
  creatingTransfer: false,
@@ -2009,7 +2170,8 @@ function createInitialState(config) {
2009
2170
  standardDesktopInlineOpenWallet: false,
2010
2171
  guestPreauthSetupCompletePending: false,
2011
2172
  privyReady: false,
2012
- privyAuthenticated: false
2173
+ privyAuthenticated: false,
2174
+ lastResumedAt: 0
2013
2175
  };
2014
2176
  }
2015
2177
  function paymentReducer(state, action) {
@@ -2107,6 +2269,9 @@ function applyAction(state, action) {
2107
2269
  next.mobileFlow = false;
2108
2270
  next.deeplinkUri = null;
2109
2271
  }
2272
+ if (action.resetSelectedTokenSymbol) {
2273
+ next.selectedTokenSymbol = null;
2274
+ }
2110
2275
  return next;
2111
2276
  }
2112
2277
  case "DATA_LOAD_END":
@@ -2121,8 +2286,43 @@ function applyAction(state, action) {
2121
2286
  next.selectedAccountId = action.defaults.accountId;
2122
2287
  next.selectedWalletId = action.defaults.walletId;
2123
2288
  }
2289
+ if (action.resetSelectedTokenSymbol) {
2290
+ next.selectedTokenSymbol = null;
2291
+ }
2124
2292
  return next;
2125
2293
  }
2294
+ case "SAVE_SELECTION":
2295
+ return {
2296
+ ...state,
2297
+ savedSelection: {
2298
+ selectedProviderId: state.selectedProviderId,
2299
+ selectedAccountId: state.selectedAccountId,
2300
+ selectedWalletId: state.selectedWalletId,
2301
+ selectedTokenSymbol: state.selectedTokenSymbol
2302
+ }
2303
+ };
2304
+ case "RESTORE_SELECTION": {
2305
+ const snap = state.savedSelection;
2306
+ if (!snap) {
2307
+ return {
2308
+ ...state,
2309
+ error: null,
2310
+ increasingLimit: false
2311
+ };
2312
+ }
2313
+ return {
2314
+ ...state,
2315
+ selectedProviderId: snap.selectedProviderId,
2316
+ selectedAccountId: snap.selectedAccountId,
2317
+ selectedWalletId: snap.selectedWalletId,
2318
+ selectedTokenSymbol: snap.selectedTokenSymbol,
2319
+ savedSelection: null,
2320
+ error: null,
2321
+ increasingLimit: false
2322
+ };
2323
+ }
2324
+ case "DISCARD_SAVED_SELECTION":
2325
+ return { ...state, savedSelection: null };
2126
2326
  // ── Source selection ──────────────────────────────────────────
2127
2327
  case "SELECT_PROVIDER":
2128
2328
  return {
@@ -2417,7 +2617,8 @@ function applyAction(state, action) {
2417
2617
  oneTapLimitSavedDuringSetup: false,
2418
2618
  guestPreauthorizing: false,
2419
2619
  guestPreauthSetupCompletePending: false,
2420
- standardDesktopInlineOpenWallet: false
2620
+ standardDesktopInlineOpenWallet: false,
2621
+ savedSelection: null
2421
2622
  };
2422
2623
  case "LOGOUT":
2423
2624
  return {
@@ -2440,6 +2641,8 @@ function applyAction(state, action) {
2440
2641
  };
2441
2642
  case "SYNC_AMOUNT":
2442
2643
  return { ...state, amount: action.amount };
2644
+ case "PAGE_RESUMED":
2645
+ return { ...state, lastResumedAt: Date.now() };
2443
2646
  default:
2444
2647
  return state;
2445
2648
  }
@@ -2529,6 +2732,41 @@ function screenForPhase(phase) {
2529
2732
  return "guest-preauth-linking";
2530
2733
  }
2531
2734
  }
2735
+
2736
+ // src/depositTokenSelection.ts
2737
+ function tokenOptionsForLinkedAccount(selectedAccount, chains) {
2738
+ if (!selectedAccount) return [];
2739
+ return selectedAccount.wallets.flatMap((w) => {
2740
+ const evmChainId = chains.find((c) => c.name === w.chain.name)?.commonId ?? void 0;
2741
+ return w.sources.filter((s) => s.balance.total.amount > 0).map((s) => ({
2742
+ symbol: s.token.symbol,
2743
+ chainName: w.chain.name,
2744
+ balance: s.balance.available.amount,
2745
+ walletId: w.id,
2746
+ status: s.token.status,
2747
+ tokenAddress: s.address,
2748
+ chainId: evmChainId
2749
+ }));
2750
+ });
2751
+ }
2752
+ function handleInlineTokenSelection(handlers, chains, selectedAccount, symbol, chainName, walletId) {
2753
+ if (walletId && selectedAccount) {
2754
+ const wallet = selectedAccount.wallets.find((w) => w.id === walletId);
2755
+ if (wallet && wallet.chain.name === chainName) {
2756
+ const source = wallet.sources.find((s) => s.token.symbol === symbol);
2757
+ const evmChainId = chains.find((c) => c.name === chainName)?.commonId ?? void 0;
2758
+ const authorized = !source?.token.status || source.token.status === "AUTHORIZED";
2759
+ if (source && !authorized && source.address && evmChainId != null) {
2760
+ void handlers.onAuthorizeToken(walletId, source.address, evmChainId, symbol);
2761
+ return;
2762
+ }
2763
+ }
2764
+ handlers.onSelectAuthorizedToken(walletId, symbol);
2765
+ return;
2766
+ }
2767
+ handlers.onSelectSourceChainChange(chainName);
2768
+ handlers.onSetSelectSourceTokenSymbol(symbol);
2769
+ }
2532
2770
  var MUTED = "#7fa4b0";
2533
2771
  var LOGO_SIZE = 48;
2534
2772
  function BlinkLoadingScreen() {
@@ -3522,16 +3760,33 @@ function PasskeyScreen({
3522
3760
  creating,
3523
3761
  error,
3524
3762
  popupFallback = false,
3525
- onCreatePasskeyViaPopup
3763
+ onCreatePasskeyViaPopup,
3764
+ onCreateNewPasskey,
3765
+ onCreateNewPasskeyViaPopup,
3766
+ createNewPopupFallback,
3767
+ creatingNewPasskey
3526
3768
  }) {
3527
3769
  const { tokens } = useBlinkConfig();
3528
3770
  const handleCreate = popupFallback && onCreatePasskeyViaPopup ? onCreatePasskeyViaPopup : onCreatePasskey;
3529
3771
  const buttonLabel = popupFallback ? "Open Passkey Window" : "Verify Passkey";
3772
+ const showCreateNewLink = onCreateNewPasskey != null;
3773
+ const usePopupForNew = createNewPopupFallback ?? popupFallback;
3774
+ const handleCreateNew = usePopupForNew && onCreateNewPasskeyViaPopup ? onCreateNewPasskeyViaPopup : onCreateNewPasskey;
3530
3775
  return /* @__PURE__ */ jsxRuntime.jsxs(
3531
3776
  ScreenLayout,
3532
3777
  {
3533
3778
  footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3534
- /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: handleCreate, disabled: creating, loading: creating, children: buttonLabel }),
3779
+ /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: handleCreate, disabled: creating || creatingNewPasskey, loading: creating, children: buttonLabel }),
3780
+ showCreateNewLink && /* @__PURE__ */ jsxRuntime.jsx(
3781
+ "button",
3782
+ {
3783
+ type: "button",
3784
+ onClick: handleCreateNew,
3785
+ disabled: creatingNewPasskey || creating,
3786
+ style: createNewLinkStyle(tokens.textMuted),
3787
+ children: creatingNewPasskey ? "Creating passkey\u2026" : "Create a new passkey for this device"
3788
+ }
3789
+ ),
3535
3790
  /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
3536
3791
  ] }),
3537
3792
  children: [
@@ -3566,6 +3821,17 @@ var subtitleStyle3 = (color) => ({
3566
3821
  margin: "0 0 20px",
3567
3822
  lineHeight: 1.5
3568
3823
  });
3824
+ var createNewLinkStyle = (color) => ({
3825
+ background: "none",
3826
+ border: "none",
3827
+ color,
3828
+ fontSize: "0.85rem",
3829
+ cursor: "pointer",
3830
+ padding: "8px 16px",
3831
+ textDecoration: "underline",
3832
+ fontFamily: "inherit",
3833
+ width: "100%"
3834
+ });
3569
3835
  var errorBannerStyle2 = (tokens) => ({
3570
3836
  background: tokens.errorBg,
3571
3837
  border: `1px solid ${tokens.error}66`,
@@ -4081,6 +4347,22 @@ var loginButtonStyle = (accentColor) => ({
4081
4347
  fontFamily: "inherit",
4082
4348
  textDecoration: "underline"
4083
4349
  });
4350
+
4351
+ // src/setupScreenSelection.ts
4352
+ function matchesSetupTokenSelection(opt, selectedTokenSymbol, selectedChainName, allOptions) {
4353
+ if (!selectedTokenSymbol || opt.symbol !== selectedTokenSymbol) {
4354
+ return false;
4355
+ }
4356
+ const chain = selectedChainName?.trim();
4357
+ if (chain) {
4358
+ return opt.chainName === chain;
4359
+ }
4360
+ const sameSymbol = allOptions.filter((o) => o.symbol === selectedTokenSymbol);
4361
+ if (sameSymbol.length === 1) {
4362
+ return opt.chainName === sameSymbol[0].chainName;
4363
+ }
4364
+ return false;
4365
+ }
4084
4366
  var DEFAULT_MAX = 1e7;
4085
4367
  var ABSOLUTE_MIN = 0.01;
4086
4368
  var PRESETS = [100, 250, 1e3];
@@ -4096,13 +4378,16 @@ function SetupScreen({
4096
4378
  loading,
4097
4379
  error,
4098
4380
  selectedTokenSymbol,
4381
+ selectedChainName,
4099
4382
  tokenOptions,
4100
4383
  onSelectToken
4101
4384
  }) {
4102
4385
  const { tokens } = useBlinkConfig();
4103
4386
  const effectiveMax = DEFAULT_MAX;
4104
4387
  const effectiveMin = Math.min(ABSOLUTE_MIN, effectiveMax);
4105
- const [limit, setLimit] = react.useState(() => Math.min(availableBalance, effectiveMax));
4388
+ const autoLimit = Math.min(Math.max(availableBalance, 0), effectiveMax);
4389
+ const [userChosenLimit, setUserChosenLimit] = react.useState(null);
4390
+ const limit = userChosenLimit ?? autoLimit;
4106
4391
  const [activePreset, setActivePreset] = react.useState(null);
4107
4392
  const [showAdvanced, setShowAdvanced] = react.useState(false);
4108
4393
  const [editing, setEditing] = react.useState(false);
@@ -4141,20 +4426,22 @@ function SetupScreen({
4141
4426
  const commitEdit = react.useCallback(() => {
4142
4427
  const parsed = parseFloat(inputValue);
4143
4428
  if (!isNaN(parsed)) {
4144
- setLimit(Math.min(effectiveMax, Math.max(effectiveMin, Math.round(parsed * 100) / 100)));
4429
+ setUserChosenLimit(
4430
+ Math.min(effectiveMax, Math.max(effectiveMin, Math.round(parsed * 100) / 100))
4431
+ );
4145
4432
  }
4146
4433
  setActivePreset(null);
4147
4434
  setEditing(false);
4148
4435
  }, [inputValue, effectiveMax, effectiveMin]);
4149
4436
  const selectPreset = (value) => {
4150
- setLimit(Math.min(value, effectiveMax));
4437
+ setUserChosenLimit(Math.min(value, effectiveMax));
4151
4438
  setActivePreset(value);
4152
4439
  };
4153
4440
  const selectMax = () => {
4154
- setLimit(Math.min(availableBalance, effectiveMax));
4441
+ setUserChosenLimit(autoLimit);
4155
4442
  setActivePreset("max");
4156
4443
  };
4157
- const selectedOption = tokenOptions?.find((o) => o.symbol === selectedTokenSymbol);
4444
+ const options = tokenOptions ?? [];
4158
4445
  return /* @__PURE__ */ jsxRuntime.jsxs(
4159
4446
  ScreenLayout,
4160
4447
  {
@@ -4187,12 +4474,7 @@ function SetupScreen({
4187
4474
  children: [
4188
4475
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenRowLabelStyle(tokens.text), children: "Token for one tap" }),
4189
4476
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenRowRightStyle, children: [
4190
- selectedOption && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: tokenRowSymbolStyle(tokens.textSecondary), children: [
4191
- selectedOption.symbol,
4192
- " \xB7 ",
4193
- selectedOption.chainName
4194
- ] }),
4195
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenIconWrapStyle, children: selectedTokenSymbol && TOKEN_LOGOS[selectedTokenSymbol] ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: TOKEN_LOGOS[selectedTokenSymbol], alt: selectedTokenSymbol, width: 36, height: 36, style: { borderRadius: "50%" } }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", children: [
4477
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenIconWrapStyle, children: selectedTokenSymbol && TOKEN_LOGOS[selectedTokenSymbol] ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: TOKEN_LOGOS[selectedTokenSymbol], alt: "", width: 36, height: 36, style: { borderRadius: "50%" } }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", children: [
4196
4478
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "18", cy: "18", r: "18", fill: "#2DB84B" }),
4197
4479
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: "18", y: "23", textAnchor: "middle", fontSize: "18", fill: "#fff", fontWeight: "700", children: "$" })
4198
4480
  ] }) }),
@@ -4204,14 +4486,21 @@ function SetupScreen({
4204
4486
  tokenDropdownOpen && tokenOptions && tokenOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: dropdownOuterStyle(tokens), children: [
4205
4487
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: dropdownLabelStyle(tokens.textMuted), children: "Choose token" }),
4206
4488
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: dropdownInnerStyle(tokens), children: tokenOptions.map((opt, index) => {
4207
- const selected = opt.symbol === selectedTokenSymbol;
4489
+ const selected = matchesSetupTokenSelection(
4490
+ opt,
4491
+ selectedTokenSymbol,
4492
+ selectedChainName,
4493
+ options
4494
+ );
4208
4495
  const isLast = index === tokenOptions.length - 1;
4496
+ const authorized = !opt.status || opt.status === "AUTHORIZED";
4209
4497
  return /* @__PURE__ */ jsxRuntime.jsxs(
4210
4498
  "button",
4211
4499
  {
4212
4500
  type: "button",
4213
4501
  onClick: () => handlePickToken(opt),
4214
4502
  style: dropdownRowStyle(tokens, selected, isLast),
4503
+ "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" : ""}`,
4215
4504
  children: [
4216
4505
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: dropdownRowLeftStyle, children: [
4217
4506
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: dropdownTokenIconStyle(tokens, !!TOKEN_LOGOS[opt.symbol]), children: TOKEN_LOGOS[opt.symbol] ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: TOKEN_LOGOS[opt.symbol], alt: opt.symbol, style: dropdownTokenLogoStyle }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: dropdownTokenFallbackStyle(tokens.textMuted), children: "$" }) }),
@@ -4221,9 +4510,12 @@ function SetupScreen({
4221
4510
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: dropdownTokenDotStyle(tokens.textMuted), children: "\xB7" }),
4222
4511
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: dropdownTokenChainStyle(tokens.textMuted), children: opt.chainName })
4223
4512
  ] }),
4224
- opt.balance != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: dropdownTokenBalanceStyle(tokens.textMuted), children: [
4225
- "$",
4226
- opt.balance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
4513
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: dropdownTokenBalanceRowStyle, children: [
4514
+ opt.balance != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: dropdownTokenBalanceStyle(tokens.textMuted), children: [
4515
+ "$",
4516
+ opt.balance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
4517
+ ] }),
4518
+ !authorized && /* @__PURE__ */ jsxRuntime.jsx("span", { style: dropdownNotAuthorizedStyle(tokens.textMuted), children: "Not authorized" })
4227
4519
  ] })
4228
4520
  ] })
4229
4521
  ] }),
@@ -4343,11 +4635,6 @@ var tokenRowRightStyle = {
4343
4635
  gap: 6,
4344
4636
  flexShrink: 0
4345
4637
  };
4346
- var tokenRowSymbolStyle = (color) => ({
4347
- fontSize: "0.84rem",
4348
- fontWeight: 500,
4349
- color
4350
- });
4351
4638
  var advancedToggleStyle = (accentColor) => ({
4352
4639
  display: "block",
4353
4640
  margin: "4px auto 24px",
@@ -4495,10 +4782,20 @@ var dropdownTokenChainStyle = (color) => ({
4495
4782
  fontWeight: 400,
4496
4783
  color
4497
4784
  });
4785
+ var dropdownTokenBalanceRowStyle = {
4786
+ display: "flex",
4787
+ alignItems: "center",
4788
+ gap: 8
4789
+ };
4498
4790
  var dropdownTokenBalanceStyle = (color) => ({
4499
4791
  fontSize: "0.78rem",
4500
4792
  color
4501
4793
  });
4794
+ var dropdownNotAuthorizedStyle = (color) => ({
4795
+ fontSize: "0.7rem",
4796
+ fontWeight: 500,
4797
+ color
4798
+ });
4502
4799
  var dropdownRadioEmptyStyle = (borderColor) => ({
4503
4800
  width: 22,
4504
4801
  height: 22,
@@ -4613,6 +4910,38 @@ var waitHintStyle = (color) => ({
4613
4910
  color,
4614
4911
  margin: 0
4615
4912
  });
4913
+
4914
+ // src/feeFormat.ts
4915
+ function isPreciseMoneyNonPositive(fee) {
4916
+ const raw = fee.value.trim();
4917
+ if (!/^-?\d+(\.\d*)?$/.test(raw)) return false;
4918
+ const n = Number(raw);
4919
+ return Number.isFinite(n) && n <= 0;
4920
+ }
4921
+ function formatNonPositiveFeeDisplay(fee) {
4922
+ if (fee.currency === "USD") return "Under $0.01";
4923
+ return `Less than 0.01 ${fee.currency}`;
4924
+ }
4925
+ function formatPreciseMoneyForDisplay(fee) {
4926
+ const raw = fee.value.trim();
4927
+ if (fee.currency === "USD") {
4928
+ if (!/^\d+(\.\d*)?$/.test(raw)) {
4929
+ return `$${raw}`;
4930
+ }
4931
+ const [whole, frac = ""] = raw.split(".");
4932
+ const dec = `${frac}00`.slice(0, 2);
4933
+ const intFmt = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
4934
+ return `$${intFmt}.${dec}`;
4935
+ }
4936
+ return `${raw} ${fee.currency}`;
4937
+ }
4938
+ function formatUsdTwoDecimals(value) {
4939
+ const n = Number(value);
4940
+ return (Number.isFinite(n) ? n : 0).toLocaleString("en-US", {
4941
+ minimumFractionDigits: 2,
4942
+ maximumFractionDigits: 2
4943
+ });
4944
+ }
4616
4945
  function DepositScreen({
4617
4946
  merchantName,
4618
4947
  availableBalance,
@@ -4620,6 +4949,8 @@ function DepositScreen({
4620
4949
  tokenCount,
4621
4950
  initialAmount,
4622
4951
  minDepositFloor,
4952
+ quoteFee,
4953
+ quoteLoading,
4623
4954
  processing,
4624
4955
  error,
4625
4956
  onDeposit,
@@ -4634,13 +4965,19 @@ function DepositScreen({
4634
4965
  onAuthorizeAccount,
4635
4966
  onAddProvider,
4636
4967
  onSelectToken,
4968
+ tokenOptions,
4969
+ onPickToken,
4637
4970
  selectedSourceLabel,
4638
- selectedTokenSymbol
4971
+ selectedTokenSymbol,
4972
+ selectedChainName
4639
4973
  }) {
4640
4974
  const { tokens } = useBlinkConfig();
4641
4975
  const amount = initialAmount;
4642
4976
  const [accountPickerOpen, setAccountPickerOpen] = react.useState(false);
4977
+ const [tokenPickerOpen, setTokenPickerOpen] = react.useState(false);
4643
4978
  const pickerRef = react.useRef(null);
4979
+ const tokenPickerRef = react.useRef(null);
4980
+ const hasTokenDropdown = tokenOptions != null && tokenOptions.length > 0 && onPickToken != null;
4644
4981
  react.useEffect(() => {
4645
4982
  if (!accountPickerOpen) return;
4646
4983
  const handleClick = (e) => {
@@ -4651,47 +4988,129 @@ function DepositScreen({
4651
4988
  document.addEventListener("mousedown", handleClick);
4652
4989
  return () => document.removeEventListener("mousedown", handleClick);
4653
4990
  }, [accountPickerOpen]);
4991
+ react.useEffect(() => {
4992
+ if (!tokenPickerOpen) return;
4993
+ const handleMouseDown = (e) => {
4994
+ if (tokenPickerRef.current && !tokenPickerRef.current.contains(e.target)) {
4995
+ setTokenPickerOpen(false);
4996
+ }
4997
+ };
4998
+ document.addEventListener("mousedown", handleMouseDown);
4999
+ return () => document.removeEventListener("mousedown", handleMouseDown);
5000
+ }, [tokenPickerOpen]);
5001
+ const handleTokenBadgeClick = react.useCallback(() => {
5002
+ if (hasTokenDropdown) {
5003
+ setTokenPickerOpen((v) => !v);
5004
+ setAccountPickerOpen(false);
5005
+ } else {
5006
+ onSelectToken?.();
5007
+ }
5008
+ }, [hasTokenDropdown, onSelectToken]);
5009
+ const handlePickToken = react.useCallback((opt) => {
5010
+ onPickToken?.(opt.symbol, opt.chainName, opt.walletId);
5011
+ setTokenPickerOpen(false);
5012
+ }, [onPickToken]);
4654
5013
  const selectedAccount = accounts?.find((a) => a.id === selectedAccountId);
4655
5014
  const selectedProviderName = selectedAccount?.name ?? "Wallet";
4656
5015
  const selectedProviderLogo = KNOWN_LOGOS[selectedProviderName.toLowerCase()];
4657
5016
  const totalAccountBalance = selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : availableBalance;
4658
5017
  const isLowBalance = availableBalance < minDepositFloor;
5018
+ const insufficientFunds = availableBalance < amount;
4659
5019
  const exceedsLimit = remainingLimit != null && amount > remainingLimit && !isLowBalance;
4660
- const canDeposit = amount >= minDepositFloor && !exceedsLimit && !isLowBalance && !processing;
5020
+ const canDeposit = amount >= minDepositFloor && !exceedsLimit && !isLowBalance && !insufficientFunds && !processing;
4661
5021
  return /* @__PURE__ */ jsxRuntime.jsxs(
4662
5022
  ScreenLayout,
4663
5023
  {
4664
5024
  footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4665
- !accountPickerOpen && (exceedsLimit && onIncreaseLimit ? /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onIncreaseLimit, loading: increasingLimit, children: "Increase limit" }) : /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: () => onDeposit(amount), disabled: !canDeposit, loading: processing, children: "Confirm" })),
5025
+ !accountPickerOpen && !tokenPickerOpen && (exceedsLimit && onIncreaseLimit ? /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onIncreaseLimit, loading: increasingLimit, children: "Increase limit" }) : /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: () => onDeposit(amount), disabled: !canDeposit, loading: processing, children: insufficientFunds ? "Insufficient Funds" : "Confirm" })),
4666
5026
  /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
4667
5027
  ] }),
4668
5028
  children: [
4669
5029
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onBack, onLogout }),
4670
5030
  /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: pickerRef, style: depositCardWrapStyle, children: [
4671
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5031
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: depositCardStyle(tokens), children: [
4672
5032
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: depositLabelStyle(tokens.textMuted), children: "Depositing" }),
4673
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: amountRowStyle, children: [
4674
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: amountValueStyle(tokens.text), children: [
4675
- "$",
4676
- amount.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5033
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: tokenPickerRef, children: [
5034
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: amountRowStyle, children: [
5035
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: amountValueStyle(tokens.text), children: [
5036
+ "$",
5037
+ formatUsdTwoDecimals(amount)
5038
+ ] }),
5039
+ /* @__PURE__ */ jsxRuntime.jsxs(
5040
+ "button",
5041
+ {
5042
+ type: "button",
5043
+ "data-testid": "deposit-token-badge",
5044
+ onClick: handleTokenBadgeClick,
5045
+ style: tokenIconButtonStyle(hasTokenDropdown || !!onSelectToken),
5046
+ "aria-expanded": hasTokenDropdown ? tokenPickerOpen : void 0,
5047
+ "aria-haspopup": hasTokenDropdown ? "listbox" : void 0,
5048
+ children: [
5049
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenIconWrapStyle2, children: selectedTokenSymbol && TOKEN_LOGOS[selectedTokenSymbol] ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: TOKEN_LOGOS[selectedTokenSymbol], alt: selectedTokenSymbol, width: 36, height: 36, style: { borderRadius: "50%" } }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", children: [
5050
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "18", cy: "18", r: "18", fill: "#2DB84B" }),
5051
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: "18", y: "23", textAnchor: "middle", fontSize: "18", fill: "#fff", fontWeight: "700", children: "$" })
5052
+ ] }) }),
5053
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", style: { opacity: 0.5 }, children: tokenPickerOpen ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 15l-6-6-6 6", stroke: tokens.textMuted, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 9l6 6 6-6", stroke: tokens.textMuted, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
5054
+ ]
5055
+ }
5056
+ )
4677
5057
  ] }),
4678
- /* @__PURE__ */ jsxRuntime.jsxs(
4679
- "button",
4680
- {
4681
- type: "button",
4682
- onClick: onSelectToken,
4683
- style: tokenIconButtonStyle(!!onSelectToken),
4684
- children: [
4685
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenIconWrapStyle2, children: selectedTokenSymbol && TOKEN_LOGOS[selectedTokenSymbol] ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: TOKEN_LOGOS[selectedTokenSymbol], alt: selectedTokenSymbol, width: 36, height: 36, style: { borderRadius: "50%" } }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "36", height: "36", viewBox: "0 0 36 36", fill: "none", children: [
4686
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "18", cy: "18", r: "18", fill: "#2DB84B" }),
4687
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: "18", y: "23", textAnchor: "middle", fontSize: "18", fill: "#fff", fontWeight: "700", children: "$" })
4688
- ] }) }),
4689
- /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", style: { opacity: 0.5 }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 9l6 6 6-6", stroke: tokens.textMuted, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
4690
- ]
4691
- }
4692
- )
5058
+ tokenPickerOpen && tokenOptions && tokenOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownStyle(tokens), children: [
5059
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownLabelStyle(tokens.textMuted), children: "Choose token" }),
5060
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownInnerStyle(tokens), children: tokenOptions.map((opt, index) => {
5061
+ const selected = opt.symbol === selectedTokenSymbol && (!selectedChainName || opt.chainName === selectedChainName);
5062
+ const isLast = index === tokenOptions.length - 1;
5063
+ const authorized = !opt.status || opt.status === "AUTHORIZED";
5064
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5065
+ "button",
5066
+ {
5067
+ type: "button",
5068
+ onClick: () => handlePickToken(opt),
5069
+ style: tokenDropdownRowStyle(tokens, selected, isLast),
5070
+ "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" : ""}`,
5071
+ children: [
5072
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownRowLeftStyle, children: [
5073
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownIconStyle(tokens, !!TOKEN_LOGOS[opt.symbol]), children: TOKEN_LOGOS[opt.symbol] ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: TOKEN_LOGOS[opt.symbol], alt: opt.symbol, style: tokenDropdownLogoStyle }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownFallbackStyle(tokens.textMuted), children: "$" }) }),
5074
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownInfoStyle, children: [
5075
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownNameRowStyle, children: [
5076
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownSymbolStyle(tokens.text), children: opt.symbol }),
5077
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownDotStyle(tokens.textMuted), children: "\xB7" }),
5078
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownChainStyle(tokens.textMuted), children: opt.chainName })
5079
+ ] }),
5080
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownBalanceRowStyle, children: [
5081
+ opt.balance != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: tokenDropdownBalanceStyle(tokens.textMuted), children: [
5082
+ "$",
5083
+ formatUsdTwoDecimals(opt.balance)
5084
+ ] }),
5085
+ !authorized && /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownNotAuthStyle(tokens.textMuted), children: "Not authorized" })
5086
+ ] })
5087
+ ] })
5088
+ ] }),
5089
+ selected ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", children: [
5090
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "11", fill: tokens.success }),
5091
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 11l3 3 5-5", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
5092
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownRadioStyle(tokens.border) })
5093
+ ]
5094
+ },
5095
+ `${opt.chainName}-${opt.symbol}`
5096
+ );
5097
+ }) })
5098
+ ] })
4693
5099
  ] }),
4694
- !accountPickerOpen && /* @__PURE__ */ jsxRuntime.jsxs(
5100
+ !accountPickerOpen && !tokenPickerOpen && (() => {
5101
+ if (quoteLoading) {
5102
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: feeRowContainerStyle, "aria-live": "polite", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle(tokens.textMuted), children: "Getting fee estimate\u2026" }) });
5103
+ }
5104
+ if (quoteFee) {
5105
+ const feeText = isPreciseMoneyNonPositive(quoteFee) ? formatNonPositiveFeeDisplay(quoteFee) : formatPreciseMoneyForDisplay(quoteFee);
5106
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: feeRowContainerStyle, "aria-live": "polite", children: [
5107
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle(tokens.textMuted), children: "Fee estimate: " }),
5108
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowAmountStyle(tokens.textSecondary), children: feeText })
5109
+ ] });
5110
+ }
5111
+ return null;
5112
+ })(),
5113
+ !accountPickerOpen && !tokenPickerOpen && /* @__PURE__ */ jsxRuntime.jsxs(
4695
5114
  "button",
4696
5115
  {
4697
5116
  type: "button",
@@ -4701,7 +5120,7 @@ function DepositScreen({
4701
5120
  selectedProviderLogo ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: selectedProviderLogo, alt: selectedProviderName, style: providerLogoStyle }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: providerFallbackStyle(tokens.textMuted), children: selectedProviderName.charAt(0) }),
4702
5121
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: walletBalanceStyle(tokens.text), children: [
4703
5122
  "$",
4704
- totalAccountBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5123
+ formatUsdTwoDecimals(totalAccountBalance)
4705
5124
  ] }),
4706
5125
  /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", style: { opacity: 0.4 }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 10l5-5 5 5M7 14l5 5 5-5", stroke: tokens.textMuted, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
4707
5126
  ]
@@ -4742,7 +5161,7 @@ function DepositScreen({
4742
5161
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: accountAddressStyle(tokens.text), children: truncatedAddress ?? account.nickname ?? account.name }),
4743
5162
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: accountBalanceTextStyle(tokens.textMuted), children: [
4744
5163
  "$",
4745
- accountBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5164
+ formatUsdTwoDecimals(accountBalance)
4746
5165
  ] })
4747
5166
  ] })
4748
5167
  ] }),
@@ -4778,21 +5197,21 @@ function DepositScreen({
4778
5197
  " ",
4779
5198
  /* @__PURE__ */ jsxRuntime.jsxs("strong", { style: { color: tokens.text }, children: [
4780
5199
  "$",
4781
- remainingLimit.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5200
+ formatUsdTwoDecimals(remainingLimit)
4782
5201
  ] }),
4783
5202
  exceedsLimit && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: limitExceededHintStyle(tokens.warning), children: [
4784
5203
  "Your deposit of $",
4785
- amount.toFixed(2),
5204
+ formatUsdTwoDecimals(amount),
4786
5205
  " exceeds your One-Tap limit of $",
4787
- remainingLimit?.toFixed(2) ?? "0.00",
5206
+ remainingLimit != null ? formatUsdTwoDecimals(remainingLimit) : "0.00",
4788
5207
  ". Increase your limit to continue."
4789
5208
  ] })
4790
5209
  ] }),
4791
5210
  !accountPickerOpen && isLowBalance && /* @__PURE__ */ jsxRuntime.jsxs(WarningBanner, { title: "Not enough funds", children: [
4792
5211
  "Your wallet balance is $",
4793
- availableBalance.toFixed(2),
5212
+ formatUsdTwoDecimals(availableBalance),
4794
5213
  " \u2014 you need at least $",
4795
- minDepositFloor.toFixed(2),
5214
+ formatUsdTwoDecimals(minDepositFloor),
4796
5215
  " to deposit via One-Tap."
4797
5216
  ] }),
4798
5217
  error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle6(tokens), children: error })
@@ -4804,6 +5223,11 @@ var depositCardWrapStyle = {
4804
5223
  position: "relative",
4805
5224
  marginBottom: 20
4806
5225
  };
5226
+ var depositCardStyle = (tokens) => ({
5227
+ border: `1px solid ${tokens.border}`,
5228
+ borderRadius: tokens.radiusLg,
5229
+ padding: "16px 20px"
5230
+ });
4807
5231
  var depositLabelStyle = (color) => ({
4808
5232
  fontSize: "0.75rem",
4809
5233
  fontWeight: 500,
@@ -4837,104 +5261,214 @@ var tokenIconWrapStyle2 = {
4837
5261
  width: 36,
4838
5262
  height: 36
4839
5263
  };
4840
- var walletBalanceRowStyle = {
4841
- display: "flex",
4842
- alignItems: "center",
4843
- justifyContent: "center",
4844
- gap: 8,
4845
- background: "transparent",
4846
- border: "none",
4847
- padding: 0,
4848
- width: "100%",
4849
- cursor: "pointer",
4850
- fontFamily: "inherit",
4851
- outline: "none"
4852
- };
4853
- var providerLogoStyle = {
4854
- width: 18,
4855
- height: 18,
4856
- borderRadius: "50%",
4857
- objectFit: "contain"
4858
- };
4859
- var providerFallbackStyle = (color) => ({
4860
- width: 18,
4861
- height: 18,
4862
- borderRadius: "50%",
4863
- display: "flex",
4864
- alignItems: "center",
4865
- justifyContent: "center",
4866
- fontSize: "0.65rem",
4867
- fontWeight: 700,
4868
- color,
4869
- flexShrink: 0
4870
- });
4871
- var walletBalanceStyle = (color) => ({
4872
- fontSize: "0.9rem",
4873
- fontWeight: 600,
4874
- color
4875
- });
4876
- var accountDropdownOuterStyle = (tokens) => ({
5264
+ var tokenDropdownStyle = (t) => ({
4877
5265
  marginTop: 4,
4878
- background: tokens.bgCard,
4879
- border: `1px solid ${tokens.border}`,
4880
- borderRadius: tokens.radiusLg,
4881
- boxShadow: tokens.shadowLg,
5266
+ marginBottom: 12,
5267
+ background: t.bgCard,
5268
+ border: `1px solid ${t.border}`,
5269
+ borderRadius: t.radiusLg,
5270
+ boxShadow: t.shadowLg,
4882
5271
  padding: "12px 14px 14px"
4883
5272
  });
4884
- var accountDropdownLabelStyle = (color) => ({
5273
+ var tokenDropdownLabelStyle = (color) => ({
4885
5274
  fontSize: "0.78rem",
4886
5275
  fontWeight: 500,
4887
5276
  color,
4888
5277
  marginBottom: 8
4889
5278
  });
4890
- var accountDropdownInnerStyle = (tokens) => ({
4891
- background: tokens.bgInput,
4892
- border: `1px solid ${tokens.border}`,
4893
- borderRadius: tokens.radiusLg,
5279
+ var tokenDropdownInnerStyle = (t) => ({
5280
+ background: t.bgInput,
5281
+ border: `1px solid ${t.border}`,
5282
+ borderRadius: t.radiusLg,
4894
5283
  overflow: "hidden"
4895
5284
  });
4896
- var accountRowStyle = (tokens, isSelected) => ({
5285
+ var tokenDropdownRowStyle = (t, isSelected, isLast) => ({
4897
5286
  display: "flex",
4898
5287
  alignItems: "center",
4899
5288
  justifyContent: "space-between",
4900
5289
  width: "100%",
4901
5290
  padding: "14px 16px",
4902
- background: isSelected ? `${tokens.accent}10` : "transparent",
5291
+ background: isSelected ? `${t.accent}18` : "transparent",
4903
5292
  border: "none",
4904
- borderBottom: `1px solid ${tokens.border}`,
5293
+ borderBottom: isLast ? "none" : `1px solid ${t.border}`,
4905
5294
  cursor: "pointer",
4906
5295
  fontFamily: "inherit",
4907
5296
  textAlign: "left",
4908
5297
  outline: "none"
4909
5298
  });
4910
- var accountRowLeftStyle = {
5299
+ var tokenDropdownRowLeftStyle = {
4911
5300
  display: "flex",
4912
5301
  alignItems: "center",
4913
5302
  gap: 12,
4914
5303
  minWidth: 0,
4915
5304
  flex: 1
4916
5305
  };
4917
- var accountLogoStyle = {
4918
- width: 28,
4919
- height: 28,
4920
- borderRadius: "50%",
4921
- objectFit: "contain",
4922
- flexShrink: 0
4923
- };
4924
- var accountFallbackIconStyle = (color) => ({
4925
- width: 28,
4926
- height: 28,
5306
+ var tokenDropdownIconStyle = (t, hasLogo) => ({
5307
+ width: 36,
5308
+ height: 36,
4927
5309
  borderRadius: "50%",
5310
+ border: hasLogo ? "none" : `1.5px solid ${t.border}`,
4928
5311
  display: "flex",
4929
5312
  alignItems: "center",
4930
5313
  justifyContent: "center",
4931
- fontSize: "0.75rem",
4932
- fontWeight: 700,
4933
- color,
4934
- flexShrink: 0
5314
+ flexShrink: 0,
5315
+ overflow: "hidden"
4935
5316
  });
4936
- var accountInfoStyle = {
4937
- display: "flex",
5317
+ var tokenDropdownLogoStyle = {
5318
+ width: 36,
5319
+ height: 36,
5320
+ borderRadius: "50%",
5321
+ objectFit: "cover"
5322
+ };
5323
+ var tokenDropdownFallbackStyle = (color) => ({
5324
+ fontSize: "1rem",
5325
+ fontWeight: 700,
5326
+ color
5327
+ });
5328
+ var tokenDropdownInfoStyle = {
5329
+ display: "flex",
5330
+ flexDirection: "column",
5331
+ gap: 2,
5332
+ minWidth: 0
5333
+ };
5334
+ var tokenDropdownNameRowStyle = {
5335
+ display: "flex",
5336
+ alignItems: "center",
5337
+ gap: 4
5338
+ };
5339
+ var tokenDropdownSymbolStyle = (color) => ({
5340
+ fontSize: "0.92rem",
5341
+ fontWeight: 600,
5342
+ color
5343
+ });
5344
+ var tokenDropdownDotStyle = (color) => ({
5345
+ fontSize: "0.8rem",
5346
+ color
5347
+ });
5348
+ var tokenDropdownChainStyle = (color) => ({
5349
+ fontSize: "0.84rem",
5350
+ fontWeight: 400,
5351
+ color
5352
+ });
5353
+ var tokenDropdownBalanceRowStyle = {
5354
+ display: "flex",
5355
+ alignItems: "center",
5356
+ gap: 8
5357
+ };
5358
+ var tokenDropdownBalanceStyle = (color) => ({
5359
+ fontSize: "0.78rem",
5360
+ color
5361
+ });
5362
+ var tokenDropdownNotAuthStyle = (color) => ({
5363
+ fontSize: "0.7rem",
5364
+ fontWeight: 500,
5365
+ color
5366
+ });
5367
+ var tokenDropdownRadioStyle = (borderColor) => ({
5368
+ width: 22,
5369
+ height: 22,
5370
+ borderRadius: "50%",
5371
+ border: `2px solid ${borderColor}`,
5372
+ flexShrink: 0
5373
+ });
5374
+ var walletBalanceRowStyle = {
5375
+ display: "flex",
5376
+ alignItems: "center",
5377
+ justifyContent: "center",
5378
+ gap: 8,
5379
+ background: "transparent",
5380
+ border: "none",
5381
+ padding: 0,
5382
+ width: "100%",
5383
+ cursor: "pointer",
5384
+ fontFamily: "inherit",
5385
+ outline: "none"
5386
+ };
5387
+ var providerLogoStyle = {
5388
+ width: 18,
5389
+ height: 18,
5390
+ borderRadius: "50%",
5391
+ objectFit: "contain"
5392
+ };
5393
+ var providerFallbackStyle = (color) => ({
5394
+ width: 18,
5395
+ height: 18,
5396
+ borderRadius: "50%",
5397
+ display: "flex",
5398
+ alignItems: "center",
5399
+ justifyContent: "center",
5400
+ fontSize: "0.65rem",
5401
+ fontWeight: 700,
5402
+ color,
5403
+ flexShrink: 0
5404
+ });
5405
+ var walletBalanceStyle = (color) => ({
5406
+ fontSize: "0.9rem",
5407
+ fontWeight: 600,
5408
+ color
5409
+ });
5410
+ var accountDropdownOuterStyle = (tokens) => ({
5411
+ marginTop: 4,
5412
+ background: tokens.bgCard,
5413
+ border: `1px solid ${tokens.border}`,
5414
+ borderRadius: tokens.radiusLg,
5415
+ boxShadow: tokens.shadowLg,
5416
+ padding: "12px 14px 14px"
5417
+ });
5418
+ var accountDropdownLabelStyle = (color) => ({
5419
+ fontSize: "0.78rem",
5420
+ fontWeight: 500,
5421
+ color,
5422
+ marginBottom: 8
5423
+ });
5424
+ var accountDropdownInnerStyle = (tokens) => ({
5425
+ background: tokens.bgInput,
5426
+ border: `1px solid ${tokens.border}`,
5427
+ borderRadius: tokens.radiusLg,
5428
+ overflow: "hidden"
5429
+ });
5430
+ var accountRowStyle = (tokens, isSelected) => ({
5431
+ display: "flex",
5432
+ alignItems: "center",
5433
+ justifyContent: "space-between",
5434
+ width: "100%",
5435
+ padding: "14px 16px",
5436
+ background: isSelected ? `${tokens.accent}10` : "transparent",
5437
+ border: "none",
5438
+ borderBottom: `1px solid ${tokens.border}`,
5439
+ cursor: "pointer",
5440
+ fontFamily: "inherit",
5441
+ textAlign: "left",
5442
+ outline: "none"
5443
+ });
5444
+ var accountRowLeftStyle = {
5445
+ display: "flex",
5446
+ alignItems: "center",
5447
+ gap: 12,
5448
+ minWidth: 0,
5449
+ flex: 1
5450
+ };
5451
+ var accountLogoStyle = {
5452
+ width: 28,
5453
+ height: 28,
5454
+ borderRadius: "50%",
5455
+ objectFit: "contain",
5456
+ flexShrink: 0
5457
+ };
5458
+ var accountFallbackIconStyle = (color) => ({
5459
+ width: 28,
5460
+ height: 28,
5461
+ borderRadius: "50%",
5462
+ display: "flex",
5463
+ alignItems: "center",
5464
+ justifyContent: "center",
5465
+ fontSize: "0.75rem",
5466
+ fontWeight: 700,
5467
+ color,
5468
+ flexShrink: 0
5469
+ });
5470
+ var accountInfoStyle = {
5471
+ display: "flex",
4938
5472
  flexDirection: "column",
4939
5473
  gap: 2,
4940
5474
  minWidth: 0
@@ -4998,9 +5532,20 @@ var limitExceededHintStyle = (color) => ({
4998
5532
  margin: "12px 0 2px",
4999
5533
  lineHeight: 1.5
5000
5534
  });
5535
+ var feeRowContainerStyle = {
5536
+ fontSize: "0.84rem",
5537
+ fontWeight: 500,
5538
+ marginTop: 6,
5539
+ marginBottom: 4
5540
+ };
5541
+ var feeRowLabelStyle = (color) => ({ color });
5542
+ var feeRowAmountStyle = (color) => ({
5543
+ color,
5544
+ fontVariantNumeric: "tabular-nums"
5545
+ });
5001
5546
  function SuccessScreen({
5002
5547
  amount,
5003
- currency,
5548
+ currency: _currency,
5004
5549
  succeeded,
5005
5550
  error,
5006
5551
  merchantName,
@@ -5013,22 +5558,30 @@ function SuccessScreen({
5013
5558
  onPreauthorize
5014
5559
  }) {
5015
5560
  const { tokens } = useBlinkConfig();
5561
+ const isGuestDepositSuccess = succeeded && onPreauthorize != null;
5016
5562
  return /* @__PURE__ */ jsxRuntime.jsxs(
5017
5563
  ScreenLayout,
5018
5564
  {
5019
5565
  footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5020
- succeeded && onPreauthorize ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5021
- /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onPreauthorize, children: "Preauthorize future transfers" }),
5022
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onDone, style: skipButtonStyle(tokens.textMuted), children: "Skip this time" })
5566
+ isGuestDepositSuccess ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5567
+ /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onPreauthorize, children: "Set up one tap" }),
5568
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onDone, style: skipButtonStyle(tokens.textMuted), children: "Return to app" })
5023
5569
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onDone, children: succeeded ? "Done" : "Try again" }),
5024
5570
  onManageAccount && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onManageAccount, style: manageStyle(tokens.textMuted), children: "Manage Blink account \u2192" }),
5025
5571
  /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
5026
5572
  ] }),
5027
5573
  children: [
5028
5574
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
5029
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
5030
- succeeded ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5031
- /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "success", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z", fill: tokens.success }) }) }),
5575
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: screenContentStyle, children: [
5576
+ isGuestDepositSuccess ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyleCompact, children: [
5577
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle7(tokens.text), children: [
5578
+ "$",
5579
+ amount.toFixed(2),
5580
+ " deposited"
5581
+ ] }),
5582
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { ...subtitleStyle7(tokens.text), fontWeight: 600, margin: "0 0 8px" }, children: "Next time, do it in one tap" }),
5583
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle7(tokens.textSecondary), children: "Set up one tap for this wallet and skip the extra steps." })
5584
+ ] }) : succeeded ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyleCompact, children: [
5032
5585
  /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle7(tokens.text), children: [
5033
5586
  "$",
5034
5587
  amount.toFixed(2),
@@ -5040,10 +5593,10 @@ function SuccessScreen({
5040
5593
  ] })
5041
5594
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5042
5595
  /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "error", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.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 }) }) }),
5043
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(tokens.text), children: "Transfer failed" }),
5044
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle7(tokens.error), children: error })
5596
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: failureHeadingStyle(tokens.text), children: "Transfer failed" }),
5597
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: failureSubtitleStyle(tokens.error), children: error })
5045
5598
  ] }),
5046
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryCardStyle(tokens), children: [
5599
+ !isGuestDepositSuccess && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryCardStyle(tokens), children: [
5047
5600
  sourceName && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
5048
5601
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryLabelStyle(tokens.textMuted), children: "From" }),
5049
5602
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryValueStyle(tokens.text), children: sourceName })
@@ -5060,7 +5613,7 @@ function SuccessScreen({
5060
5613
  ] })
5061
5614
  ] })
5062
5615
  ] }),
5063
- succeeded && onIncreaseLimits && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellCardStyle(tokens), children: [
5616
+ succeeded && onIncreaseLimits && !isGuestDepositSuccess && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellCardStyle(tokens), children: [
5064
5617
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellHeaderStyle, children: [
5065
5618
  /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { marginRight: 6 }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 14l5-5 5 5", stroke: tokens.accent, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
5066
5619
  /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Want higher limits?" })
@@ -5073,21 +5626,42 @@ function SuccessScreen({
5073
5626
  }
5074
5627
  );
5075
5628
  }
5076
- var contentStyle6 = {
5629
+ var screenContentStyle = {
5077
5630
  flex: 1,
5078
5631
  display: "flex",
5079
5632
  flexDirection: "column",
5080
5633
  alignItems: "center",
5081
5634
  paddingTop: 16
5082
5635
  };
5636
+ var contentStyleCompact = {
5637
+ textAlign: "center",
5638
+ display: "flex",
5639
+ flexDirection: "column",
5640
+ alignItems: "center",
5641
+ width: "100%"
5642
+ };
5083
5643
  var headingStyle7 = (color) => ({
5644
+ fontSize: "1.45rem",
5645
+ fontWeight: 700,
5646
+ letterSpacing: "-0.02em",
5647
+ color,
5648
+ margin: "20px 0 8px"
5649
+ });
5650
+ var subtitleStyle7 = (color) => ({
5651
+ fontSize: "0.9rem",
5652
+ color,
5653
+ margin: "0 0 28px",
5654
+ lineHeight: 1.5,
5655
+ maxWidth: 280
5656
+ });
5657
+ var failureHeadingStyle = (color) => ({
5084
5658
  fontSize: "1.5rem",
5085
5659
  fontWeight: 700,
5086
5660
  letterSpacing: "-0.02em",
5087
5661
  color,
5088
5662
  margin: "20px 0 4px"
5089
5663
  });
5090
- var subtitleStyle7 = (color) => ({
5664
+ var failureSubtitleStyle = (color) => ({
5091
5665
  fontSize: "0.9rem",
5092
5666
  color,
5093
5667
  margin: "0 0 20px"
@@ -5603,7 +6177,7 @@ function TransferStatusScreen({
5603
6177
  const steps = buildSteps(phase);
5604
6178
  return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { footer: /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {}), children: [
5605
6179
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
5606
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
6180
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
5607
6181
  /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 64 }),
5608
6182
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle9(tokens.text), children: "Depositing your money..." }),
5609
6183
  error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle7(tokens), children: error }),
@@ -5611,7 +6185,7 @@ function TransferStatusScreen({
5611
6185
  ] })
5612
6186
  ] });
5613
6187
  }
5614
- var contentStyle7 = {
6188
+ var contentStyle6 = {
5615
6189
  flex: 1,
5616
6190
  display: "flex",
5617
6191
  flexDirection: "column",
@@ -5681,7 +6255,7 @@ function OpenWalletScreen({
5681
6255
  ] }),
5682
6256
  children: [
5683
6257
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
5684
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle8, children: [
6258
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
5685
6259
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: logoCircleStyle(tokens.bgInput), children: logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle2 }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 32 }) }),
5686
6260
  /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle10(tokens.text), children: [
5687
6261
  "Setting up ",
@@ -5714,7 +6288,7 @@ function OpenWalletScreen({
5714
6288
  ] }),
5715
6289
  children: [
5716
6290
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onBack, onLogout }),
5717
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle8, children: [
6291
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
5718
6292
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: logoCircleStyle(tokens.bgInput), children: logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle2 }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 32 }) }),
5719
6293
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle10(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
5720
6294
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle10(tokens.textSecondary), children: loading ? "Creating transfer and preparing your wallet link..." : `Continue in ${displayName} to authorize this connection.` }),
@@ -5727,7 +6301,7 @@ function OpenWalletScreen({
5727
6301
  }
5728
6302
  );
5729
6303
  }
5730
- var contentStyle8 = {
6304
+ var contentStyle7 = {
5731
6305
  flex: 1,
5732
6306
  display: "flex",
5733
6307
  flexDirection: "column",
@@ -5813,7 +6387,7 @@ function ConfirmSignScreen({
5813
6387
  ] }),
5814
6388
  children: [
5815
6389
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
5816
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle9, children: [
6390
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle8, children: [
5817
6391
  logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle3 }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
5818
6392
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle11(tokens.text), children: "Wallet authorized" }),
5819
6393
  /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle11(tokens.textSecondary), children: [
@@ -5829,7 +6403,7 @@ function ConfirmSignScreen({
5829
6403
  }
5830
6404
  );
5831
6405
  }
5832
- var contentStyle9 = {
6406
+ var contentStyle8 = {
5833
6407
  flex: 1,
5834
6408
  display: "flex",
5835
6409
  flexDirection: "column",
@@ -6124,29 +6698,6 @@ var selectCircleSelectedStyle = (color) => ({
6124
6698
  function entryKey(entry) {
6125
6699
  return `${entry.sourceChainId}-${entry.tokenAddress.toLowerCase()}`;
6126
6700
  }
6127
- function isPreciseMoneyNonPositive(fee) {
6128
- const raw = fee.value.trim();
6129
- if (!/^-?\d+(\.\d*)?$/.test(raw)) return false;
6130
- const n = Number(raw);
6131
- return Number.isFinite(n) && n <= 0;
6132
- }
6133
- function formatNonPositiveFeeDisplay(fee) {
6134
- if (fee.currency === "USD") return "Under $0.01";
6135
- return `Less than 0.01 ${fee.currency}`;
6136
- }
6137
- function formatPreciseMoneyForDisplay(fee) {
6138
- const raw = fee.value.trim();
6139
- if (fee.currency === "USD") {
6140
- if (!/^\d+(\.\d*)?$/.test(raw)) {
6141
- return `$${raw}`;
6142
- }
6143
- const [whole, frac = ""] = raw.split(".");
6144
- const dec = `${frac}00`.slice(0, 2);
6145
- const intFmt = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
6146
- return `$${intFmt}.${dec}`;
6147
- }
6148
- return `${raw} ${fee.currency}`;
6149
- }
6150
6701
  function GuestTokenPickerScreen({
6151
6702
  entries,
6152
6703
  loading,
@@ -6186,13 +6737,13 @@ function GuestTokenPickerScreen({
6186
6737
  const canConfirm = Boolean(quoteFee && pendingEntry && !quoteLoading);
6187
6738
  const feeLine = (() => {
6188
6739
  if (quoteLoading && pendingEntry) {
6189
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: feeRowContainerStyle, "aria-live": "polite", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle(t.textMuted), children: "Getting fee estimate\u2026" }) });
6740
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: feeRowContainerStyle2, "aria-live": "polite", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle2(t.textMuted), children: "Getting fee estimate\u2026" }) });
6190
6741
  }
6191
6742
  if (quoteFee) {
6192
6743
  const feeText = isPreciseMoneyNonPositive(quoteFee) ? formatNonPositiveFeeDisplay(quoteFee) : formatPreciseMoneyForDisplay(quoteFee);
6193
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: feeRowContainerStyle, "aria-live": "polite", children: [
6194
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle(t.textMuted), children: "Fee estimate: " }),
6195
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowAmountStyle(t.textSecondary), children: feeText })
6744
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: feeRowContainerStyle2, "aria-live": "polite", children: [
6745
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle2(t.textMuted), children: "Fee estimate: " }),
6746
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowAmountStyle2(t.textSecondary), children: feeText })
6196
6747
  ] });
6197
6748
  }
6198
6749
  return null;
@@ -6214,7 +6765,7 @@ function GuestTokenPickerScreen({
6214
6765
  depositAmount != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: depositAmountStyle2(t.text), children: [
6215
6766
  "$",
6216
6767
  depositAmount.toLocaleString("en-US", {
6217
- minimumFractionDigits: 0,
6768
+ minimumFractionDigits: 2,
6218
6769
  maximumFractionDigits: 2
6219
6770
  })
6220
6771
  ] }),
@@ -6296,7 +6847,7 @@ function GuestTokenPickerScreen({
6296
6847
  ] }),
6297
6848
  tokenListOpen && entries.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownOuterStyle(t), children: [
6298
6849
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: accountDropdownLabelStyle2(t.textMuted), children: "Choose token" }),
6299
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownInnerStyle(t), children: entries.map((entry, index) => {
6850
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownInnerStyle2(t), children: entries.map((entry, index) => {
6300
6851
  const selected = pendingKey === entryKey(entry);
6301
6852
  const isLast = index === entries.length - 1;
6302
6853
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -6397,13 +6948,13 @@ var depositAmountStyle2 = (color) => ({
6397
6948
  color,
6398
6949
  lineHeight: 1.05
6399
6950
  });
6400
- var feeRowContainerStyle = {
6951
+ var feeRowContainerStyle2 = {
6401
6952
  fontSize: "0.84rem",
6402
6953
  fontWeight: 500,
6403
6954
  marginTop: 6
6404
6955
  };
6405
- var feeRowLabelStyle = (color) => ({ color });
6406
- var feeRowAmountStyle = (color) => ({
6956
+ var feeRowLabelStyle2 = (color) => ({ color });
6957
+ var feeRowAmountStyle2 = (color) => ({
6407
6958
  color,
6408
6959
  fontVariantNumeric: "tabular-nums"
6409
6960
  });
@@ -6449,7 +7000,7 @@ var accountDropdownLabelStyle2 = (color) => ({
6449
7000
  color,
6450
7001
  marginBottom: 8
6451
7002
  });
6452
- var tokenDropdownInnerStyle = (tokens) => ({
7003
+ var tokenDropdownInnerStyle2 = (tokens) => ({
6453
7004
  background: tokens.bgInput,
6454
7005
  border: `1px solid ${tokens.border}`,
6455
7006
  borderRadius: tokens.radiusLg,
@@ -6561,7 +7112,7 @@ function GuestPreauthSetupCompleteScreen({
6561
7112
  ] }),
6562
7113
  children: [
6563
7114
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
6564
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle10, children: [
7115
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle9, children: [
6565
7116
  /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "success", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
6566
7117
  "path",
6567
7118
  {
@@ -6570,13 +7121,13 @@ function GuestPreauthSetupCompleteScreen({
6570
7121
  }
6571
7122
  ) }) }),
6572
7123
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle12(tokens.text), children: "Setup complete" }),
6573
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle12(tokens.textSecondary), children: "Your account is linked and ready. You can close this window or make another deposit." })
7124
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle12(tokens.textSecondary), children: "Your account is linked and ready. You can close this window." })
6574
7125
  ] })
6575
7126
  ]
6576
7127
  }
6577
7128
  );
6578
7129
  }
6579
- var contentStyle10 = {
7130
+ var contentStyle9 = {
6580
7131
  display: "flex",
6581
7132
  flexDirection: "column",
6582
7133
  alignItems: "center",
@@ -6605,14 +7156,14 @@ function GuestPreauthLinkingScreen({ onLogout }) {
6605
7156
  const { tokens } = useBlinkConfig();
6606
7157
  return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
6607
7158
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
6608
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle11, children: [
7159
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle10, children: [
6609
7160
  /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
6610
7161
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle13(tokens.text), children: "Setting up your account..." }),
6611
7162
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle13(tokens.textSecondary), children: "Linking your wallet to your Blink account. This usually takes a few seconds." })
6612
7163
  ] })
6613
7164
  ] });
6614
7165
  }
6615
- var contentStyle11 = {
7166
+ var contentStyle10 = {
6616
7167
  flex: 1,
6617
7168
  display: "flex",
6618
7169
  flexDirection: "column",
@@ -6789,13 +7340,15 @@ function StepRendererContent({
6789
7340
  return /* @__PURE__ */ jsxRuntime.jsx(
6790
7341
  PasskeyScreen,
6791
7342
  {
6792
- onCreatePasskey: handlers.onRegisterPasskey,
7343
+ onCreatePasskey: handlers.onVerifyPasskey,
6793
7344
  onBack: handlers.onLogout,
6794
7345
  onLogout: handlers.onLogout,
6795
- creating: state.verifyingPasskeyPopup,
7346
+ creating: state.registeringPasskey,
6796
7347
  error: state.error,
6797
- popupFallback: true,
6798
- onCreatePasskeyViaPopup: handlers.onVerifyPasskeyViaPopup
7348
+ onCreateNewPasskey: handlers.onCreateNewPasskey,
7349
+ onCreateNewPasskeyViaPopup: handlers.onCreateNewPasskeyViaPopup,
7350
+ createNewPopupFallback: state.passkeyPopupNeeded,
7351
+ creatingNewPasskey: state.registeringPasskey
6799
7352
  }
6800
7353
  );
6801
7354
  case "wallet-picker": {
@@ -6852,15 +7405,11 @@ function StepRendererContent({
6852
7405
  0
6853
7406
  );
6854
7407
  const effectiveTokenCount = tokenCount > 0 ? tokenCount : selectSourceTokenCount;
6855
- const effectiveSourceLabel = selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
6856
- const setupTokenOptions = selectedAccount ? selectedAccount.wallets.flatMap(
6857
- (w) => w.sources.filter((s) => s.balance.total.amount > 0).map((s) => ({
6858
- symbol: s.token.symbol,
6859
- chainName: w.chain.name,
6860
- balance: s.balance.available.amount,
6861
- walletId: w.id
6862
- }))
6863
- ) : selectSourceChoices.flatMap(
7408
+ const setupFromPendingSelectSource = pendingSelectSource != null;
7409
+ const setupSelectedTokenSymbol = setupFromPendingSelectSource ? selectSourceTokenSymbol || selectSourceRecommended?.tokenSymbol || "" : selectedSource?.token.symbol ?? selectSourceTokenSymbol;
7410
+ const setupSelectedChainName = setupFromPendingSelectSource ? selectSourceChainName || selectSourceRecommended?.chainName || "" : selectedWallet?.chain.name ?? selectSourceChainName;
7411
+ const effectiveSourceLabel = setupFromPendingSelectSource ? setupSelectedTokenSymbol && setupSelectedChainName ? `${setupSelectedTokenSymbol} on ${setupSelectedChainName}` : void 0 : selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
7412
+ const setupTokenOptions = selectedAccount ? tokenOptionsForLinkedAccount(selectedAccount, state.chains) : selectSourceChoices.flatMap(
6864
7413
  (chain) => chain.tokens.map((t) => ({
6865
7414
  symbol: t.tokenSymbol,
6866
7415
  chainName: chain.chainName,
@@ -6868,9 +7417,8 @@ function StepRendererContent({
6868
7417
  }))
6869
7418
  );
6870
7419
  const handleSetupSelectToken = (symbol, chainName, walletId) => {
6871
- if (walletId) {
6872
- handlers.onSelectAuthorizedToken(walletId, symbol);
6873
- } else {
7420
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7421
+ if (pendingSelectSource) {
6874
7422
  handlers.onSelectSourceChainChange(chainName);
6875
7423
  handlers.onSetSelectSourceTokenSymbol(symbol);
6876
7424
  }
@@ -6878,17 +7426,18 @@ function StepRendererContent({
6878
7426
  return /* @__PURE__ */ jsxRuntime.jsx(
6879
7427
  SetupScreen,
6880
7428
  {
6881
- availableBalance: selectedSource ? selectedSource.balance.available.amount : selectSourceAvailableBalance > 0 ? selectSourceAvailableBalance : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : maxSourceBalance,
7429
+ availableBalance: selectedSource ? selectedSource.balance.available.amount : setupFromPendingSelectSource ? selectSourceAvailableBalance : selectSourceAvailableBalance > 0 ? selectSourceAvailableBalance : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : maxSourceBalance,
6882
7430
  tokenCount: effectiveTokenCount,
6883
7431
  sourceName,
6884
7432
  onSetupOneTap: handlers.onSetupOneTap,
6885
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7433
+ onBack: handlers.onBackFromSubflow,
6886
7434
  onLogout: handlers.onLogout,
6887
7435
  onAdvanced: handlers.onSelectToken,
6888
7436
  selectedSourceLabel: effectiveSourceLabel,
6889
7437
  loading: savingOneTapLimit,
6890
7438
  error: state.error,
6891
- selectedTokenSymbol: selectedSource?.token.symbol ?? selectSourceTokenSymbol,
7439
+ selectedTokenSymbol: setupSelectedTokenSymbol,
7440
+ selectedChainName: setupSelectedChainName,
6892
7441
  tokenOptions: setupTokenOptions,
6893
7442
  onSelectToken: handleSetupSelectToken
6894
7443
  }
@@ -6910,6 +7459,7 @@ function StepRendererContent({
6910
7459
  case "deposit": {
6911
7460
  const parsedAmt = depositAmount != null ? depositAmount : 5;
6912
7461
  const minDepositFloor = depositAmount != null ? depositAmount : DEFAULT_MIN_DEPOSIT_USD;
7462
+ const depositTokenOptions = tokenOptionsForLinkedAccount(selectedAccount, state.chains);
6913
7463
  return /* @__PURE__ */ jsxRuntime.jsx(
6914
7464
  DepositScreen,
6915
7465
  {
@@ -6918,6 +7468,8 @@ function StepRendererContent({
6918
7468
  remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
6919
7469
  tokenCount,
6920
7470
  initialAmount: parsedAmt,
7471
+ quoteFee: forms.depositQuoteFee,
7472
+ quoteLoading: forms.depositQuoteLoading,
6921
7473
  processing: state.creatingTransfer,
6922
7474
  error: state.error,
6923
7475
  onDeposit: handlers.onPay,
@@ -6932,8 +7484,13 @@ function StepRendererContent({
6932
7484
  onAuthorizeAccount: handlers.onContinueConnection,
6933
7485
  onAddProvider: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
6934
7486
  onSelectToken: handlers.onSelectToken,
7487
+ tokenOptions: depositTokenOptions,
7488
+ onPickToken: (symbol, chainName, walletId) => {
7489
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7490
+ },
6935
7491
  selectedSourceLabel,
6936
7492
  selectedTokenSymbol: selectedSource?.token.symbol,
7493
+ selectedChainName: selectedWallet?.chain.name,
6937
7494
  minDepositFloor
6938
7495
  }
6939
7496
  );
@@ -6974,7 +7531,7 @@ function StepRendererContent({
6974
7531
  chains: state.chains,
6975
7532
  onSelectAuthorized: handlers.onSelectAuthorizedToken,
6976
7533
  onAuthorizeToken: handlers.onAuthorizeToken,
6977
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7534
+ onBack: handlers.onBackFromSubflow,
6978
7535
  onLogout: handlers.onLogout,
6979
7536
  depositAmount: depositAmount ?? void 0,
6980
7537
  selectedTokenSymbol: selectedSource?.token.symbol,
@@ -7053,7 +7610,7 @@ function StepRendererContent({
7053
7610
  })() : void 0,
7054
7611
  onDone: onDismiss ?? handlers.onNewPayment,
7055
7612
  onLogout: authenticated ? handlers.onLogout : void 0,
7056
- onPreauthorize: state.isGuestFlow ? handlers.onPreauthorize : void 0
7613
+ onPreauthorize: state.isGuestFlow && isDesktop ? handlers.onPreauthorize : void 0
7057
7614
  }
7058
7615
  );
7059
7616
  }
@@ -7341,6 +7898,53 @@ function usePasskeyHandlers(dispatch, apiBaseUrl, accounts, knownCredentialIds)
7341
7898
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
7342
7899
  }
7343
7900
  }, [user, knownCredentialIds, activateExistingCredential, completePasskeyRegistration, dispatch]);
7901
+ const handleVerifyPasskey = react.useCallback(async () => {
7902
+ dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
7903
+ dispatch({ type: "SET_ERROR", error: null });
7904
+ try {
7905
+ const matched = await findDevicePasskey(knownCredentialIds);
7906
+ if (matched) {
7907
+ await activateExistingCredential(matched);
7908
+ } else {
7909
+ dispatch({
7910
+ type: "SET_ERROR",
7911
+ error: "No matching passkey found on this device. Please try again."
7912
+ });
7913
+ }
7914
+ } catch (err) {
7915
+ captureException(err);
7916
+ dispatch({
7917
+ type: "SET_ERROR",
7918
+ error: err instanceof Error ? err.message : "Passkey verification failed."
7919
+ });
7920
+ } finally {
7921
+ dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
7922
+ }
7923
+ }, [knownCredentialIds, activateExistingCredential, dispatch]);
7924
+ const handleCreateNewPasskey = react.useCallback(async () => {
7925
+ dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
7926
+ dispatch({ type: "SET_ERROR", error: null });
7927
+ try {
7928
+ const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Blink User";
7929
+ const { credentialId, publicKey } = await createPasskeyCredential({
7930
+ userId: user?.id ?? "unknown",
7931
+ displayName: passkeyDisplayName
7932
+ });
7933
+ await completePasskeyRegistration(credentialId, publicKey);
7934
+ } catch (err) {
7935
+ if (err instanceof PasskeyIframeBlockedError) {
7936
+ dispatch({ type: "SET_PASSKEY_POPUP_NEEDED", needed: true });
7937
+ } else {
7938
+ captureException(err);
7939
+ dispatch({
7940
+ type: "SET_ERROR",
7941
+ error: err instanceof Error ? err.message : "Failed to register passkey"
7942
+ });
7943
+ }
7944
+ } finally {
7945
+ dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
7946
+ }
7947
+ }, [user, completePasskeyRegistration, dispatch]);
7344
7948
  const handleCreatePasskeyViaPopup = react.useCallback(async () => {
7345
7949
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
7346
7950
  dispatch({ type: "SET_ERROR", error: null });
@@ -7387,6 +7991,35 @@ function usePasskeyHandlers(dispatch, apiBaseUrl, accounts, knownCredentialIds)
7387
7991
  dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
7388
7992
  }
7389
7993
  }, [user, knownCredentialIds, getAccessToken, apiBaseUrl, activateExistingCredential, dispatch]);
7994
+ const handleCreateNewPasskeyViaPopup = react.useCallback(async () => {
7995
+ dispatch({ type: "SET_REGISTERING_PASSKEY", value: true });
7996
+ dispatch({ type: "SET_ERROR", error: null });
7997
+ try {
7998
+ const token = await getAccessToken();
7999
+ const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Blink User";
8000
+ const popupOptions = buildPasskeyPopupOptions({
8001
+ userId: user?.id ?? "unknown",
8002
+ displayName: passkeyDisplayName,
8003
+ authToken: token ?? void 0,
8004
+ apiBaseUrl
8005
+ });
8006
+ const { credentialId, publicKey } = await createPasskeyViaPopup(popupOptions);
8007
+ dispatch({ type: "PASSKEY_ACTIVATED", credentialId, publicKey });
8008
+ localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
8009
+ if (token) {
8010
+ reportPasskeyActivity(apiBaseUrl, token, credentialId).catch(() => {
8011
+ });
8012
+ }
8013
+ } catch (err) {
8014
+ captureException(err);
8015
+ dispatch({
8016
+ type: "SET_ERROR",
8017
+ error: err instanceof Error ? err.message : "Failed to register passkey"
8018
+ });
8019
+ } finally {
8020
+ dispatch({ type: "SET_REGISTERING_PASSKEY", value: false });
8021
+ }
8022
+ }, [user, getAccessToken, apiBaseUrl, dispatch]);
7390
8023
  const handleVerifyPasskeyViaPopup = react.useCallback(async () => {
7391
8024
  dispatch({ type: "SET_VERIFYING_PASSKEY", value: true });
7392
8025
  dispatch({ type: "SET_ERROR", error: null });
@@ -7429,7 +8062,10 @@ function usePasskeyHandlers(dispatch, apiBaseUrl, accounts, knownCredentialIds)
7429
8062
  }, [knownCredentialIds, getAccessToken, apiBaseUrl, dispatch]);
7430
8063
  return {
7431
8064
  handleRegisterPasskey,
8065
+ handleVerifyPasskey,
8066
+ handleCreateNewPasskey,
7432
8067
  handleCreatePasskeyViaPopup,
8068
+ handleCreateNewPasskeyViaPopup,
7433
8069
  handleVerifyPasskeyViaPopup,
7434
8070
  checkingPasskeyRef
7435
8071
  };
@@ -7452,6 +8088,9 @@ function useTransferHandlers(deps) {
7452
8088
  sourceTokenAddress,
7453
8089
  activeCredentialId,
7454
8090
  selectedAccountId,
8091
+ selectedWalletId,
8092
+ selectedTokenSymbol,
8093
+ quoteId,
7455
8094
  transfer,
7456
8095
  accounts
7457
8096
  } = deps;
@@ -7465,9 +8104,32 @@ function useTransferHandlers(deps) {
7465
8104
  fetchProviders(apiBaseUrl, token)
7466
8105
  ]);
7467
8106
  const parsedAmt = depositAmount != null ? depositAmount : 0;
7468
- const defaults = resolveDepositSelection(accts, parsedAmt, selectedAccountId);
7469
- dispatch({ type: "ACCOUNTS_RELOADED", accounts: accts, providers: prov, defaults });
7470
- }, [getAccessToken, activeCredentialId, selectedAccountId, apiBaseUrl, depositAmount, dispatch]);
8107
+ const { defaults, resetSelectedTokenSymbol } = resolveDepositSelectionAfterRefresh(
8108
+ accts,
8109
+ parsedAmt,
8110
+ {
8111
+ selectedAccountId,
8112
+ selectedWalletId,
8113
+ selectedTokenSymbol
8114
+ }
8115
+ );
8116
+ dispatch({
8117
+ type: "ACCOUNTS_RELOADED",
8118
+ accounts: accts,
8119
+ providers: prov,
8120
+ defaults,
8121
+ resetSelectedTokenSymbol
8122
+ });
8123
+ }, [
8124
+ getAccessToken,
8125
+ activeCredentialId,
8126
+ selectedAccountId,
8127
+ selectedWalletId,
8128
+ selectedTokenSymbol,
8129
+ apiBaseUrl,
8130
+ depositAmount,
8131
+ dispatch
8132
+ ]);
7471
8133
  const handlePay = react.useCallback(async (payAmount, sourceOverrides) => {
7472
8134
  const minUsd = effectiveMinTransferAmountUsd(depositAmount);
7473
8135
  if (isNaN(payAmount) || payAmount < minUsd) {
@@ -7511,7 +8173,8 @@ function useTransferHandlers(deps) {
7511
8173
  sourceId: effectiveSourceId,
7512
8174
  sourceTokenAddress,
7513
8175
  destination,
7514
- amount: payAmount
8176
+ amount: payAmount,
8177
+ ...quoteId ? { quoteId } : {}
7515
8178
  });
7516
8179
  dispatch({ type: "TRANSFER_CREATED", transfer: t });
7517
8180
  if (t.status === "COMPLETED") {
@@ -7543,6 +8206,7 @@ function useTransferHandlers(deps) {
7543
8206
  accounts,
7544
8207
  destination,
7545
8208
  apiBaseUrl,
8209
+ quoteId,
7546
8210
  getAccessToken,
7547
8211
  transferSigning,
7548
8212
  polling,
@@ -7593,20 +8257,20 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7593
8257
  const selectSourceAvailableBalance = react.useMemo(() => {
7594
8258
  if (!pendingSelectSourceAction) return 0;
7595
8259
  const options2 = pendingSelectSourceAction.metadata?.options ?? [];
7596
- const recommended = selectSourceRecommended;
7597
- if (recommended) {
7598
- const match = options2.find(
7599
- (opt) => opt.chainName === recommended.chainName && opt.tokenSymbol === recommended.tokenSymbol
7600
- );
7601
- if (match) return Number(match.rawBalance) / Math.pow(10, match.decimals);
7602
- }
7603
- let max = 0;
7604
- for (const opt of options2) {
7605
- const bal = Number(opt.rawBalance) / Math.pow(10, opt.decimals);
7606
- if (bal > max) max = bal;
7607
- }
7608
- return max;
7609
- }, [pendingSelectSourceAction, selectSourceRecommended]);
8260
+ return resolveSelectSourceAvailableBalance(
8261
+ selectSourceChoices,
8262
+ options2,
8263
+ selectSourceChainName,
8264
+ selectSourceTokenSymbol,
8265
+ selectSourceRecommended
8266
+ );
8267
+ }, [
8268
+ pendingSelectSourceAction,
8269
+ selectSourceChoices,
8270
+ selectSourceChainName,
8271
+ selectSourceTokenSymbol,
8272
+ selectSourceRecommended
8273
+ ]);
7610
8274
  const handleSelectSourceChainChange = react.useCallback(
7611
8275
  (chainName) => {
7612
8276
  setSelectSourceChainName(chainName);
@@ -7657,7 +8321,7 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7657
8321
  initializedSelectSourceActionRef
7658
8322
  };
7659
8323
  }
7660
- function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete) {
8324
+ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete, lastResumedAt) {
7661
8325
  const {
7662
8326
  mobileSetupFlowRef,
7663
8327
  handlingMobileReturnRef,
@@ -7668,6 +8332,8 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7668
8332
  onCompleteRef.current = onComplete;
7669
8333
  const guestPollingActiveRef = react.useRef(false);
7670
8334
  const guestPollingCleanupRef = react.useRef(null);
8335
+ const mobileFlowRef = react.useRef(guestCheckout.mobileFlow);
8336
+ mobileFlowRef.current = guestCheckout.mobileFlow;
7671
8337
  const guestTransferIdRef = react.useRef(guestCheckout.guestTransferId);
7672
8338
  guestTransferIdRef.current = guestCheckout.guestTransferId;
7673
8339
  const guestSessionTokenRef = react.useRef(guestCheckout.guestSessionToken);
@@ -7723,27 +8389,15 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7723
8389
  startGuestPolling
7724
8390
  ]);
7725
8391
  react.useEffect(() => {
7726
- const tryStartPolling = () => {
7727
- if (guestPollingActiveRef.current) return;
7728
- const transferId = guestTransferIdRef.current;
7729
- const token = guestSessionTokenRef.current;
7730
- if (transferId && token) {
7731
- startGuestPolling(transferId, token);
7732
- }
7733
- };
7734
- const handleVisibility = () => {
7735
- if (document.visibilityState === "visible") tryStartPolling();
7736
- };
7737
- const handlePageShow = (e) => {
7738
- if (e.persisted) tryStartPolling();
7739
- };
7740
- document.addEventListener("visibilitychange", handleVisibility);
7741
- window.addEventListener("pageshow", handlePageShow);
7742
- return () => {
7743
- document.removeEventListener("visibilitychange", handleVisibility);
7744
- window.removeEventListener("pageshow", handlePageShow);
7745
- };
7746
- }, [startGuestPolling]);
8392
+ if (!lastResumedAt) return;
8393
+ if (guestPollingActiveRef.current) return;
8394
+ if (!mobileFlowRef.current) return;
8395
+ const transferId = guestTransferIdRef.current;
8396
+ const token = guestSessionTokenRef.current;
8397
+ if (transferId && token) {
8398
+ startGuestPolling(transferId, token);
8399
+ }
8400
+ }, [lastResumedAt, startGuestPolling]);
7747
8401
  const handleAuthorizedMobileReturn = react.useCallback(async (authorizedTransfer, isSetup) => {
7748
8402
  if (handlingMobileReturnRef.current) return;
7749
8403
  handlingMobileReturnRef.current = true;
@@ -7977,6 +8631,9 @@ function useProviderHandlers(deps) {
7977
8631
  await reloadAccounts();
7978
8632
  }
7979
8633
  } catch (err) {
8634
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8635
+ return;
8636
+ }
7980
8637
  captureException(err);
7981
8638
  const msg = err instanceof Error ? err.message : "Failed to set up wallet";
7982
8639
  dispatch({ type: "PAY_ERROR", error: msg });
@@ -8043,7 +8700,12 @@ function useProviderHandlers(deps) {
8043
8700
  }
8044
8701
  const acct = accounts.find((a) => a.id === selectedAccountId);
8045
8702
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8046
- if (matchedProvider) {
8703
+ const isMobile = !shouldUseWalletConnector({
8704
+ useWalletConnector: useWalletConnectorProp,
8705
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8706
+ });
8707
+ if (matchedProvider && isMobile) {
8708
+ dispatch({ type: "SAVE_SELECTION" });
8047
8709
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8048
8710
  }
8049
8711
  dispatch({ type: "SET_ERROR", error: null });
@@ -8063,10 +8725,6 @@ function useProviderHandlers(deps) {
8063
8725
  activeCredentialId,
8064
8726
  { tokenAddress: source?.address, chainId: evmChainId }
8065
8727
  );
8066
- const isMobile = !shouldUseWalletConnector({
8067
- useWalletConnector: useWalletConnectorProp,
8068
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8069
- });
8070
8728
  if (isMobile) {
8071
8729
  handlingMobileReturnRef.current = false;
8072
8730
  mobileSetupFlowRef.current = true;
@@ -8083,12 +8741,20 @@ function useProviderHandlers(deps) {
8083
8741
  });
8084
8742
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8085
8743
  triggerDeeplink(session.uri);
8744
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8086
8745
  } else {
8087
8746
  await authExecutor.executeSessionById(session.id);
8088
8747
  await reloadAccounts();
8748
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8089
8749
  }
8090
8750
  } catch (err) {
8751
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8752
+ return;
8753
+ }
8091
8754
  captureException(err);
8755
+ if (isMobile) {
8756
+ dispatch({ type: "RESTORE_SELECTION" });
8757
+ }
8092
8758
  const msg = err instanceof Error ? err.message : "Failed to increase limit";
8093
8759
  dispatch({ type: "SET_ERROR", error: msg });
8094
8760
  onError?.(msg);
@@ -8133,7 +8799,12 @@ function useProviderHandlers(deps) {
8133
8799
  }
8134
8800
  const acct = accounts.find((a) => a.id === selectedAccountId);
8135
8801
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8136
- if (matchedProvider) {
8802
+ const isMobile = !shouldUseWalletConnector({
8803
+ useWalletConnector: useWalletConnectorProp,
8804
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8805
+ });
8806
+ if (matchedProvider && isMobile) {
8807
+ dispatch({ type: "SAVE_SELECTION" });
8137
8808
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8138
8809
  }
8139
8810
  dispatch({ type: "SET_ERROR", error: null });
@@ -8148,10 +8819,6 @@ function useProviderHandlers(deps) {
8148
8819
  activeCredentialId,
8149
8820
  { tokenAddress, chainId }
8150
8821
  );
8151
- const isMobile = !shouldUseWalletConnector({
8152
- useWalletConnector: useWalletConnectorProp,
8153
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8154
- });
8155
8822
  if (isMobile) {
8156
8823
  handlingMobileReturnRef.current = false;
8157
8824
  mobileSetupFlowRef.current = true;
@@ -8169,13 +8836,21 @@ function useProviderHandlers(deps) {
8169
8836
  });
8170
8837
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8171
8838
  triggerDeeplink(session.uri);
8839
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8172
8840
  } else {
8173
8841
  await authExecutor.executeSessionById(session.id);
8174
8842
  await reloadAccounts();
8175
8843
  dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
8844
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8176
8845
  }
8177
8846
  } catch (err) {
8847
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8848
+ return;
8849
+ }
8178
8850
  captureException(err);
8851
+ if (isMobile) {
8852
+ dispatch({ type: "RESTORE_SELECTION" });
8853
+ }
8179
8854
  const msg = err instanceof Error ? err.message : "Failed to authorize token";
8180
8855
  dispatch({ type: "SET_ERROR", error: msg });
8181
8856
  onError?.(msg);
@@ -9008,28 +9683,6 @@ function usePasskeyCheckEffect(deps) {
9008
9683
  await restoreState(activeCredentialId, token);
9009
9684
  return;
9010
9685
  }
9011
- if (cancelled) return;
9012
- const credentialIds = allPasskeys.map((p) => p.credentialId);
9013
- let matched = null;
9014
- if (isSafari() && isInCrossOriginIframe()) {
9015
- matched = await findDevicePasskeyViaPopup({
9016
- credentialIds,
9017
- rpId: resolvePasskeyRpId(),
9018
- authToken: token ?? void 0,
9019
- apiBaseUrl
9020
- });
9021
- } else {
9022
- matched = await findDevicePasskey(credentialIds);
9023
- }
9024
- if (cancelled) return;
9025
- if (matched) {
9026
- const publicKey = allPasskeys.find((p) => p.credentialId === matched)?.publicKey;
9027
- dispatch({ type: "PASSKEY_ACTIVATED", credentialId: matched, publicKey });
9028
- window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
9029
- reportPasskeyActivity(apiBaseUrl, token, matched).catch(() => {
9030
- });
9031
- await restoreState(matched, token);
9032
- }
9033
9686
  } catch (err) {
9034
9687
  dispatch({
9035
9688
  type: "PASSKEY_CONFIG_LOADED",
@@ -9112,7 +9765,15 @@ function useDataLoadEffect(deps) {
9112
9765
  ]);
9113
9766
  if (cancelled) return;
9114
9767
  const parsedAmt = depositAmount != null ? depositAmount : 0;
9115
- const defaults = resolveDepositSelection(accts, parsedAmt, state.selectedAccountId);
9768
+ const { defaults, resetSelectedTokenSymbol } = resolveDepositSelectionAfterRefresh(
9769
+ accts,
9770
+ parsedAmt,
9771
+ {
9772
+ selectedAccountId: state.selectedAccountId,
9773
+ selectedWalletId: state.selectedWalletId,
9774
+ selectedTokenSymbol: state.selectedTokenSymbol
9775
+ }
9776
+ );
9116
9777
  const persisted = loadMobileFlowState();
9117
9778
  const clearMobile = persisted?.isSetup && accts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
9118
9779
  dispatch({
@@ -9121,7 +9782,8 @@ function useDataLoadEffect(deps) {
9121
9782
  accounts: accts,
9122
9783
  chains: chn,
9123
9784
  defaults,
9124
- clearMobileState: clearMobile
9785
+ clearMobileState: clearMobile,
9786
+ resetSelectedTokenSymbol
9125
9787
  });
9126
9788
  if (clearMobile) clearMobileFlowState();
9127
9789
  } catch (err) {
@@ -9150,6 +9812,8 @@ function useDataLoadEffect(deps) {
9150
9812
  apiBaseUrl,
9151
9813
  state.activeCredentialId,
9152
9814
  state.selectedAccountId,
9815
+ state.selectedWalletId,
9816
+ state.selectedTokenSymbol,
9153
9817
  depositAmount
9154
9818
  ]);
9155
9819
  react.useEffect(() => {
@@ -9378,25 +10042,16 @@ function useMobilePollingEffect(deps) {
9378
10042
  const poll = isReauth ? pollReauthorization : pollWalletActive;
9379
10043
  poll();
9380
10044
  const intervalId = window.setInterval(poll, POLL_INTERVAL_MS);
9381
- const handleVisibility = () => {
9382
- if (document.visibilityState === "visible" && !cancelled) poll();
9383
- };
9384
- const handlePageShow = (e) => {
9385
- if (e.persisted && !cancelled) poll();
9386
- };
9387
- document.addEventListener("visibilitychange", handleVisibility);
9388
- window.addEventListener("pageshow", handlePageShow);
9389
10045
  return () => {
9390
10046
  cancelled = true;
9391
10047
  window.clearInterval(intervalId);
9392
- document.removeEventListener("visibilitychange", handleVisibility);
9393
- window.removeEventListener("pageshow", handlePageShow);
9394
10048
  };
9395
10049
  }, [
9396
10050
  state.mobileFlow,
9397
10051
  state.isGuestFlow,
9398
10052
  state.guestPreauthSessionId,
9399
10053
  state.activeCredentialId,
10054
+ state.lastResumedAt,
9400
10055
  apiBaseUrl,
9401
10056
  reloadAccounts,
9402
10057
  dispatch,
@@ -9413,28 +10068,13 @@ function useMobilePollingEffect(deps) {
9413
10068
  const transferIdToResume = pollingTransferIdRef.current ?? state.transfer?.id;
9414
10069
  if (!transferIdToResume) return;
9415
10070
  if (!polling.isPolling) polling.startPolling(transferIdToResume);
9416
- const handleVisibility = () => {
9417
- if (document.visibilityState === "visible" && !handlingMobileReturnRef.current) {
9418
- polling.startPolling(transferIdToResume);
9419
- }
9420
- };
9421
- const handlePageShow = (e) => {
9422
- if (e.persisted && !handlingMobileReturnRef.current) {
9423
- polling.startPolling(transferIdToResume);
9424
- }
9425
- };
9426
- document.addEventListener("visibilitychange", handleVisibility);
9427
- window.addEventListener("pageshow", handlePageShow);
9428
- return () => {
9429
- document.removeEventListener("visibilitychange", handleVisibility);
9430
- window.removeEventListener("pageshow", handlePageShow);
9431
- };
9432
10071
  }, [
9433
10072
  state.mobileFlow,
9434
10073
  state.transfer?.id,
9435
10074
  state.isGuestFlow,
9436
10075
  state.guestPreauthSessionId,
9437
10076
  state.guestPreauthAccountId,
10077
+ state.lastResumedAt,
9438
10078
  polling.isPolling,
9439
10079
  polling.startPolling,
9440
10080
  handlingMobileReturnRef,
@@ -9445,6 +10085,7 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9445
10085
  const {
9446
10086
  dispatch,
9447
10087
  privyReady,
10088
+ lastResumedAt,
9448
10089
  mobileSetupFlowRef,
9449
10090
  setupAccountIdRef,
9450
10091
  startGuestAccountPolling
@@ -9455,68 +10096,43 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9455
10096
  console.info("[blink-sdk] guestPreauthMobileRestore: skipping, privyReady=false");
9456
10097
  return;
9457
10098
  }
9458
- const tryStart = (trigger) => {
9459
- if (startedRef.current) {
9460
- console.info("[blink-sdk] guestPreauthMobileRestore.tryStart: already started", { trigger });
9461
- return;
9462
- }
9463
- const persisted = loadMobileFlowState();
9464
- console.info("[blink-sdk] guestPreauthMobileRestore.tryStart", {
9465
- trigger,
9466
- hasPersistedState: !!persisted,
9467
- isGuestPreauth: persisted?.isGuestPreauth ?? false,
9468
- hasGuestSessionToken: !!persisted?.guestSessionToken,
9469
- hasSessionId: !!persisted?.sessionId,
9470
- accountId: persisted?.accountId ?? null
9471
- });
9472
- if (!persisted?.isGuestPreauth || !persisted.guestSessionToken || !persisted.sessionId) {
9473
- return;
9474
- }
9475
- startedRef.current = true;
9476
- mobileSetupFlowRef.current = true;
9477
- if (persisted.accountId) {
9478
- setupAccountIdRef.current = persisted.accountId;
9479
- }
9480
- if (persisted.accountId) {
9481
- dispatch({
9482
- type: "GUEST_PREAUTH_DETECTED",
9483
- accountId: persisted.accountId,
9484
- sessionId: persisted.sessionId
9485
- });
9486
- }
10099
+ if (startedRef.current) {
10100
+ console.info("[blink-sdk] guestPreauthMobileRestore: already started");
10101
+ return;
10102
+ }
10103
+ const persisted = loadMobileFlowState();
10104
+ console.info("[blink-sdk] guestPreauthMobileRestore.tryStart", {
10105
+ trigger: lastResumedAt ? "page-resume" : "initial",
10106
+ hasPersistedState: !!persisted,
10107
+ isGuestPreauth: persisted?.isGuestPreauth ?? false,
10108
+ hasGuestSessionToken: !!persisted?.guestSessionToken,
10109
+ hasSessionId: !!persisted?.sessionId,
10110
+ accountId: persisted?.accountId ?? null
10111
+ });
10112
+ if (!persisted?.isGuestPreauth || !persisted.guestSessionToken || !persisted.sessionId) {
10113
+ return;
10114
+ }
10115
+ startedRef.current = true;
10116
+ mobileSetupFlowRef.current = true;
10117
+ if (persisted.accountId) {
10118
+ setupAccountIdRef.current = persisted.accountId;
10119
+ }
10120
+ if (persisted.accountId) {
9487
10121
  dispatch({
9488
- type: "MOBILE_DEEPLINK_READY",
9489
- deeplinkUri: persisted.deeplinkUri
9490
- });
9491
- console.info("[blink-sdk] guestPreauthMobileRestore: starting guest account polling", {
9492
- trigger,
10122
+ type: "GUEST_PREAUTH_DETECTED",
10123
+ accountId: persisted.accountId,
9493
10124
  sessionId: persisted.sessionId
9494
10125
  });
9495
- startGuestAccountPolling(persisted.guestSessionToken, persisted.sessionId);
9496
- };
9497
- const onVisibility = () => {
9498
- console.info("[blink-sdk] guestPreauthMobileRestore: visibilitychange fired", {
9499
- visibilityState: document.visibilityState,
9500
- startedRef: startedRef.current
9501
- });
9502
- if (document.visibilityState === "visible") tryStart("visibilitychange");
9503
- };
9504
- const onPageShow = (e) => {
9505
- console.info("[blink-sdk] guestPreauthMobileRestore: pageshow fired", {
9506
- persisted: e.persisted,
9507
- visibilityState: document.visibilityState,
9508
- startedRef: startedRef.current
9509
- });
9510
- if (document.visibilityState === "visible") tryStart("pageshow");
9511
- };
9512
- tryStart("initial");
9513
- document.addEventListener("visibilitychange", onVisibility);
9514
- window.addEventListener("pageshow", onPageShow);
9515
- return () => {
9516
- document.removeEventListener("visibilitychange", onVisibility);
9517
- window.removeEventListener("pageshow", onPageShow);
9518
- };
9519
- }, [privyReady, dispatch, mobileSetupFlowRef, setupAccountIdRef, startGuestAccountPolling]);
10126
+ }
10127
+ dispatch({
10128
+ type: "MOBILE_DEEPLINK_READY",
10129
+ deeplinkUri: persisted.deeplinkUri
10130
+ });
10131
+ console.info("[blink-sdk] guestPreauthMobileRestore: starting guest account polling", {
10132
+ sessionId: persisted.sessionId
10133
+ });
10134
+ startGuestAccountPolling(persisted.guestSessionToken, persisted.sessionId);
10135
+ }, [privyReady, lastResumedAt, dispatch, mobileSetupFlowRef, setupAccountIdRef, startGuestAccountPolling]);
9520
10136
  }
9521
10137
  function useSelectSourceEffect(deps) {
9522
10138
  const {
@@ -9820,7 +10436,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9820
10436
  react.useEffect(() => {
9821
10437
  if (!isDesktop || state.guestPreauthorizing) return;
9822
10438
  if (!state.privyAuthenticated || !state.activeCredentialId || !state.selectedAccountId) return;
9823
- const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && !authExecutor.pendingOneTapSetup;
10439
+ const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && (!authExecutor.pendingOneTapSetup || authExecutor.authorizationSessionStackDepth > 1);
9824
10440
  if (shouldPin && !state.standardDesktopInlineOpenWallet) {
9825
10441
  dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: true });
9826
10442
  return;
@@ -9838,6 +10454,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9838
10454
  authExecutor.executing,
9839
10455
  authExecutor.pendingSelectSource,
9840
10456
  authExecutor.pendingOneTapSetup,
10457
+ authExecutor.authorizationSessionStackDepth,
9841
10458
  dispatch
9842
10459
  ]);
9843
10460
  }
@@ -9856,7 +10473,84 @@ function useGuestAccountAutoPollingEffect(deps) {
9856
10473
  startPolling(guestSessionToken);
9857
10474
  }, [mobileFlow, isGuestFlow, guestSessionToken, isPolling, guestAccount, startPolling]);
9858
10475
  }
9859
- function useGuestAccountPolling(intervalMs = 3e3) {
10476
+ var WATCHDOG_INTERVAL_MS = 1e3;
10477
+ var WATCHDOG_THRESHOLD_MS = 3e3;
10478
+ var DEDUP_COOLDOWN_MS = 500;
10479
+ function usePageResume(callback) {
10480
+ const callbackRef = react.useRef(callback);
10481
+ callbackRef.current = callback;
10482
+ react.useEffect(() => {
10483
+ let lastTickAt = Date.now();
10484
+ let lastFiredAt = 0;
10485
+ const fire = (source, frozenDurationMs) => {
10486
+ const now = Date.now();
10487
+ if (now - lastFiredAt < DEDUP_COOLDOWN_MS) return;
10488
+ lastFiredAt = now;
10489
+ callbackRef.current({ frozenDurationMs, source });
10490
+ };
10491
+ const watchdogId = window.setInterval(() => {
10492
+ const now = Date.now();
10493
+ const elapsed = now - lastTickAt;
10494
+ lastTickAt = now;
10495
+ if (elapsed > WATCHDOG_THRESHOLD_MS) {
10496
+ fire("watchdog", elapsed);
10497
+ }
10498
+ }, WATCHDOG_INTERVAL_MS);
10499
+ const handleVisibility = () => {
10500
+ const now = Date.now();
10501
+ if (document.visibilityState === "visible") {
10502
+ fire("visibilitychange", now - lastTickAt);
10503
+ }
10504
+ lastTickAt = now;
10505
+ };
10506
+ const handlePageShow = (_e) => {
10507
+ const now = Date.now();
10508
+ if (document.visibilityState === "visible") {
10509
+ fire("pageshow", now - lastTickAt);
10510
+ }
10511
+ lastTickAt = now;
10512
+ };
10513
+ const handleFocus = () => {
10514
+ const now = Date.now();
10515
+ const elapsed = now - lastTickAt;
10516
+ lastTickAt = now;
10517
+ if (elapsed > WATCHDOG_THRESHOLD_MS) {
10518
+ fire("focus", elapsed);
10519
+ }
10520
+ };
10521
+ document.addEventListener("visibilitychange", handleVisibility);
10522
+ window.addEventListener("pageshow", handlePageShow);
10523
+ window.addEventListener("focus", handleFocus);
10524
+ return () => {
10525
+ window.clearInterval(watchdogId);
10526
+ document.removeEventListener("visibilitychange", handleVisibility);
10527
+ window.removeEventListener("pageshow", handlePageShow);
10528
+ window.removeEventListener("focus", handleFocus);
10529
+ };
10530
+ }, []);
10531
+ }
10532
+ var STUCK_REF_THRESHOLD_MS = 5e3;
10533
+ function usePageResumeEffect(deps) {
10534
+ const { dispatch, handlingMobileReturnRef } = deps;
10535
+ const dispatchRef = react.useRef(dispatch);
10536
+ dispatchRef.current = dispatch;
10537
+ usePageResume((info) => {
10538
+ console.info("[blink-sdk] PAGE_RESUMED", {
10539
+ source: info.source,
10540
+ frozenDurationMs: info.frozenDurationMs
10541
+ });
10542
+ dispatchRef.current({ type: "PAGE_RESUMED", frozenDurationMs: info.frozenDurationMs });
10543
+ if (info.frozenDurationMs > STUCK_REF_THRESHOLD_MS) {
10544
+ if (handlingMobileReturnRef.current) {
10545
+ console.info("[blink-sdk] PAGE_RESUMED: clearing stuck handlingMobileReturnRef", {
10546
+ frozenDurationMs: info.frozenDurationMs
10547
+ });
10548
+ handlingMobileReturnRef.current = false;
10549
+ }
10550
+ }
10551
+ });
10552
+ }
10553
+ function useGuestAccountPolling(intervalMs = 3e3, lastResumedAt = 0) {
9860
10554
  const { apiBaseUrl } = useBlinkConfig();
9861
10555
  const [guestAccount, setGuestAccount] = react.useState(null);
9862
10556
  const [isPolling, setIsPolling] = react.useState(false);
@@ -9954,36 +10648,71 @@ function useGuestAccountPolling(intervalMs = 3e3) {
9954
10648
  [poll, intervalMs, stopPolling]
9955
10649
  );
9956
10650
  react.useEffect(() => {
9957
- const handleVisibility = () => {
9958
- console.info("[blink-sdk] useGuestAccountPolling: visibilitychange fired", {
9959
- visibilityState: document.visibilityState,
9960
- hasToken: !!guestTokenRef.current,
9961
- hasSessionId: !!sessionIdRef.current
9962
- });
9963
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9964
- void poll();
9965
- }
9966
- };
9967
- const handlePageShow = (e) => {
9968
- console.info("[blink-sdk] useGuestAccountPolling: pageshow fired", {
9969
- persisted: e.persisted,
9970
- visibilityState: document.visibilityState,
9971
- hasToken: !!guestTokenRef.current,
9972
- hasSessionId: !!sessionIdRef.current
10651
+ if (!lastResumedAt || !guestTokenRef.current) return;
10652
+ console.info("[blink-sdk] useGuestAccountPolling: page resumed, triggering poll", {
10653
+ lastResumedAt,
10654
+ hasToken: !!guestTokenRef.current,
10655
+ hasSessionId: !!sessionIdRef.current
10656
+ });
10657
+ void poll();
10658
+ }, [lastResumedAt, poll]);
10659
+ react.useEffect(() => () => stopPolling(), [stopPolling]);
10660
+ return { guestAccount, error, isPolling, startPolling, stopPolling };
10661
+ }
10662
+ function useDepositFeeEstimate({
10663
+ apiBaseUrl,
10664
+ getAccessToken,
10665
+ walletId,
10666
+ sourceTokenAddress,
10667
+ destination,
10668
+ amount,
10669
+ enabled
10670
+ }) {
10671
+ const [quoteId, setQuoteId] = react.useState(null);
10672
+ const [quoteFee, setQuoteFee] = react.useState(null);
10673
+ const [quoteLoading, setQuoteLoading] = react.useState(false);
10674
+ const abortRef = react.useRef(null);
10675
+ const fetchQuote = react.useCallback(async () => {
10676
+ if (!enabled || !walletId || !sourceTokenAddress) {
10677
+ setQuoteId(null);
10678
+ setQuoteFee(null);
10679
+ setQuoteLoading(false);
10680
+ return;
10681
+ }
10682
+ abortRef.current?.abort();
10683
+ const controller = new AbortController();
10684
+ abortRef.current = controller;
10685
+ setQuoteLoading(true);
10686
+ try {
10687
+ const token = await getAccessToken();
10688
+ if (!token || controller.signal.aborted) return;
10689
+ const quote = await postTransferQuote(apiBaseUrl, token, {
10690
+ walletId,
10691
+ sourceTokenAddress,
10692
+ destination,
10693
+ amount: { amount, currency: "USD" }
9973
10694
  });
9974
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9975
- void poll();
10695
+ if (controller.signal.aborted) return;
10696
+ setQuoteId(quote.id);
10697
+ setQuoteFee(quote.fee);
10698
+ } catch (err) {
10699
+ if (controller.signal.aborted) return;
10700
+ console.warn("[blink-sdk] Fee quote failed:", err);
10701
+ setQuoteId(null);
10702
+ setQuoteFee(null);
10703
+ } finally {
10704
+ if (!controller.signal.aborted) {
10705
+ setQuoteLoading(false);
9976
10706
  }
9977
- };
9978
- document.addEventListener("visibilitychange", handleVisibility);
9979
- window.addEventListener("pageshow", handlePageShow);
10707
+ }
10708
+ }, [enabled, walletId, sourceTokenAddress, destination, amount, apiBaseUrl, getAccessToken]);
10709
+ react.useEffect(() => {
10710
+ fetchQuote();
9980
10711
  return () => {
9981
- document.removeEventListener("visibilitychange", handleVisibility);
9982
- window.removeEventListener("pageshow", handlePageShow);
10712
+ abortRef.current?.abort();
9983
10713
  };
9984
- }, [poll]);
9985
- react.useEffect(() => () => stopPolling(), [stopPolling]);
9986
- return { guestAccount, error, isPolling, startPolling, stopPolling };
10714
+ }, [fetchQuote]);
10715
+ return { quoteId, quoteFee, quoteLoading };
9987
10716
  }
9988
10717
  function BlinkPayment(props) {
9989
10718
  const resetKey = react.useRef(0);
@@ -10016,14 +10745,14 @@ function BlinkPaymentInner({
10016
10745
  paymentReducer,
10017
10746
  {
10018
10747
  depositAmount,
10019
- passkeyPopupNeeded: isSafari() && isInCrossOriginIframe(),
10748
+ passkeyPopupNeeded: isSafari() && isInCrossOriginIframe() && !isDesktop,
10020
10749
  activeCredentialId: typeof window === "undefined" ? null : window.localStorage.getItem(ACTIVE_CREDENTIAL_STORAGE_KEY)
10021
10750
  },
10022
10751
  createInitialState
10023
10752
  );
10024
10753
  const authExecutor = useAuthorizationExecutor();
10025
10754
  const polling = useTransferPolling();
10026
- const guestAccountPolling = useGuestAccountPolling();
10755
+ const guestAccountPolling = useGuestAccountPolling(3e3, state.lastResumedAt);
10027
10756
  const transferSigning = useTransferSigning();
10028
10757
  const mobileFlowRefs = {
10029
10758
  mobileSetupFlowRef: react.useRef(false),
@@ -10041,6 +10770,15 @@ function BlinkPaymentInner({
10041
10770
  state.accounts,
10042
10771
  state.knownCredentialIds
10043
10772
  );
10773
+ const depositFee = useDepositFeeEstimate({
10774
+ apiBaseUrl,
10775
+ getAccessToken,
10776
+ walletId: state.selectedWalletId,
10777
+ sourceTokenAddress: derived.selectedSource?.address,
10778
+ destination,
10779
+ amount: depositAmount ?? 5,
10780
+ enabled: state.phase.step === "deposit" && !state.isGuestFlow && authenticated
10781
+ });
10044
10782
  const transfer = useTransferHandlers({
10045
10783
  dispatch,
10046
10784
  getAccessToken,
@@ -10058,6 +10796,9 @@ function BlinkPaymentInner({
10058
10796
  sourceTokenAddress: derived.selectedSource?.address,
10059
10797
  activeCredentialId: state.activeCredentialId,
10060
10798
  selectedAccountId: state.selectedAccountId,
10799
+ selectedWalletId: state.selectedWalletId,
10800
+ selectedTokenSymbol: state.selectedTokenSymbol,
10801
+ quoteId: depositFee.quoteId,
10061
10802
  transfer: state.transfer,
10062
10803
  accounts: state.accounts
10063
10804
  });
@@ -10074,7 +10815,8 @@ function BlinkPaymentInner({
10074
10815
  guestTransferId: state.guestTransferId,
10075
10816
  guestSessionToken: state.guestSessionToken
10076
10817
  },
10077
- onComplete
10818
+ onComplete,
10819
+ state.lastResumedAt
10078
10820
  );
10079
10821
  const sourceSelection = useSourceSelectionHandlers(dispatch, authExecutor, {
10080
10822
  guestPreauthSessionId: state.guestPreauthSessionId,
@@ -10170,10 +10912,15 @@ function BlinkPaymentInner({
10170
10912
  dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
10171
10913
  }
10172
10914
  }, [depositAmount, dispatch]);
10915
+ usePageResumeEffect({
10916
+ dispatch,
10917
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef
10918
+ });
10173
10919
  usePrivySessionSyncEffect({ dispatch, ready, authenticated });
10174
10920
  useGuestPreauthMobileRestoreEffect({
10175
10921
  dispatch,
10176
10922
  privyReady: state.privyReady,
10923
+ lastResumedAt: state.lastResumedAt,
10177
10924
  mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
10178
10925
  setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
10179
10926
  startGuestAccountPolling: guestAccountPolling.startPolling
@@ -10186,25 +10933,24 @@ function BlinkPaymentInner({
10186
10933
  guestAccount: guestAccountPolling.guestAccount,
10187
10934
  startPolling: guestAccountPolling.startPolling
10188
10935
  });
10189
- const guestSessionTokenRef = react.useRef(state.guestSessionToken);
10190
- guestSessionTokenRef.current = state.guestSessionToken;
10191
- const guestPreauthSessionIdRef = react.useRef(state.guestPreauthSessionId);
10192
- guestPreauthSessionIdRef.current = state.guestPreauthSessionId;
10193
10936
  react.useEffect(() => {
10194
- const handleVisibility = () => {
10195
- if (document.visibilityState !== "visible") return;
10196
- const token = guestSessionTokenRef.current;
10197
- const sessionId = guestPreauthSessionIdRef.current;
10198
- if (!token) return;
10199
- if (guestAccountPolling.isPolling || guestAccountPolling.guestAccount) return;
10200
- console.info("[blink-sdk] warm-return: restarting guest account polling from React state", {
10201
- sessionId: sessionId ?? "(none \u2014 account-only mode)"
10202
- });
10203
- guestAccountPolling.startPolling(token, sessionId ?? void 0);
10204
- };
10205
- document.addEventListener("visibilitychange", handleVisibility);
10206
- return () => document.removeEventListener("visibilitychange", handleVisibility);
10207
- }, [guestAccountPolling.isPolling, guestAccountPolling.guestAccount, guestAccountPolling.startPolling]);
10937
+ if (!state.lastResumedAt) return;
10938
+ const token = state.guestSessionToken;
10939
+ const sessionId = state.guestPreauthSessionId;
10940
+ if (!token) return;
10941
+ if (guestAccountPolling.isPolling || guestAccountPolling.guestAccount) return;
10942
+ console.info("[blink-sdk] warm-return: restarting guest account polling from React state", {
10943
+ sessionId: sessionId ?? "(none \u2014 account-only mode)"
10944
+ });
10945
+ guestAccountPolling.startPolling(token, sessionId ?? void 0);
10946
+ }, [
10947
+ state.lastResumedAt,
10948
+ state.guestSessionToken,
10949
+ state.guestPreauthSessionId,
10950
+ guestAccountPolling.isPolling,
10951
+ guestAccountPolling.guestAccount,
10952
+ guestAccountPolling.startPolling
10953
+ ]);
10208
10954
  react.useEffect(() => {
10209
10955
  console.info("[blink-sdk] guestPreauthCompletion effect", {
10210
10956
  hasGuestAccount: !!guestAccountPolling.guestAccount,
@@ -10355,7 +11101,10 @@ function BlinkPaymentInner({
10355
11101
  dispatch({ type: "BACK_TO_LOGIN" });
10356
11102
  },
10357
11103
  onRegisterPasskey: passkey.handleRegisterPasskey,
11104
+ onVerifyPasskey: passkey.handleVerifyPasskey,
11105
+ onCreateNewPasskey: passkey.handleCreateNewPasskey,
10358
11106
  onCreatePasskeyViaPopup: passkey.handleCreatePasskeyViaPopup,
11107
+ onCreateNewPasskeyViaPopup: passkey.handleCreateNewPasskeyViaPopup,
10359
11108
  onVerifyPasskeyViaPopup: passkey.handleVerifyPasskeyViaPopup,
10360
11109
  onPrepareProvider: provider.handlePrepareProvider,
10361
11110
  onSelectProvider: provider.handleSelectProvider,
@@ -10369,6 +11118,11 @@ function BlinkPaymentInner({
10369
11118
  onLogout: handleLogout,
10370
11119
  onNewPayment: handleNewPayment,
10371
11120
  onSetPhase: (phase) => dispatch({ type: "SET_USER_INTENT", intent: phase }),
11121
+ onBackFromSubflow: () => {
11122
+ authExecutor.cancelPendingExecution();
11123
+ dispatch({ type: "RESTORE_SELECTION" });
11124
+ dispatch({ type: "SET_USER_INTENT", intent: { step: "deposit" } });
11125
+ },
10372
11126
  onSetAuthInput: auth.setAuthInput,
10373
11127
  onSetOtpCode: (code) => {
10374
11128
  auth.setOtpCode(code);
@@ -10402,7 +11156,8 @@ function BlinkPaymentInner({
10402
11156
  handleLogout,
10403
11157
  handleNewPayment,
10404
11158
  onDismiss,
10405
- dispatch
11159
+ dispatch,
11160
+ authExecutor
10406
11161
  ]);
10407
11162
  return /* @__PURE__ */ jsxRuntime.jsx(
10408
11163
  StepRenderer,
@@ -10451,7 +11206,10 @@ function BlinkPaymentInner({
10451
11206
  guestSettingSender: guestTransfer.settingSender,
10452
11207
  guestPendingEntry: guestTransfer.pendingGuestEntry,
10453
11208
  guestQuoteFee: guestTransfer.guestFee?.quote ?? null,
10454
- guestQuoteLoading: guestTransfer.guestQuoteLoading
11209
+ guestQuoteLoading: guestTransfer.guestQuoteLoading,
11210
+ depositQuoteId: depositFee.quoteId,
11211
+ depositQuoteFee: depositFee.quoteFee,
11212
+ depositQuoteLoading: depositFee.quoteLoading
10455
11213
  },
10456
11214
  handlers
10457
11215
  }
@@ -10459,6 +11217,7 @@ function BlinkPaymentInner({
10459
11217
  }
10460
11218
 
10461
11219
  exports.AdvancedSourceScreen = AdvancedSourceScreen;
11220
+ exports.AuthorizationSessionCancelledError = AuthorizationSessionCancelledError;
10462
11221
  exports.BLINK_LOGO = BLINK_LOGO;
10463
11222
  exports.BLINK_MASCOT = BLINK_MASCOT;
10464
11223
  exports.BlinkLoadingScreen = BlinkLoadingScreen;
@@ -10503,6 +11262,8 @@ exports.findDevicePasskey = findDevicePasskey;
10503
11262
  exports.findDevicePasskeyViaPopup = findDevicePasskeyViaPopup;
10504
11263
  exports.getTheme = getTheme;
10505
11264
  exports.guestEntryMatchingRecommended = guestEntryMatchingRecommended;
11265
+ exports.isAuthorizationSessionCancelled = isAuthorizationSessionCancelled;
11266
+ exports.isUserDismissedAuthorizationError = isUserDismissedAuthorizationError;
10506
11267
  exports.lightTheme = lightTheme;
10507
11268
  exports.mapGuestPickerEntries = mapGuestPickerEntries;
10508
11269
  exports.resolvePasskeyRpId = resolvePasskeyRpId;