@swype-org/react-sdk 0.1.301 → 0.1.329

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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;
@@ -1793,10 +1952,11 @@ function resolvePhase(state) {
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
1954
  const shouldPromptPasskeyVerification = missingActivePasskeyCredential && state.knownCredentialIds.length > 0 && state.passkeyPopupNeeded;
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() {
@@ -4081,6 +4319,22 @@ var loginButtonStyle = (accentColor) => ({
4081
4319
  fontFamily: "inherit",
4082
4320
  textDecoration: "underline"
4083
4321
  });
4322
+
4323
+ // src/setupScreenSelection.ts
4324
+ function matchesSetupTokenSelection(opt, selectedTokenSymbol, selectedChainName, allOptions) {
4325
+ if (!selectedTokenSymbol || opt.symbol !== selectedTokenSymbol) {
4326
+ return false;
4327
+ }
4328
+ const chain = selectedChainName?.trim();
4329
+ if (chain) {
4330
+ return opt.chainName === chain;
4331
+ }
4332
+ const sameSymbol = allOptions.filter((o) => o.symbol === selectedTokenSymbol);
4333
+ if (sameSymbol.length === 1) {
4334
+ return opt.chainName === sameSymbol[0].chainName;
4335
+ }
4336
+ return false;
4337
+ }
4084
4338
  var DEFAULT_MAX = 1e7;
4085
4339
  var ABSOLUTE_MIN = 0.01;
4086
4340
  var PRESETS = [100, 250, 1e3];
@@ -4096,13 +4350,16 @@ function SetupScreen({
4096
4350
  loading,
4097
4351
  error,
4098
4352
  selectedTokenSymbol,
4353
+ selectedChainName,
4099
4354
  tokenOptions,
4100
4355
  onSelectToken
4101
4356
  }) {
4102
4357
  const { tokens } = useBlinkConfig();
4103
4358
  const effectiveMax = DEFAULT_MAX;
4104
4359
  const effectiveMin = Math.min(ABSOLUTE_MIN, effectiveMax);
4105
- const [limit, setLimit] = react.useState(() => Math.min(availableBalance, effectiveMax));
4360
+ const autoLimit = Math.min(Math.max(availableBalance, 0), effectiveMax);
4361
+ const [userChosenLimit, setUserChosenLimit] = react.useState(null);
4362
+ const limit = userChosenLimit ?? autoLimit;
4106
4363
  const [activePreset, setActivePreset] = react.useState(null);
4107
4364
  const [showAdvanced, setShowAdvanced] = react.useState(false);
4108
4365
  const [editing, setEditing] = react.useState(false);
@@ -4141,20 +4398,22 @@ function SetupScreen({
4141
4398
  const commitEdit = react.useCallback(() => {
4142
4399
  const parsed = parseFloat(inputValue);
4143
4400
  if (!isNaN(parsed)) {
4144
- setLimit(Math.min(effectiveMax, Math.max(effectiveMin, Math.round(parsed * 100) / 100)));
4401
+ setUserChosenLimit(
4402
+ Math.min(effectiveMax, Math.max(effectiveMin, Math.round(parsed * 100) / 100))
4403
+ );
4145
4404
  }
4146
4405
  setActivePreset(null);
4147
4406
  setEditing(false);
4148
4407
  }, [inputValue, effectiveMax, effectiveMin]);
4149
4408
  const selectPreset = (value) => {
4150
- setLimit(Math.min(value, effectiveMax));
4409
+ setUserChosenLimit(Math.min(value, effectiveMax));
4151
4410
  setActivePreset(value);
4152
4411
  };
4153
4412
  const selectMax = () => {
4154
- setLimit(Math.min(availableBalance, effectiveMax));
4413
+ setUserChosenLimit(autoLimit);
4155
4414
  setActivePreset("max");
4156
4415
  };
4157
- const selectedOption = tokenOptions?.find((o) => o.symbol === selectedTokenSymbol);
4416
+ const options = tokenOptions ?? [];
4158
4417
  return /* @__PURE__ */ jsxRuntime.jsxs(
4159
4418
  ScreenLayout,
4160
4419
  {
@@ -4187,12 +4446,7 @@ function SetupScreen({
4187
4446
  children: [
4188
4447
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenRowLabelStyle(tokens.text), children: "Token for one tap" }),
4189
4448
  /* @__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: [
4449
+ /* @__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
4450
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "18", cy: "18", r: "18", fill: "#2DB84B" }),
4197
4451
  /* @__PURE__ */ jsxRuntime.jsx("text", { x: "18", y: "23", textAnchor: "middle", fontSize: "18", fill: "#fff", fontWeight: "700", children: "$" })
4198
4452
  ] }) }),
@@ -4204,14 +4458,21 @@ function SetupScreen({
4204
4458
  tokenDropdownOpen && tokenOptions && tokenOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: dropdownOuterStyle(tokens), children: [
4205
4459
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: dropdownLabelStyle(tokens.textMuted), children: "Choose token" }),
4206
4460
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: dropdownInnerStyle(tokens), children: tokenOptions.map((opt, index) => {
4207
- const selected = opt.symbol === selectedTokenSymbol;
4461
+ const selected = matchesSetupTokenSelection(
4462
+ opt,
4463
+ selectedTokenSymbol,
4464
+ selectedChainName,
4465
+ options
4466
+ );
4208
4467
  const isLast = index === tokenOptions.length - 1;
4468
+ const authorized = !opt.status || opt.status === "AUTHORIZED";
4209
4469
  return /* @__PURE__ */ jsxRuntime.jsxs(
4210
4470
  "button",
4211
4471
  {
4212
4472
  type: "button",
4213
4473
  onClick: () => handlePickToken(opt),
4214
4474
  style: dropdownRowStyle(tokens, selected, isLast),
4475
+ "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
4476
  children: [
4216
4477
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: dropdownRowLeftStyle, children: [
4217
4478
  /* @__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 +4482,12 @@ function SetupScreen({
4221
4482
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: dropdownTokenDotStyle(tokens.textMuted), children: "\xB7" }),
4222
4483
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: dropdownTokenChainStyle(tokens.textMuted), children: opt.chainName })
4223
4484
  ] }),
4224
- opt.balance != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: dropdownTokenBalanceStyle(tokens.textMuted), children: [
4225
- "$",
4226
- opt.balance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
4485
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: dropdownTokenBalanceRowStyle, children: [
4486
+ opt.balance != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: dropdownTokenBalanceStyle(tokens.textMuted), children: [
4487
+ "$",
4488
+ opt.balance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
4489
+ ] }),
4490
+ !authorized && /* @__PURE__ */ jsxRuntime.jsx("span", { style: dropdownNotAuthorizedStyle(tokens.textMuted), children: "Not authorized" })
4227
4491
  ] })
4228
4492
  ] })
4229
4493
  ] }),
@@ -4343,11 +4607,6 @@ var tokenRowRightStyle = {
4343
4607
  gap: 6,
4344
4608
  flexShrink: 0
4345
4609
  };
4346
- var tokenRowSymbolStyle = (color) => ({
4347
- fontSize: "0.84rem",
4348
- fontWeight: 500,
4349
- color
4350
- });
4351
4610
  var advancedToggleStyle = (accentColor) => ({
4352
4611
  display: "block",
4353
4612
  margin: "4px auto 24px",
@@ -4495,10 +4754,20 @@ var dropdownTokenChainStyle = (color) => ({
4495
4754
  fontWeight: 400,
4496
4755
  color
4497
4756
  });
4757
+ var dropdownTokenBalanceRowStyle = {
4758
+ display: "flex",
4759
+ alignItems: "center",
4760
+ gap: 8
4761
+ };
4498
4762
  var dropdownTokenBalanceStyle = (color) => ({
4499
4763
  fontSize: "0.78rem",
4500
4764
  color
4501
4765
  });
4766
+ var dropdownNotAuthorizedStyle = (color) => ({
4767
+ fontSize: "0.7rem",
4768
+ fontWeight: 500,
4769
+ color
4770
+ });
4502
4771
  var dropdownRadioEmptyStyle = (borderColor) => ({
4503
4772
  width: 22,
4504
4773
  height: 22,
@@ -4613,6 +4882,38 @@ var waitHintStyle = (color) => ({
4613
4882
  color,
4614
4883
  margin: 0
4615
4884
  });
4885
+
4886
+ // src/feeFormat.ts
4887
+ function isPreciseMoneyNonPositive(fee) {
4888
+ const raw = fee.value.trim();
4889
+ if (!/^-?\d+(\.\d*)?$/.test(raw)) return false;
4890
+ const n = Number(raw);
4891
+ return Number.isFinite(n) && n <= 0;
4892
+ }
4893
+ function formatNonPositiveFeeDisplay(fee) {
4894
+ if (fee.currency === "USD") return "Under $0.01";
4895
+ return `Less than 0.01 ${fee.currency}`;
4896
+ }
4897
+ function formatPreciseMoneyForDisplay(fee) {
4898
+ const raw = fee.value.trim();
4899
+ if (fee.currency === "USD") {
4900
+ if (!/^\d+(\.\d*)?$/.test(raw)) {
4901
+ return `$${raw}`;
4902
+ }
4903
+ const [whole, frac = ""] = raw.split(".");
4904
+ const dec = `${frac}00`.slice(0, 2);
4905
+ const intFmt = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
4906
+ return `$${intFmt}.${dec}`;
4907
+ }
4908
+ return `${raw} ${fee.currency}`;
4909
+ }
4910
+ function formatUsdTwoDecimals(value) {
4911
+ const n = Number(value);
4912
+ return (Number.isFinite(n) ? n : 0).toLocaleString("en-US", {
4913
+ minimumFractionDigits: 2,
4914
+ maximumFractionDigits: 2
4915
+ });
4916
+ }
4616
4917
  function DepositScreen({
4617
4918
  merchantName,
4618
4919
  availableBalance,
@@ -4620,6 +4921,8 @@ function DepositScreen({
4620
4921
  tokenCount,
4621
4922
  initialAmount,
4622
4923
  minDepositFloor,
4924
+ quoteFee,
4925
+ quoteLoading,
4623
4926
  processing,
4624
4927
  error,
4625
4928
  onDeposit,
@@ -4634,13 +4937,19 @@ function DepositScreen({
4634
4937
  onAuthorizeAccount,
4635
4938
  onAddProvider,
4636
4939
  onSelectToken,
4940
+ tokenOptions,
4941
+ onPickToken,
4637
4942
  selectedSourceLabel,
4638
- selectedTokenSymbol
4943
+ selectedTokenSymbol,
4944
+ selectedChainName
4639
4945
  }) {
4640
4946
  const { tokens } = useBlinkConfig();
4641
4947
  const amount = initialAmount;
4642
4948
  const [accountPickerOpen, setAccountPickerOpen] = react.useState(false);
4949
+ const [tokenPickerOpen, setTokenPickerOpen] = react.useState(false);
4643
4950
  const pickerRef = react.useRef(null);
4951
+ const tokenPickerRef = react.useRef(null);
4952
+ const hasTokenDropdown = tokenOptions != null && tokenOptions.length > 0 && onPickToken != null;
4644
4953
  react.useEffect(() => {
4645
4954
  if (!accountPickerOpen) return;
4646
4955
  const handleClick = (e) => {
@@ -4651,6 +4960,28 @@ function DepositScreen({
4651
4960
  document.addEventListener("mousedown", handleClick);
4652
4961
  return () => document.removeEventListener("mousedown", handleClick);
4653
4962
  }, [accountPickerOpen]);
4963
+ react.useEffect(() => {
4964
+ if (!tokenPickerOpen) return;
4965
+ const handleMouseDown = (e) => {
4966
+ if (tokenPickerRef.current && !tokenPickerRef.current.contains(e.target)) {
4967
+ setTokenPickerOpen(false);
4968
+ }
4969
+ };
4970
+ document.addEventListener("mousedown", handleMouseDown);
4971
+ return () => document.removeEventListener("mousedown", handleMouseDown);
4972
+ }, [tokenPickerOpen]);
4973
+ const handleTokenBadgeClick = react.useCallback(() => {
4974
+ if (hasTokenDropdown) {
4975
+ setTokenPickerOpen((v) => !v);
4976
+ setAccountPickerOpen(false);
4977
+ } else {
4978
+ onSelectToken?.();
4979
+ }
4980
+ }, [hasTokenDropdown, onSelectToken]);
4981
+ const handlePickToken = react.useCallback((opt) => {
4982
+ onPickToken?.(opt.symbol, opt.chainName, opt.walletId);
4983
+ setTokenPickerOpen(false);
4984
+ }, [onPickToken]);
4654
4985
  const selectedAccount = accounts?.find((a) => a.id === selectedAccountId);
4655
4986
  const selectedProviderName = selectedAccount?.name ?? "Wallet";
4656
4987
  const selectedProviderLogo = KNOWN_LOGOS[selectedProviderName.toLowerCase()];
@@ -4662,36 +4993,95 @@ function DepositScreen({
4662
4993
  ScreenLayout,
4663
4994
  {
4664
4995
  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" })),
4996
+ !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: "Confirm" })),
4666
4997
  /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
4667
4998
  ] }),
4668
4999
  children: [
4669
5000
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onBack, onLogout }),
4670
5001
  /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: pickerRef, style: depositCardWrapStyle, children: [
4671
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
5002
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: depositCardStyle(tokens), children: [
4672
5003
  /* @__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 })
5004
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: tokenPickerRef, children: [
5005
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: amountRowStyle, children: [
5006
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: amountValueStyle(tokens.text), children: [
5007
+ "$",
5008
+ formatUsdTwoDecimals(amount)
5009
+ ] }),
5010
+ /* @__PURE__ */ jsxRuntime.jsxs(
5011
+ "button",
5012
+ {
5013
+ type: "button",
5014
+ "data-testid": "deposit-token-badge",
5015
+ onClick: handleTokenBadgeClick,
5016
+ style: tokenIconButtonStyle(hasTokenDropdown || !!onSelectToken),
5017
+ "aria-expanded": hasTokenDropdown ? tokenPickerOpen : void 0,
5018
+ "aria-haspopup": hasTokenDropdown ? "listbox" : void 0,
5019
+ children: [
5020
+ /* @__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: [
5021
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "18", cy: "18", r: "18", fill: "#2DB84B" }),
5022
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: "18", y: "23", textAnchor: "middle", fontSize: "18", fill: "#fff", fontWeight: "700", children: "$" })
5023
+ ] }) }),
5024
+ /* @__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" }) })
5025
+ ]
5026
+ }
5027
+ )
4677
5028
  ] }),
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
- )
5029
+ tokenPickerOpen && tokenOptions && tokenOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownStyle(tokens), children: [
5030
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownLabelStyle(tokens.textMuted), children: "Choose token" }),
5031
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownInnerStyle(tokens), children: tokenOptions.map((opt, index) => {
5032
+ const selected = opt.symbol === selectedTokenSymbol && (!selectedChainName || opt.chainName === selectedChainName);
5033
+ const isLast = index === tokenOptions.length - 1;
5034
+ const authorized = !opt.status || opt.status === "AUTHORIZED";
5035
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5036
+ "button",
5037
+ {
5038
+ type: "button",
5039
+ onClick: () => handlePickToken(opt),
5040
+ style: tokenDropdownRowStyle(tokens, selected, isLast),
5041
+ "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" : ""}`,
5042
+ children: [
5043
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownRowLeftStyle, children: [
5044
+ /* @__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: "$" }) }),
5045
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownInfoStyle, children: [
5046
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownNameRowStyle, children: [
5047
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownSymbolStyle(tokens.text), children: opt.symbol }),
5048
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownDotStyle(tokens.textMuted), children: "\xB7" }),
5049
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownChainStyle(tokens.textMuted), children: opt.chainName })
5050
+ ] }),
5051
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownBalanceRowStyle, children: [
5052
+ opt.balance != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: tokenDropdownBalanceStyle(tokens.textMuted), children: [
5053
+ "$",
5054
+ formatUsdTwoDecimals(opt.balance)
5055
+ ] }),
5056
+ !authorized && /* @__PURE__ */ jsxRuntime.jsx("span", { style: tokenDropdownNotAuthStyle(tokens.textMuted), children: "Not authorized" })
5057
+ ] })
5058
+ ] })
5059
+ ] }),
5060
+ selected ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 22 22", fill: "none", children: [
5061
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "11", fill: tokens.success }),
5062
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 11l3 3 5-5", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
5063
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownRadioStyle(tokens.border) })
5064
+ ]
5065
+ },
5066
+ `${opt.chainName}-${opt.symbol}`
5067
+ );
5068
+ }) })
5069
+ ] })
4693
5070
  ] }),
4694
- !accountPickerOpen && /* @__PURE__ */ jsxRuntime.jsxs(
5071
+ !accountPickerOpen && !tokenPickerOpen && (() => {
5072
+ if (quoteLoading) {
5073
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: feeRowContainerStyle, "aria-live": "polite", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle(tokens.textMuted), children: "Getting fee estimate\u2026" }) });
5074
+ }
5075
+ if (quoteFee) {
5076
+ const feeText = isPreciseMoneyNonPositive(quoteFee) ? formatNonPositiveFeeDisplay(quoteFee) : formatPreciseMoneyForDisplay(quoteFee);
5077
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: feeRowContainerStyle, "aria-live": "polite", children: [
5078
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle(tokens.textMuted), children: "Fee estimate: " }),
5079
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowAmountStyle(tokens.textSecondary), children: feeText })
5080
+ ] });
5081
+ }
5082
+ return null;
5083
+ })(),
5084
+ !accountPickerOpen && !tokenPickerOpen && /* @__PURE__ */ jsxRuntime.jsxs(
4695
5085
  "button",
4696
5086
  {
4697
5087
  type: "button",
@@ -4701,7 +5091,7 @@ function DepositScreen({
4701
5091
  selectedProviderLogo ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: selectedProviderLogo, alt: selectedProviderName, style: providerLogoStyle }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: providerFallbackStyle(tokens.textMuted), children: selectedProviderName.charAt(0) }),
4702
5092
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: walletBalanceStyle(tokens.text), children: [
4703
5093
  "$",
4704
- totalAccountBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5094
+ formatUsdTwoDecimals(totalAccountBalance)
4705
5095
  ] }),
4706
5096
  /* @__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
5097
  ]
@@ -4742,7 +5132,7 @@ function DepositScreen({
4742
5132
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: accountAddressStyle(tokens.text), children: truncatedAddress ?? account.nickname ?? account.name }),
4743
5133
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: accountBalanceTextStyle(tokens.textMuted), children: [
4744
5134
  "$",
4745
- accountBalance.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5135
+ formatUsdTwoDecimals(accountBalance)
4746
5136
  ] })
4747
5137
  ] })
4748
5138
  ] }),
@@ -4778,21 +5168,21 @@ function DepositScreen({
4778
5168
  " ",
4779
5169
  /* @__PURE__ */ jsxRuntime.jsxs("strong", { style: { color: tokens.text }, children: [
4780
5170
  "$",
4781
- remainingLimit.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
5171
+ formatUsdTwoDecimals(remainingLimit)
4782
5172
  ] }),
4783
5173
  exceedsLimit && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: limitExceededHintStyle(tokens.warning), children: [
4784
5174
  "Your deposit of $",
4785
- amount.toFixed(2),
5175
+ formatUsdTwoDecimals(amount),
4786
5176
  " exceeds your One-Tap limit of $",
4787
- remainingLimit?.toFixed(2) ?? "0.00",
5177
+ remainingLimit != null ? formatUsdTwoDecimals(remainingLimit) : "0.00",
4788
5178
  ". Increase your limit to continue."
4789
5179
  ] })
4790
5180
  ] }),
4791
5181
  !accountPickerOpen && isLowBalance && /* @__PURE__ */ jsxRuntime.jsxs(WarningBanner, { title: "Not enough funds", children: [
4792
5182
  "Your wallet balance is $",
4793
- availableBalance.toFixed(2),
5183
+ formatUsdTwoDecimals(availableBalance),
4794
5184
  " \u2014 you need at least $",
4795
- minDepositFloor.toFixed(2),
5185
+ formatUsdTwoDecimals(minDepositFloor),
4796
5186
  " to deposit via One-Tap."
4797
5187
  ] }),
4798
5188
  error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle6(tokens), children: error })
@@ -4804,6 +5194,11 @@ var depositCardWrapStyle = {
4804
5194
  position: "relative",
4805
5195
  marginBottom: 20
4806
5196
  };
5197
+ var depositCardStyle = (tokens) => ({
5198
+ border: `1px solid ${tokens.border}`,
5199
+ borderRadius: tokens.radiusLg,
5200
+ padding: "16px 20px"
5201
+ });
4807
5202
  var depositLabelStyle = (color) => ({
4808
5203
  fontSize: "0.75rem",
4809
5204
  fontWeight: 500,
@@ -4837,6 +5232,116 @@ var tokenIconWrapStyle2 = {
4837
5232
  width: 36,
4838
5233
  height: 36
4839
5234
  };
5235
+ var tokenDropdownStyle = (t) => ({
5236
+ marginTop: 4,
5237
+ marginBottom: 12,
5238
+ background: t.bgCard,
5239
+ border: `1px solid ${t.border}`,
5240
+ borderRadius: t.radiusLg,
5241
+ boxShadow: t.shadowLg,
5242
+ padding: "12px 14px 14px"
5243
+ });
5244
+ var tokenDropdownLabelStyle = (color) => ({
5245
+ fontSize: "0.78rem",
5246
+ fontWeight: 500,
5247
+ color,
5248
+ marginBottom: 8
5249
+ });
5250
+ var tokenDropdownInnerStyle = (t) => ({
5251
+ background: t.bgInput,
5252
+ border: `1px solid ${t.border}`,
5253
+ borderRadius: t.radiusLg,
5254
+ overflow: "hidden"
5255
+ });
5256
+ var tokenDropdownRowStyle = (t, isSelected, isLast) => ({
5257
+ display: "flex",
5258
+ alignItems: "center",
5259
+ justifyContent: "space-between",
5260
+ width: "100%",
5261
+ padding: "14px 16px",
5262
+ background: isSelected ? `${t.accent}18` : "transparent",
5263
+ border: "none",
5264
+ borderBottom: isLast ? "none" : `1px solid ${t.border}`,
5265
+ cursor: "pointer",
5266
+ fontFamily: "inherit",
5267
+ textAlign: "left",
5268
+ outline: "none"
5269
+ });
5270
+ var tokenDropdownRowLeftStyle = {
5271
+ display: "flex",
5272
+ alignItems: "center",
5273
+ gap: 12,
5274
+ minWidth: 0,
5275
+ flex: 1
5276
+ };
5277
+ var tokenDropdownIconStyle = (t, hasLogo) => ({
5278
+ width: 36,
5279
+ height: 36,
5280
+ borderRadius: "50%",
5281
+ border: hasLogo ? "none" : `1.5px solid ${t.border}`,
5282
+ display: "flex",
5283
+ alignItems: "center",
5284
+ justifyContent: "center",
5285
+ flexShrink: 0,
5286
+ overflow: "hidden"
5287
+ });
5288
+ var tokenDropdownLogoStyle = {
5289
+ width: 36,
5290
+ height: 36,
5291
+ borderRadius: "50%",
5292
+ objectFit: "cover"
5293
+ };
5294
+ var tokenDropdownFallbackStyle = (color) => ({
5295
+ fontSize: "1rem",
5296
+ fontWeight: 700,
5297
+ color
5298
+ });
5299
+ var tokenDropdownInfoStyle = {
5300
+ display: "flex",
5301
+ flexDirection: "column",
5302
+ gap: 2,
5303
+ minWidth: 0
5304
+ };
5305
+ var tokenDropdownNameRowStyle = {
5306
+ display: "flex",
5307
+ alignItems: "center",
5308
+ gap: 4
5309
+ };
5310
+ var tokenDropdownSymbolStyle = (color) => ({
5311
+ fontSize: "0.92rem",
5312
+ fontWeight: 600,
5313
+ color
5314
+ });
5315
+ var tokenDropdownDotStyle = (color) => ({
5316
+ fontSize: "0.8rem",
5317
+ color
5318
+ });
5319
+ var tokenDropdownChainStyle = (color) => ({
5320
+ fontSize: "0.84rem",
5321
+ fontWeight: 400,
5322
+ color
5323
+ });
5324
+ var tokenDropdownBalanceRowStyle = {
5325
+ display: "flex",
5326
+ alignItems: "center",
5327
+ gap: 8
5328
+ };
5329
+ var tokenDropdownBalanceStyle = (color) => ({
5330
+ fontSize: "0.78rem",
5331
+ color
5332
+ });
5333
+ var tokenDropdownNotAuthStyle = (color) => ({
5334
+ fontSize: "0.7rem",
5335
+ fontWeight: 500,
5336
+ color
5337
+ });
5338
+ var tokenDropdownRadioStyle = (borderColor) => ({
5339
+ width: 22,
5340
+ height: 22,
5341
+ borderRadius: "50%",
5342
+ border: `2px solid ${borderColor}`,
5343
+ flexShrink: 0
5344
+ });
4840
5345
  var walletBalanceRowStyle = {
4841
5346
  display: "flex",
4842
5347
  alignItems: "center",
@@ -4998,9 +5503,20 @@ var limitExceededHintStyle = (color) => ({
4998
5503
  margin: "12px 0 2px",
4999
5504
  lineHeight: 1.5
5000
5505
  });
5506
+ var feeRowContainerStyle = {
5507
+ fontSize: "0.84rem",
5508
+ fontWeight: 500,
5509
+ marginTop: 6,
5510
+ marginBottom: 4
5511
+ };
5512
+ var feeRowLabelStyle = (color) => ({ color });
5513
+ var feeRowAmountStyle = (color) => ({
5514
+ color,
5515
+ fontVariantNumeric: "tabular-nums"
5516
+ });
5001
5517
  function SuccessScreen({
5002
5518
  amount,
5003
- currency,
5519
+ currency: _currency,
5004
5520
  succeeded,
5005
5521
  error,
5006
5522
  merchantName,
@@ -5013,22 +5529,30 @@ function SuccessScreen({
5013
5529
  onPreauthorize
5014
5530
  }) {
5015
5531
  const { tokens } = useBlinkConfig();
5532
+ const isGuestDepositSuccess = succeeded && onPreauthorize != null;
5016
5533
  return /* @__PURE__ */ jsxRuntime.jsxs(
5017
5534
  ScreenLayout,
5018
5535
  {
5019
5536
  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" })
5537
+ isGuestDepositSuccess ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5538
+ /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onPreauthorize, children: "Set up one tap" }),
5539
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onDone, style: skipButtonStyle(tokens.textMuted), children: "Return to app" })
5023
5540
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onDone, children: succeeded ? "Done" : "Try again" }),
5024
5541
  onManageAccount && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onManageAccount, style: manageStyle(tokens.textMuted), children: "Manage Blink account \u2192" }),
5025
5542
  /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
5026
5543
  ] }),
5027
5544
  children: [
5028
5545
  /* @__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 }) }) }),
5546
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: screenContentStyle, children: [
5547
+ isGuestDepositSuccess ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyleCompact, children: [
5548
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle7(tokens.text), children: [
5549
+ "$",
5550
+ amount.toFixed(2),
5551
+ " deposited"
5552
+ ] }),
5553
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { ...subtitleStyle7(tokens.text), fontWeight: 600, margin: "0 0 8px" }, children: "Next time, do it in one tap" }),
5554
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle7(tokens.textSecondary), children: "Set up one tap for this wallet and skip the extra steps." })
5555
+ ] }) : succeeded ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyleCompact, children: [
5032
5556
  /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle7(tokens.text), children: [
5033
5557
  "$",
5034
5558
  amount.toFixed(2),
@@ -5040,10 +5564,10 @@ function SuccessScreen({
5040
5564
  ] })
5041
5565
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5042
5566
  /* @__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 })
5567
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: failureHeadingStyle(tokens.text), children: "Transfer failed" }),
5568
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: failureSubtitleStyle(tokens.error), children: error })
5045
5569
  ] }),
5046
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryCardStyle(tokens), children: [
5570
+ !isGuestDepositSuccess && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryCardStyle(tokens), children: [
5047
5571
  sourceName && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
5048
5572
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryLabelStyle(tokens.textMuted), children: "From" }),
5049
5573
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryValueStyle(tokens.text), children: sourceName })
@@ -5060,7 +5584,7 @@ function SuccessScreen({
5060
5584
  ] })
5061
5585
  ] })
5062
5586
  ] }),
5063
- succeeded && onIncreaseLimits && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellCardStyle(tokens), children: [
5587
+ succeeded && onIncreaseLimits && !isGuestDepositSuccess && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellCardStyle(tokens), children: [
5064
5588
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellHeaderStyle, children: [
5065
5589
  /* @__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
5590
  /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Want higher limits?" })
@@ -5073,21 +5597,42 @@ function SuccessScreen({
5073
5597
  }
5074
5598
  );
5075
5599
  }
5076
- var contentStyle6 = {
5600
+ var screenContentStyle = {
5077
5601
  flex: 1,
5078
5602
  display: "flex",
5079
5603
  flexDirection: "column",
5080
5604
  alignItems: "center",
5081
5605
  paddingTop: 16
5082
5606
  };
5607
+ var contentStyleCompact = {
5608
+ textAlign: "center",
5609
+ display: "flex",
5610
+ flexDirection: "column",
5611
+ alignItems: "center",
5612
+ width: "100%"
5613
+ };
5083
5614
  var headingStyle7 = (color) => ({
5615
+ fontSize: "1.45rem",
5616
+ fontWeight: 700,
5617
+ letterSpacing: "-0.02em",
5618
+ color,
5619
+ margin: "20px 0 8px"
5620
+ });
5621
+ var subtitleStyle7 = (color) => ({
5622
+ fontSize: "0.9rem",
5623
+ color,
5624
+ margin: "0 0 28px",
5625
+ lineHeight: 1.5,
5626
+ maxWidth: 280
5627
+ });
5628
+ var failureHeadingStyle = (color) => ({
5084
5629
  fontSize: "1.5rem",
5085
5630
  fontWeight: 700,
5086
5631
  letterSpacing: "-0.02em",
5087
5632
  color,
5088
5633
  margin: "20px 0 4px"
5089
5634
  });
5090
- var subtitleStyle7 = (color) => ({
5635
+ var failureSubtitleStyle = (color) => ({
5091
5636
  fontSize: "0.9rem",
5092
5637
  color,
5093
5638
  margin: "0 0 20px"
@@ -5603,7 +6148,7 @@ function TransferStatusScreen({
5603
6148
  const steps = buildSteps(phase);
5604
6149
  return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { footer: /* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {}), children: [
5605
6150
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
5606
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
6151
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
5607
6152
  /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 64 }),
5608
6153
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle9(tokens.text), children: "Depositing your money..." }),
5609
6154
  error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle7(tokens), children: error }),
@@ -5611,7 +6156,7 @@ function TransferStatusScreen({
5611
6156
  ] })
5612
6157
  ] });
5613
6158
  }
5614
- var contentStyle7 = {
6159
+ var contentStyle6 = {
5615
6160
  flex: 1,
5616
6161
  display: "flex",
5617
6162
  flexDirection: "column",
@@ -5681,7 +6226,7 @@ function OpenWalletScreen({
5681
6226
  ] }),
5682
6227
  children: [
5683
6228
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
5684
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle8, children: [
6229
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
5685
6230
  /* @__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
6231
  /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle10(tokens.text), children: [
5687
6232
  "Setting up ",
@@ -5714,7 +6259,7 @@ function OpenWalletScreen({
5714
6259
  ] }),
5715
6260
  children: [
5716
6261
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onBack, onLogout }),
5717
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle8, children: [
6262
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle7, children: [
5718
6263
  /* @__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
6264
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle10(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
5720
6265
  /* @__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 +6272,7 @@ function OpenWalletScreen({
5727
6272
  }
5728
6273
  );
5729
6274
  }
5730
- var contentStyle8 = {
6275
+ var contentStyle7 = {
5731
6276
  flex: 1,
5732
6277
  display: "flex",
5733
6278
  flexDirection: "column",
@@ -5813,7 +6358,7 @@ function ConfirmSignScreen({
5813
6358
  ] }),
5814
6359
  children: [
5815
6360
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
5816
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle9, children: [
6361
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle8, children: [
5817
6362
  logoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoSrc, alt: displayName, style: logoStyle3 }) : /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
5818
6363
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle11(tokens.text), children: "Wallet authorized" }),
5819
6364
  /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle11(tokens.textSecondary), children: [
@@ -5829,7 +6374,7 @@ function ConfirmSignScreen({
5829
6374
  }
5830
6375
  );
5831
6376
  }
5832
- var contentStyle9 = {
6377
+ var contentStyle8 = {
5833
6378
  flex: 1,
5834
6379
  display: "flex",
5835
6380
  flexDirection: "column",
@@ -6124,29 +6669,6 @@ var selectCircleSelectedStyle = (color) => ({
6124
6669
  function entryKey(entry) {
6125
6670
  return `${entry.sourceChainId}-${entry.tokenAddress.toLowerCase()}`;
6126
6671
  }
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
6672
  function GuestTokenPickerScreen({
6151
6673
  entries,
6152
6674
  loading,
@@ -6186,13 +6708,13 @@ function GuestTokenPickerScreen({
6186
6708
  const canConfirm = Boolean(quoteFee && pendingEntry && !quoteLoading);
6187
6709
  const feeLine = (() => {
6188
6710
  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" }) });
6711
+ 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
6712
  }
6191
6713
  if (quoteFee) {
6192
6714
  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 })
6715
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: feeRowContainerStyle2, "aria-live": "polite", children: [
6716
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowLabelStyle2(t.textMuted), children: "Fee estimate: " }),
6717
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: feeRowAmountStyle2(t.textSecondary), children: feeText })
6196
6718
  ] });
6197
6719
  }
6198
6720
  return null;
@@ -6214,7 +6736,7 @@ function GuestTokenPickerScreen({
6214
6736
  depositAmount != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: depositAmountStyle2(t.text), children: [
6215
6737
  "$",
6216
6738
  depositAmount.toLocaleString("en-US", {
6217
- minimumFractionDigits: 0,
6739
+ minimumFractionDigits: 2,
6218
6740
  maximumFractionDigits: 2
6219
6741
  })
6220
6742
  ] }),
@@ -6296,7 +6818,7 @@ function GuestTokenPickerScreen({
6296
6818
  ] }),
6297
6819
  tokenListOpen && entries.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tokenDropdownOuterStyle(t), children: [
6298
6820
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: accountDropdownLabelStyle2(t.textMuted), children: "Choose token" }),
6299
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownInnerStyle(t), children: entries.map((entry, index) => {
6821
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tokenDropdownInnerStyle2(t), children: entries.map((entry, index) => {
6300
6822
  const selected = pendingKey === entryKey(entry);
6301
6823
  const isLast = index === entries.length - 1;
6302
6824
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -6397,13 +6919,13 @@ var depositAmountStyle2 = (color) => ({
6397
6919
  color,
6398
6920
  lineHeight: 1.05
6399
6921
  });
6400
- var feeRowContainerStyle = {
6922
+ var feeRowContainerStyle2 = {
6401
6923
  fontSize: "0.84rem",
6402
6924
  fontWeight: 500,
6403
6925
  marginTop: 6
6404
6926
  };
6405
- var feeRowLabelStyle = (color) => ({ color });
6406
- var feeRowAmountStyle = (color) => ({
6927
+ var feeRowLabelStyle2 = (color) => ({ color });
6928
+ var feeRowAmountStyle2 = (color) => ({
6407
6929
  color,
6408
6930
  fontVariantNumeric: "tabular-nums"
6409
6931
  });
@@ -6449,7 +6971,7 @@ var accountDropdownLabelStyle2 = (color) => ({
6449
6971
  color,
6450
6972
  marginBottom: 8
6451
6973
  });
6452
- var tokenDropdownInnerStyle = (tokens) => ({
6974
+ var tokenDropdownInnerStyle2 = (tokens) => ({
6453
6975
  background: tokens.bgInput,
6454
6976
  border: `1px solid ${tokens.border}`,
6455
6977
  borderRadius: tokens.radiusLg,
@@ -6561,7 +7083,7 @@ function GuestPreauthSetupCompleteScreen({
6561
7083
  ] }),
6562
7084
  children: [
6563
7085
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
6564
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle10, children: [
7086
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle9, children: [
6565
7087
  /* @__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
7088
  "path",
6567
7089
  {
@@ -6570,13 +7092,13 @@ function GuestPreauthSetupCompleteScreen({
6570
7092
  }
6571
7093
  ) }) }),
6572
7094
  /* @__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." })
7095
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle12(tokens.textSecondary), children: "Your account is linked and ready. You can close this window." })
6574
7096
  ] })
6575
7097
  ]
6576
7098
  }
6577
7099
  );
6578
7100
  }
6579
- var contentStyle10 = {
7101
+ var contentStyle9 = {
6580
7102
  display: "flex",
6581
7103
  flexDirection: "column",
6582
7104
  alignItems: "center",
@@ -6605,14 +7127,14 @@ function GuestPreauthLinkingScreen({ onLogout }) {
6605
7127
  const { tokens } = useBlinkConfig();
6606
7128
  return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
6607
7129
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onLogout }),
6608
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle11, children: [
7130
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle10, children: [
6609
7131
  /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
6610
7132
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle13(tokens.text), children: "Setting up your account..." }),
6611
7133
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle13(tokens.textSecondary), children: "Linking your wallet to your Blink account. This usually takes a few seconds." })
6612
7134
  ] })
6613
7135
  ] });
6614
7136
  }
6615
- var contentStyle11 = {
7137
+ var contentStyle10 = {
6616
7138
  flex: 1,
6617
7139
  display: "flex",
6618
7140
  flexDirection: "column",
@@ -6852,15 +7374,11 @@ function StepRendererContent({
6852
7374
  0
6853
7375
  );
6854
7376
  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(
7377
+ const setupFromPendingSelectSource = pendingSelectSource != null;
7378
+ const setupSelectedTokenSymbol = setupFromPendingSelectSource ? selectSourceTokenSymbol || selectSourceRecommended?.tokenSymbol || "" : selectedSource?.token.symbol ?? selectSourceTokenSymbol;
7379
+ const setupSelectedChainName = setupFromPendingSelectSource ? selectSourceChainName || selectSourceRecommended?.chainName || "" : selectedWallet?.chain.name ?? selectSourceChainName;
7380
+ const effectiveSourceLabel = setupFromPendingSelectSource ? setupSelectedTokenSymbol && setupSelectedChainName ? `${setupSelectedTokenSymbol} on ${setupSelectedChainName}` : void 0 : selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
7381
+ const setupTokenOptions = selectedAccount ? tokenOptionsForLinkedAccount(selectedAccount, state.chains) : selectSourceChoices.flatMap(
6864
7382
  (chain) => chain.tokens.map((t) => ({
6865
7383
  symbol: t.tokenSymbol,
6866
7384
  chainName: chain.chainName,
@@ -6868,9 +7386,8 @@ function StepRendererContent({
6868
7386
  }))
6869
7387
  );
6870
7388
  const handleSetupSelectToken = (symbol, chainName, walletId) => {
6871
- if (walletId) {
6872
- handlers.onSelectAuthorizedToken(walletId, symbol);
6873
- } else {
7389
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7390
+ if (pendingSelectSource) {
6874
7391
  handlers.onSelectSourceChainChange(chainName);
6875
7392
  handlers.onSetSelectSourceTokenSymbol(symbol);
6876
7393
  }
@@ -6878,17 +7395,18 @@ function StepRendererContent({
6878
7395
  return /* @__PURE__ */ jsxRuntime.jsx(
6879
7396
  SetupScreen,
6880
7397
  {
6881
- availableBalance: selectedSource ? selectedSource.balance.available.amount : selectSourceAvailableBalance > 0 ? selectSourceAvailableBalance : selectedAccount ? selectedAccount.wallets.reduce((sum, w) => sum + w.balance.available.amount, 0) : maxSourceBalance,
7398
+ 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
7399
  tokenCount: effectiveTokenCount,
6883
7400
  sourceName,
6884
7401
  onSetupOneTap: handlers.onSetupOneTap,
6885
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7402
+ onBack: handlers.onBackFromSubflow,
6886
7403
  onLogout: handlers.onLogout,
6887
7404
  onAdvanced: handlers.onSelectToken,
6888
7405
  selectedSourceLabel: effectiveSourceLabel,
6889
7406
  loading: savingOneTapLimit,
6890
7407
  error: state.error,
6891
- selectedTokenSymbol: selectedSource?.token.symbol ?? selectSourceTokenSymbol,
7408
+ selectedTokenSymbol: setupSelectedTokenSymbol,
7409
+ selectedChainName: setupSelectedChainName,
6892
7410
  tokenOptions: setupTokenOptions,
6893
7411
  onSelectToken: handleSetupSelectToken
6894
7412
  }
@@ -6910,6 +7428,7 @@ function StepRendererContent({
6910
7428
  case "deposit": {
6911
7429
  const parsedAmt = depositAmount != null ? depositAmount : 5;
6912
7430
  const minDepositFloor = depositAmount != null ? depositAmount : DEFAULT_MIN_DEPOSIT_USD;
7431
+ const depositTokenOptions = tokenOptionsForLinkedAccount(selectedAccount, state.chains);
6913
7432
  return /* @__PURE__ */ jsxRuntime.jsx(
6914
7433
  DepositScreen,
6915
7434
  {
@@ -6918,6 +7437,8 @@ function StepRendererContent({
6918
7437
  remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
6919
7438
  tokenCount,
6920
7439
  initialAmount: parsedAmt,
7440
+ quoteFee: forms.depositQuoteFee,
7441
+ quoteLoading: forms.depositQuoteLoading,
6921
7442
  processing: state.creatingTransfer,
6922
7443
  error: state.error,
6923
7444
  onDeposit: handlers.onPay,
@@ -6932,8 +7453,13 @@ function StepRendererContent({
6932
7453
  onAuthorizeAccount: handlers.onContinueConnection,
6933
7454
  onAddProvider: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
6934
7455
  onSelectToken: handlers.onSelectToken,
7456
+ tokenOptions: depositTokenOptions,
7457
+ onPickToken: (symbol, chainName, walletId) => {
7458
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7459
+ },
6935
7460
  selectedSourceLabel,
6936
7461
  selectedTokenSymbol: selectedSource?.token.symbol,
7462
+ selectedChainName: selectedWallet?.chain.name,
6937
7463
  minDepositFloor
6938
7464
  }
6939
7465
  );
@@ -6974,7 +7500,7 @@ function StepRendererContent({
6974
7500
  chains: state.chains,
6975
7501
  onSelectAuthorized: handlers.onSelectAuthorizedToken,
6976
7502
  onAuthorizeToken: handlers.onAuthorizeToken,
6977
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7503
+ onBack: handlers.onBackFromSubflow,
6978
7504
  onLogout: handlers.onLogout,
6979
7505
  depositAmount: depositAmount ?? void 0,
6980
7506
  selectedTokenSymbol: selectedSource?.token.symbol,
@@ -7053,7 +7579,7 @@ function StepRendererContent({
7053
7579
  })() : void 0,
7054
7580
  onDone: onDismiss ?? handlers.onNewPayment,
7055
7581
  onLogout: authenticated ? handlers.onLogout : void 0,
7056
- onPreauthorize: state.isGuestFlow ? handlers.onPreauthorize : void 0
7582
+ onPreauthorize: state.isGuestFlow && isDesktop ? handlers.onPreauthorize : void 0
7057
7583
  }
7058
7584
  );
7059
7585
  }
@@ -7452,6 +7978,9 @@ function useTransferHandlers(deps) {
7452
7978
  sourceTokenAddress,
7453
7979
  activeCredentialId,
7454
7980
  selectedAccountId,
7981
+ selectedWalletId,
7982
+ selectedTokenSymbol,
7983
+ quoteId,
7455
7984
  transfer,
7456
7985
  accounts
7457
7986
  } = deps;
@@ -7465,9 +7994,32 @@ function useTransferHandlers(deps) {
7465
7994
  fetchProviders(apiBaseUrl, token)
7466
7995
  ]);
7467
7996
  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]);
7997
+ const { defaults, resetSelectedTokenSymbol } = resolveDepositSelectionAfterRefresh(
7998
+ accts,
7999
+ parsedAmt,
8000
+ {
8001
+ selectedAccountId,
8002
+ selectedWalletId,
8003
+ selectedTokenSymbol
8004
+ }
8005
+ );
8006
+ dispatch({
8007
+ type: "ACCOUNTS_RELOADED",
8008
+ accounts: accts,
8009
+ providers: prov,
8010
+ defaults,
8011
+ resetSelectedTokenSymbol
8012
+ });
8013
+ }, [
8014
+ getAccessToken,
8015
+ activeCredentialId,
8016
+ selectedAccountId,
8017
+ selectedWalletId,
8018
+ selectedTokenSymbol,
8019
+ apiBaseUrl,
8020
+ depositAmount,
8021
+ dispatch
8022
+ ]);
7471
8023
  const handlePay = react.useCallback(async (payAmount, sourceOverrides) => {
7472
8024
  const minUsd = effectiveMinTransferAmountUsd(depositAmount);
7473
8025
  if (isNaN(payAmount) || payAmount < minUsd) {
@@ -7511,7 +8063,8 @@ function useTransferHandlers(deps) {
7511
8063
  sourceId: effectiveSourceId,
7512
8064
  sourceTokenAddress,
7513
8065
  destination,
7514
- amount: payAmount
8066
+ amount: payAmount,
8067
+ ...quoteId ? { quoteId } : {}
7515
8068
  });
7516
8069
  dispatch({ type: "TRANSFER_CREATED", transfer: t });
7517
8070
  if (t.status === "COMPLETED") {
@@ -7543,6 +8096,7 @@ function useTransferHandlers(deps) {
7543
8096
  accounts,
7544
8097
  destination,
7545
8098
  apiBaseUrl,
8099
+ quoteId,
7546
8100
  getAccessToken,
7547
8101
  transferSigning,
7548
8102
  polling,
@@ -7593,20 +8147,20 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7593
8147
  const selectSourceAvailableBalance = react.useMemo(() => {
7594
8148
  if (!pendingSelectSourceAction) return 0;
7595
8149
  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]);
8150
+ return resolveSelectSourceAvailableBalance(
8151
+ selectSourceChoices,
8152
+ options2,
8153
+ selectSourceChainName,
8154
+ selectSourceTokenSymbol,
8155
+ selectSourceRecommended
8156
+ );
8157
+ }, [
8158
+ pendingSelectSourceAction,
8159
+ selectSourceChoices,
8160
+ selectSourceChainName,
8161
+ selectSourceTokenSymbol,
8162
+ selectSourceRecommended
8163
+ ]);
7610
8164
  const handleSelectSourceChainChange = react.useCallback(
7611
8165
  (chainName) => {
7612
8166
  setSelectSourceChainName(chainName);
@@ -7657,7 +8211,7 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7657
8211
  initializedSelectSourceActionRef
7658
8212
  };
7659
8213
  }
7660
- function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete) {
8214
+ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete, lastResumedAt) {
7661
8215
  const {
7662
8216
  mobileSetupFlowRef,
7663
8217
  handlingMobileReturnRef,
@@ -7668,6 +8222,8 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7668
8222
  onCompleteRef.current = onComplete;
7669
8223
  const guestPollingActiveRef = react.useRef(false);
7670
8224
  const guestPollingCleanupRef = react.useRef(null);
8225
+ const mobileFlowRef = react.useRef(guestCheckout.mobileFlow);
8226
+ mobileFlowRef.current = guestCheckout.mobileFlow;
7671
8227
  const guestTransferIdRef = react.useRef(guestCheckout.guestTransferId);
7672
8228
  guestTransferIdRef.current = guestCheckout.guestTransferId;
7673
8229
  const guestSessionTokenRef = react.useRef(guestCheckout.guestSessionToken);
@@ -7723,27 +8279,15 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7723
8279
  startGuestPolling
7724
8280
  ]);
7725
8281
  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]);
8282
+ if (!lastResumedAt) return;
8283
+ if (guestPollingActiveRef.current) return;
8284
+ if (!mobileFlowRef.current) return;
8285
+ const transferId = guestTransferIdRef.current;
8286
+ const token = guestSessionTokenRef.current;
8287
+ if (transferId && token) {
8288
+ startGuestPolling(transferId, token);
8289
+ }
8290
+ }, [lastResumedAt, startGuestPolling]);
7747
8291
  const handleAuthorizedMobileReturn = react.useCallback(async (authorizedTransfer, isSetup) => {
7748
8292
  if (handlingMobileReturnRef.current) return;
7749
8293
  handlingMobileReturnRef.current = true;
@@ -7977,6 +8521,9 @@ function useProviderHandlers(deps) {
7977
8521
  await reloadAccounts();
7978
8522
  }
7979
8523
  } catch (err) {
8524
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8525
+ return;
8526
+ }
7980
8527
  captureException(err);
7981
8528
  const msg = err instanceof Error ? err.message : "Failed to set up wallet";
7982
8529
  dispatch({ type: "PAY_ERROR", error: msg });
@@ -8043,7 +8590,12 @@ function useProviderHandlers(deps) {
8043
8590
  }
8044
8591
  const acct = accounts.find((a) => a.id === selectedAccountId);
8045
8592
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8046
- if (matchedProvider) {
8593
+ const isMobile = !shouldUseWalletConnector({
8594
+ useWalletConnector: useWalletConnectorProp,
8595
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8596
+ });
8597
+ if (matchedProvider && isMobile) {
8598
+ dispatch({ type: "SAVE_SELECTION" });
8047
8599
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8048
8600
  }
8049
8601
  dispatch({ type: "SET_ERROR", error: null });
@@ -8063,10 +8615,6 @@ function useProviderHandlers(deps) {
8063
8615
  activeCredentialId,
8064
8616
  { tokenAddress: source?.address, chainId: evmChainId }
8065
8617
  );
8066
- const isMobile = !shouldUseWalletConnector({
8067
- useWalletConnector: useWalletConnectorProp,
8068
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8069
- });
8070
8618
  if (isMobile) {
8071
8619
  handlingMobileReturnRef.current = false;
8072
8620
  mobileSetupFlowRef.current = true;
@@ -8083,12 +8631,20 @@ function useProviderHandlers(deps) {
8083
8631
  });
8084
8632
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8085
8633
  triggerDeeplink(session.uri);
8634
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8086
8635
  } else {
8087
8636
  await authExecutor.executeSessionById(session.id);
8088
8637
  await reloadAccounts();
8638
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8089
8639
  }
8090
8640
  } catch (err) {
8641
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8642
+ return;
8643
+ }
8091
8644
  captureException(err);
8645
+ if (isMobile) {
8646
+ dispatch({ type: "RESTORE_SELECTION" });
8647
+ }
8092
8648
  const msg = err instanceof Error ? err.message : "Failed to increase limit";
8093
8649
  dispatch({ type: "SET_ERROR", error: msg });
8094
8650
  onError?.(msg);
@@ -8133,7 +8689,12 @@ function useProviderHandlers(deps) {
8133
8689
  }
8134
8690
  const acct = accounts.find((a) => a.id === selectedAccountId);
8135
8691
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8136
- if (matchedProvider) {
8692
+ const isMobile = !shouldUseWalletConnector({
8693
+ useWalletConnector: useWalletConnectorProp,
8694
+ userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8695
+ });
8696
+ if (matchedProvider && isMobile) {
8697
+ dispatch({ type: "SAVE_SELECTION" });
8137
8698
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8138
8699
  }
8139
8700
  dispatch({ type: "SET_ERROR", error: null });
@@ -8148,10 +8709,6 @@ function useProviderHandlers(deps) {
8148
8709
  activeCredentialId,
8149
8710
  { tokenAddress, chainId }
8150
8711
  );
8151
- const isMobile = !shouldUseWalletConnector({
8152
- useWalletConnector: useWalletConnectorProp,
8153
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8154
- });
8155
8712
  if (isMobile) {
8156
8713
  handlingMobileReturnRef.current = false;
8157
8714
  mobileSetupFlowRef.current = true;
@@ -8169,13 +8726,21 @@ function useProviderHandlers(deps) {
8169
8726
  });
8170
8727
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8171
8728
  triggerDeeplink(session.uri);
8729
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8172
8730
  } else {
8173
8731
  await authExecutor.executeSessionById(session.id);
8174
8732
  await reloadAccounts();
8175
8733
  dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
8734
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8176
8735
  }
8177
8736
  } catch (err) {
8737
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8738
+ return;
8739
+ }
8178
8740
  captureException(err);
8741
+ if (isMobile) {
8742
+ dispatch({ type: "RESTORE_SELECTION" });
8743
+ }
8179
8744
  const msg = err instanceof Error ? err.message : "Failed to authorize token";
8180
8745
  dispatch({ type: "SET_ERROR", error: msg });
8181
8746
  onError?.(msg);
@@ -9112,7 +9677,15 @@ function useDataLoadEffect(deps) {
9112
9677
  ]);
9113
9678
  if (cancelled) return;
9114
9679
  const parsedAmt = depositAmount != null ? depositAmount : 0;
9115
- const defaults = resolveDepositSelection(accts, parsedAmt, state.selectedAccountId);
9680
+ const { defaults, resetSelectedTokenSymbol } = resolveDepositSelectionAfterRefresh(
9681
+ accts,
9682
+ parsedAmt,
9683
+ {
9684
+ selectedAccountId: state.selectedAccountId,
9685
+ selectedWalletId: state.selectedWalletId,
9686
+ selectedTokenSymbol: state.selectedTokenSymbol
9687
+ }
9688
+ );
9116
9689
  const persisted = loadMobileFlowState();
9117
9690
  const clearMobile = persisted?.isSetup && accts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
9118
9691
  dispatch({
@@ -9121,7 +9694,8 @@ function useDataLoadEffect(deps) {
9121
9694
  accounts: accts,
9122
9695
  chains: chn,
9123
9696
  defaults,
9124
- clearMobileState: clearMobile
9697
+ clearMobileState: clearMobile,
9698
+ resetSelectedTokenSymbol
9125
9699
  });
9126
9700
  if (clearMobile) clearMobileFlowState();
9127
9701
  } catch (err) {
@@ -9150,6 +9724,8 @@ function useDataLoadEffect(deps) {
9150
9724
  apiBaseUrl,
9151
9725
  state.activeCredentialId,
9152
9726
  state.selectedAccountId,
9727
+ state.selectedWalletId,
9728
+ state.selectedTokenSymbol,
9153
9729
  depositAmount
9154
9730
  ]);
9155
9731
  react.useEffect(() => {
@@ -9378,25 +9954,16 @@ function useMobilePollingEffect(deps) {
9378
9954
  const poll = isReauth ? pollReauthorization : pollWalletActive;
9379
9955
  poll();
9380
9956
  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
9957
  return () => {
9390
9958
  cancelled = true;
9391
9959
  window.clearInterval(intervalId);
9392
- document.removeEventListener("visibilitychange", handleVisibility);
9393
- window.removeEventListener("pageshow", handlePageShow);
9394
9960
  };
9395
9961
  }, [
9396
9962
  state.mobileFlow,
9397
9963
  state.isGuestFlow,
9398
9964
  state.guestPreauthSessionId,
9399
9965
  state.activeCredentialId,
9966
+ state.lastResumedAt,
9400
9967
  apiBaseUrl,
9401
9968
  reloadAccounts,
9402
9969
  dispatch,
@@ -9413,28 +9980,13 @@ function useMobilePollingEffect(deps) {
9413
9980
  const transferIdToResume = pollingTransferIdRef.current ?? state.transfer?.id;
9414
9981
  if (!transferIdToResume) return;
9415
9982
  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
9983
  }, [
9433
9984
  state.mobileFlow,
9434
9985
  state.transfer?.id,
9435
9986
  state.isGuestFlow,
9436
9987
  state.guestPreauthSessionId,
9437
9988
  state.guestPreauthAccountId,
9989
+ state.lastResumedAt,
9438
9990
  polling.isPolling,
9439
9991
  polling.startPolling,
9440
9992
  handlingMobileReturnRef,
@@ -9445,6 +9997,7 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9445
9997
  const {
9446
9998
  dispatch,
9447
9999
  privyReady,
10000
+ lastResumedAt,
9448
10001
  mobileSetupFlowRef,
9449
10002
  setupAccountIdRef,
9450
10003
  startGuestAccountPolling
@@ -9455,68 +10008,43 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9455
10008
  console.info("[blink-sdk] guestPreauthMobileRestore: skipping, privyReady=false");
9456
10009
  return;
9457
10010
  }
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
- }
10011
+ if (startedRef.current) {
10012
+ console.info("[blink-sdk] guestPreauthMobileRestore: already started");
10013
+ return;
10014
+ }
10015
+ const persisted = loadMobileFlowState();
10016
+ console.info("[blink-sdk] guestPreauthMobileRestore.tryStart", {
10017
+ trigger: lastResumedAt ? "page-resume" : "initial",
10018
+ hasPersistedState: !!persisted,
10019
+ isGuestPreauth: persisted?.isGuestPreauth ?? false,
10020
+ hasGuestSessionToken: !!persisted?.guestSessionToken,
10021
+ hasSessionId: !!persisted?.sessionId,
10022
+ accountId: persisted?.accountId ?? null
10023
+ });
10024
+ if (!persisted?.isGuestPreauth || !persisted.guestSessionToken || !persisted.sessionId) {
10025
+ return;
10026
+ }
10027
+ startedRef.current = true;
10028
+ mobileSetupFlowRef.current = true;
10029
+ if (persisted.accountId) {
10030
+ setupAccountIdRef.current = persisted.accountId;
10031
+ }
10032
+ if (persisted.accountId) {
9487
10033
  dispatch({
9488
- type: "MOBILE_DEEPLINK_READY",
9489
- deeplinkUri: persisted.deeplinkUri
9490
- });
9491
- console.info("[blink-sdk] guestPreauthMobileRestore: starting guest account polling", {
9492
- trigger,
10034
+ type: "GUEST_PREAUTH_DETECTED",
10035
+ accountId: persisted.accountId,
9493
10036
  sessionId: persisted.sessionId
9494
10037
  });
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]);
10038
+ }
10039
+ dispatch({
10040
+ type: "MOBILE_DEEPLINK_READY",
10041
+ deeplinkUri: persisted.deeplinkUri
10042
+ });
10043
+ console.info("[blink-sdk] guestPreauthMobileRestore: starting guest account polling", {
10044
+ sessionId: persisted.sessionId
10045
+ });
10046
+ startGuestAccountPolling(persisted.guestSessionToken, persisted.sessionId);
10047
+ }, [privyReady, lastResumedAt, dispatch, mobileSetupFlowRef, setupAccountIdRef, startGuestAccountPolling]);
9520
10048
  }
9521
10049
  function useSelectSourceEffect(deps) {
9522
10050
  const {
@@ -9820,7 +10348,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9820
10348
  react.useEffect(() => {
9821
10349
  if (!isDesktop || state.guestPreauthorizing) return;
9822
10350
  if (!state.privyAuthenticated || !state.activeCredentialId || !state.selectedAccountId) return;
9823
- const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && !authExecutor.pendingOneTapSetup;
10351
+ const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && (!authExecutor.pendingOneTapSetup || authExecutor.authorizationSessionStackDepth > 1);
9824
10352
  if (shouldPin && !state.standardDesktopInlineOpenWallet) {
9825
10353
  dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: true });
9826
10354
  return;
@@ -9838,6 +10366,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9838
10366
  authExecutor.executing,
9839
10367
  authExecutor.pendingSelectSource,
9840
10368
  authExecutor.pendingOneTapSetup,
10369
+ authExecutor.authorizationSessionStackDepth,
9841
10370
  dispatch
9842
10371
  ]);
9843
10372
  }
@@ -9856,7 +10385,84 @@ function useGuestAccountAutoPollingEffect(deps) {
9856
10385
  startPolling(guestSessionToken);
9857
10386
  }, [mobileFlow, isGuestFlow, guestSessionToken, isPolling, guestAccount, startPolling]);
9858
10387
  }
9859
- function useGuestAccountPolling(intervalMs = 3e3) {
10388
+ var WATCHDOG_INTERVAL_MS = 1e3;
10389
+ var WATCHDOG_THRESHOLD_MS = 3e3;
10390
+ var DEDUP_COOLDOWN_MS = 500;
10391
+ function usePageResume(callback) {
10392
+ const callbackRef = react.useRef(callback);
10393
+ callbackRef.current = callback;
10394
+ react.useEffect(() => {
10395
+ let lastTickAt = Date.now();
10396
+ let lastFiredAt = 0;
10397
+ const fire = (source, frozenDurationMs) => {
10398
+ const now = Date.now();
10399
+ if (now - lastFiredAt < DEDUP_COOLDOWN_MS) return;
10400
+ lastFiredAt = now;
10401
+ callbackRef.current({ frozenDurationMs, source });
10402
+ };
10403
+ const watchdogId = window.setInterval(() => {
10404
+ const now = Date.now();
10405
+ const elapsed = now - lastTickAt;
10406
+ lastTickAt = now;
10407
+ if (elapsed > WATCHDOG_THRESHOLD_MS) {
10408
+ fire("watchdog", elapsed);
10409
+ }
10410
+ }, WATCHDOG_INTERVAL_MS);
10411
+ const handleVisibility = () => {
10412
+ const now = Date.now();
10413
+ if (document.visibilityState === "visible") {
10414
+ fire("visibilitychange", now - lastTickAt);
10415
+ }
10416
+ lastTickAt = now;
10417
+ };
10418
+ const handlePageShow = (_e) => {
10419
+ const now = Date.now();
10420
+ if (document.visibilityState === "visible") {
10421
+ fire("pageshow", now - lastTickAt);
10422
+ }
10423
+ lastTickAt = now;
10424
+ };
10425
+ const handleFocus = () => {
10426
+ const now = Date.now();
10427
+ const elapsed = now - lastTickAt;
10428
+ lastTickAt = now;
10429
+ if (elapsed > WATCHDOG_THRESHOLD_MS) {
10430
+ fire("focus", elapsed);
10431
+ }
10432
+ };
10433
+ document.addEventListener("visibilitychange", handleVisibility);
10434
+ window.addEventListener("pageshow", handlePageShow);
10435
+ window.addEventListener("focus", handleFocus);
10436
+ return () => {
10437
+ window.clearInterval(watchdogId);
10438
+ document.removeEventListener("visibilitychange", handleVisibility);
10439
+ window.removeEventListener("pageshow", handlePageShow);
10440
+ window.removeEventListener("focus", handleFocus);
10441
+ };
10442
+ }, []);
10443
+ }
10444
+ var STUCK_REF_THRESHOLD_MS = 5e3;
10445
+ function usePageResumeEffect(deps) {
10446
+ const { dispatch, handlingMobileReturnRef } = deps;
10447
+ const dispatchRef = react.useRef(dispatch);
10448
+ dispatchRef.current = dispatch;
10449
+ usePageResume((info) => {
10450
+ console.info("[blink-sdk] PAGE_RESUMED", {
10451
+ source: info.source,
10452
+ frozenDurationMs: info.frozenDurationMs
10453
+ });
10454
+ dispatchRef.current({ type: "PAGE_RESUMED", frozenDurationMs: info.frozenDurationMs });
10455
+ if (info.frozenDurationMs > STUCK_REF_THRESHOLD_MS) {
10456
+ if (handlingMobileReturnRef.current) {
10457
+ console.info("[blink-sdk] PAGE_RESUMED: clearing stuck handlingMobileReturnRef", {
10458
+ frozenDurationMs: info.frozenDurationMs
10459
+ });
10460
+ handlingMobileReturnRef.current = false;
10461
+ }
10462
+ }
10463
+ });
10464
+ }
10465
+ function useGuestAccountPolling(intervalMs = 3e3, lastResumedAt = 0) {
9860
10466
  const { apiBaseUrl } = useBlinkConfig();
9861
10467
  const [guestAccount, setGuestAccount] = react.useState(null);
9862
10468
  const [isPolling, setIsPolling] = react.useState(false);
@@ -9954,36 +10560,71 @@ function useGuestAccountPolling(intervalMs = 3e3) {
9954
10560
  [poll, intervalMs, stopPolling]
9955
10561
  );
9956
10562
  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
10563
+ if (!lastResumedAt || !guestTokenRef.current) return;
10564
+ console.info("[blink-sdk] useGuestAccountPolling: page resumed, triggering poll", {
10565
+ lastResumedAt,
10566
+ hasToken: !!guestTokenRef.current,
10567
+ hasSessionId: !!sessionIdRef.current
10568
+ });
10569
+ void poll();
10570
+ }, [lastResumedAt, poll]);
10571
+ react.useEffect(() => () => stopPolling(), [stopPolling]);
10572
+ return { guestAccount, error, isPolling, startPolling, stopPolling };
10573
+ }
10574
+ function useDepositFeeEstimate({
10575
+ apiBaseUrl,
10576
+ getAccessToken,
10577
+ walletId,
10578
+ sourceTokenAddress,
10579
+ destination,
10580
+ amount,
10581
+ enabled
10582
+ }) {
10583
+ const [quoteId, setQuoteId] = react.useState(null);
10584
+ const [quoteFee, setQuoteFee] = react.useState(null);
10585
+ const [quoteLoading, setQuoteLoading] = react.useState(false);
10586
+ const abortRef = react.useRef(null);
10587
+ const fetchQuote = react.useCallback(async () => {
10588
+ if (!enabled || !walletId || !sourceTokenAddress) {
10589
+ setQuoteId(null);
10590
+ setQuoteFee(null);
10591
+ setQuoteLoading(false);
10592
+ return;
10593
+ }
10594
+ abortRef.current?.abort();
10595
+ const controller = new AbortController();
10596
+ abortRef.current = controller;
10597
+ setQuoteLoading(true);
10598
+ try {
10599
+ const token = await getAccessToken();
10600
+ if (!token || controller.signal.aborted) return;
10601
+ const quote = await postTransferQuote(apiBaseUrl, token, {
10602
+ walletId,
10603
+ sourceTokenAddress,
10604
+ destination,
10605
+ amount: { amount, currency: "USD" }
9973
10606
  });
9974
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9975
- void poll();
10607
+ if (controller.signal.aborted) return;
10608
+ setQuoteId(quote.id);
10609
+ setQuoteFee(quote.fee);
10610
+ } catch (err) {
10611
+ if (controller.signal.aborted) return;
10612
+ console.warn("[blink-sdk] Fee quote failed:", err);
10613
+ setQuoteId(null);
10614
+ setQuoteFee(null);
10615
+ } finally {
10616
+ if (!controller.signal.aborted) {
10617
+ setQuoteLoading(false);
9976
10618
  }
9977
- };
9978
- document.addEventListener("visibilitychange", handleVisibility);
9979
- window.addEventListener("pageshow", handlePageShow);
10619
+ }
10620
+ }, [enabled, walletId, sourceTokenAddress, destination, amount, apiBaseUrl, getAccessToken]);
10621
+ react.useEffect(() => {
10622
+ fetchQuote();
9980
10623
  return () => {
9981
- document.removeEventListener("visibilitychange", handleVisibility);
9982
- window.removeEventListener("pageshow", handlePageShow);
10624
+ abortRef.current?.abort();
9983
10625
  };
9984
- }, [poll]);
9985
- react.useEffect(() => () => stopPolling(), [stopPolling]);
9986
- return { guestAccount, error, isPolling, startPolling, stopPolling };
10626
+ }, [fetchQuote]);
10627
+ return { quoteId, quoteFee, quoteLoading };
9987
10628
  }
9988
10629
  function BlinkPayment(props) {
9989
10630
  const resetKey = react.useRef(0);
@@ -10023,7 +10664,7 @@ function BlinkPaymentInner({
10023
10664
  );
10024
10665
  const authExecutor = useAuthorizationExecutor();
10025
10666
  const polling = useTransferPolling();
10026
- const guestAccountPolling = useGuestAccountPolling();
10667
+ const guestAccountPolling = useGuestAccountPolling(3e3, state.lastResumedAt);
10027
10668
  const transferSigning = useTransferSigning();
10028
10669
  const mobileFlowRefs = {
10029
10670
  mobileSetupFlowRef: react.useRef(false),
@@ -10041,6 +10682,15 @@ function BlinkPaymentInner({
10041
10682
  state.accounts,
10042
10683
  state.knownCredentialIds
10043
10684
  );
10685
+ const depositFee = useDepositFeeEstimate({
10686
+ apiBaseUrl,
10687
+ getAccessToken,
10688
+ walletId: state.selectedWalletId,
10689
+ sourceTokenAddress: derived.selectedSource?.address,
10690
+ destination,
10691
+ amount: depositAmount ?? 5,
10692
+ enabled: state.phase.step === "deposit" && !state.isGuestFlow && authenticated
10693
+ });
10044
10694
  const transfer = useTransferHandlers({
10045
10695
  dispatch,
10046
10696
  getAccessToken,
@@ -10058,6 +10708,9 @@ function BlinkPaymentInner({
10058
10708
  sourceTokenAddress: derived.selectedSource?.address,
10059
10709
  activeCredentialId: state.activeCredentialId,
10060
10710
  selectedAccountId: state.selectedAccountId,
10711
+ selectedWalletId: state.selectedWalletId,
10712
+ selectedTokenSymbol: state.selectedTokenSymbol,
10713
+ quoteId: depositFee.quoteId,
10061
10714
  transfer: state.transfer,
10062
10715
  accounts: state.accounts
10063
10716
  });
@@ -10074,7 +10727,8 @@ function BlinkPaymentInner({
10074
10727
  guestTransferId: state.guestTransferId,
10075
10728
  guestSessionToken: state.guestSessionToken
10076
10729
  },
10077
- onComplete
10730
+ onComplete,
10731
+ state.lastResumedAt
10078
10732
  );
10079
10733
  const sourceSelection = useSourceSelectionHandlers(dispatch, authExecutor, {
10080
10734
  guestPreauthSessionId: state.guestPreauthSessionId,
@@ -10170,10 +10824,15 @@ function BlinkPaymentInner({
10170
10824
  dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
10171
10825
  }
10172
10826
  }, [depositAmount, dispatch]);
10827
+ usePageResumeEffect({
10828
+ dispatch,
10829
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef
10830
+ });
10173
10831
  usePrivySessionSyncEffect({ dispatch, ready, authenticated });
10174
10832
  useGuestPreauthMobileRestoreEffect({
10175
10833
  dispatch,
10176
10834
  privyReady: state.privyReady,
10835
+ lastResumedAt: state.lastResumedAt,
10177
10836
  mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
10178
10837
  setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
10179
10838
  startGuestAccountPolling: guestAccountPolling.startPolling
@@ -10186,25 +10845,24 @@ function BlinkPaymentInner({
10186
10845
  guestAccount: guestAccountPolling.guestAccount,
10187
10846
  startPolling: guestAccountPolling.startPolling
10188
10847
  });
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
10848
  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]);
10849
+ if (!state.lastResumedAt) return;
10850
+ const token = state.guestSessionToken;
10851
+ const sessionId = state.guestPreauthSessionId;
10852
+ if (!token) return;
10853
+ if (guestAccountPolling.isPolling || guestAccountPolling.guestAccount) return;
10854
+ console.info("[blink-sdk] warm-return: restarting guest account polling from React state", {
10855
+ sessionId: sessionId ?? "(none \u2014 account-only mode)"
10856
+ });
10857
+ guestAccountPolling.startPolling(token, sessionId ?? void 0);
10858
+ }, [
10859
+ state.lastResumedAt,
10860
+ state.guestSessionToken,
10861
+ state.guestPreauthSessionId,
10862
+ guestAccountPolling.isPolling,
10863
+ guestAccountPolling.guestAccount,
10864
+ guestAccountPolling.startPolling
10865
+ ]);
10208
10866
  react.useEffect(() => {
10209
10867
  console.info("[blink-sdk] guestPreauthCompletion effect", {
10210
10868
  hasGuestAccount: !!guestAccountPolling.guestAccount,
@@ -10369,6 +11027,11 @@ function BlinkPaymentInner({
10369
11027
  onLogout: handleLogout,
10370
11028
  onNewPayment: handleNewPayment,
10371
11029
  onSetPhase: (phase) => dispatch({ type: "SET_USER_INTENT", intent: phase }),
11030
+ onBackFromSubflow: () => {
11031
+ authExecutor.cancelPendingExecution();
11032
+ dispatch({ type: "RESTORE_SELECTION" });
11033
+ dispatch({ type: "SET_USER_INTENT", intent: { step: "deposit" } });
11034
+ },
10372
11035
  onSetAuthInput: auth.setAuthInput,
10373
11036
  onSetOtpCode: (code) => {
10374
11037
  auth.setOtpCode(code);
@@ -10402,7 +11065,8 @@ function BlinkPaymentInner({
10402
11065
  handleLogout,
10403
11066
  handleNewPayment,
10404
11067
  onDismiss,
10405
- dispatch
11068
+ dispatch,
11069
+ authExecutor
10406
11070
  ]);
10407
11071
  return /* @__PURE__ */ jsxRuntime.jsx(
10408
11072
  StepRenderer,
@@ -10451,7 +11115,10 @@ function BlinkPaymentInner({
10451
11115
  guestSettingSender: guestTransfer.settingSender,
10452
11116
  guestPendingEntry: guestTransfer.pendingGuestEntry,
10453
11117
  guestQuoteFee: guestTransfer.guestFee?.quote ?? null,
10454
- guestQuoteLoading: guestTransfer.guestQuoteLoading
11118
+ guestQuoteLoading: guestTransfer.guestQuoteLoading,
11119
+ depositQuoteId: depositFee.quoteId,
11120
+ depositQuoteFee: depositFee.quoteFee,
11121
+ depositQuoteLoading: depositFee.quoteLoading
10455
11122
  },
10456
11123
  handlers
10457
11124
  }
@@ -10459,6 +11126,7 @@ function BlinkPaymentInner({
10459
11126
  }
10460
11127
 
10461
11128
  exports.AdvancedSourceScreen = AdvancedSourceScreen;
11129
+ exports.AuthorizationSessionCancelledError = AuthorizationSessionCancelledError;
10462
11130
  exports.BLINK_LOGO = BLINK_LOGO;
10463
11131
  exports.BLINK_MASCOT = BLINK_MASCOT;
10464
11132
  exports.BlinkLoadingScreen = BlinkLoadingScreen;
@@ -10503,6 +11171,8 @@ exports.findDevicePasskey = findDevicePasskey;
10503
11171
  exports.findDevicePasskeyViaPopup = findDevicePasskeyViaPopup;
10504
11172
  exports.getTheme = getTheme;
10505
11173
  exports.guestEntryMatchingRecommended = guestEntryMatchingRecommended;
11174
+ exports.isAuthorizationSessionCancelled = isAuthorizationSessionCancelled;
11175
+ exports.isUserDismissedAuthorizationError = isUserDismissedAuthorizationError;
10506
11176
  exports.lightTheme = lightTheme;
10507
11177
  exports.mapGuestPickerEntries = mapGuestPickerEntries;
10508
11178
  exports.resolvePasskeyRpId = resolvePasskeyRpId;