@swype-org/react-sdk 0.1.300 → 0.1.329

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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",
@@ -6669,6 +7191,10 @@ function resolveHeaderFlowPhase(screen, phase, authenticated) {
6669
7191
  return raw;
6670
7192
  }
6671
7193
  var DEFAULT_MIN_DEPOSIT_USD = 0.25;
7194
+ var GUEST_PREAUTH_LOGIN_HERO_TITLE = "Create an account to complete one tap setup";
7195
+ function isGuestPreauthLogin(state) {
7196
+ return state.isGuestFlow && (state.guestPreauthSessionId != null || state.guestPreauthAccountId != null);
7197
+ }
6672
7198
  function StepRenderer(props) {
6673
7199
  const screen = screenForPhase(props.flow.state.phase);
6674
7200
  const flowPhase = resolveHeaderFlowPhase(
@@ -6748,7 +7274,8 @@ function StepRendererContent({
6748
7274
  sending: activeOtpStatus === "sending-code",
6749
7275
  error: state.error,
6750
7276
  onBack,
6751
- merchantInitials: merchantName ? merchantName.slice(0, 2).toUpperCase() : void 0
7277
+ merchantInitials: merchantName ? merchantName.slice(0, 2).toUpperCase() : void 0,
7278
+ heroTitle: isGuestPreauthLogin(state) ? GUEST_PREAUTH_LOGIN_HERO_TITLE : void 0
6752
7279
  }
6753
7280
  );
6754
7281
  case "otp-verify":
@@ -6847,15 +7374,11 @@ function StepRendererContent({
6847
7374
  0
6848
7375
  );
6849
7376
  const effectiveTokenCount = tokenCount > 0 ? tokenCount : selectSourceTokenCount;
6850
- const effectiveSourceLabel = selectedSourceLabel ?? (selectSourceChainName && selectSourceTokenSymbol ? `${selectSourceTokenSymbol} on ${selectSourceChainName}` : void 0);
6851
- const setupTokenOptions = selectedAccount ? selectedAccount.wallets.flatMap(
6852
- (w) => w.sources.filter((s) => s.balance.total.amount > 0).map((s) => ({
6853
- symbol: s.token.symbol,
6854
- chainName: w.chain.name,
6855
- balance: s.balance.available.amount,
6856
- walletId: w.id
6857
- }))
6858
- ) : 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(
6859
7382
  (chain) => chain.tokens.map((t) => ({
6860
7383
  symbol: t.tokenSymbol,
6861
7384
  chainName: chain.chainName,
@@ -6863,9 +7386,8 @@ function StepRendererContent({
6863
7386
  }))
6864
7387
  );
6865
7388
  const handleSetupSelectToken = (symbol, chainName, walletId) => {
6866
- if (walletId) {
6867
- handlers.onSelectAuthorizedToken(walletId, symbol);
6868
- } else {
7389
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7390
+ if (pendingSelectSource) {
6869
7391
  handlers.onSelectSourceChainChange(chainName);
6870
7392
  handlers.onSetSelectSourceTokenSymbol(symbol);
6871
7393
  }
@@ -6873,17 +7395,18 @@ function StepRendererContent({
6873
7395
  return /* @__PURE__ */ jsxRuntime.jsx(
6874
7396
  SetupScreen,
6875
7397
  {
6876
- 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,
6877
7399
  tokenCount: effectiveTokenCount,
6878
7400
  sourceName,
6879
7401
  onSetupOneTap: handlers.onSetupOneTap,
6880
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7402
+ onBack: handlers.onBackFromSubflow,
6881
7403
  onLogout: handlers.onLogout,
6882
7404
  onAdvanced: handlers.onSelectToken,
6883
7405
  selectedSourceLabel: effectiveSourceLabel,
6884
7406
  loading: savingOneTapLimit,
6885
7407
  error: state.error,
6886
- selectedTokenSymbol: selectedSource?.token.symbol ?? selectSourceTokenSymbol,
7408
+ selectedTokenSymbol: setupSelectedTokenSymbol,
7409
+ selectedChainName: setupSelectedChainName,
6887
7410
  tokenOptions: setupTokenOptions,
6888
7411
  onSelectToken: handleSetupSelectToken
6889
7412
  }
@@ -6905,6 +7428,7 @@ function StepRendererContent({
6905
7428
  case "deposit": {
6906
7429
  const parsedAmt = depositAmount != null ? depositAmount : 5;
6907
7430
  const minDepositFloor = depositAmount != null ? depositAmount : DEFAULT_MIN_DEPOSIT_USD;
7431
+ const depositTokenOptions = tokenOptionsForLinkedAccount(selectedAccount, state.chains);
6908
7432
  return /* @__PURE__ */ jsxRuntime.jsx(
6909
7433
  DepositScreen,
6910
7434
  {
@@ -6913,6 +7437,8 @@ function StepRendererContent({
6913
7437
  remainingLimit: selectedSource != null ? selectedSource.remainingAllowance ?? null : selectedAccount?.remainingAllowance ?? null,
6914
7438
  tokenCount,
6915
7439
  initialAmount: parsedAmt,
7440
+ quoteFee: forms.depositQuoteFee,
7441
+ quoteLoading: forms.depositQuoteLoading,
6916
7442
  processing: state.creatingTransfer,
6917
7443
  error: state.error,
6918
7444
  onDeposit: handlers.onPay,
@@ -6927,8 +7453,13 @@ function StepRendererContent({
6927
7453
  onAuthorizeAccount: handlers.onContinueConnection,
6928
7454
  onAddProvider: () => handlers.onSetPhase({ step: "wallet-picker", reason: "switch" }),
6929
7455
  onSelectToken: handlers.onSelectToken,
7456
+ tokenOptions: depositTokenOptions,
7457
+ onPickToken: (symbol, chainName, walletId) => {
7458
+ handleInlineTokenSelection(handlers, state.chains, selectedAccount, symbol, chainName, walletId);
7459
+ },
6930
7460
  selectedSourceLabel,
6931
7461
  selectedTokenSymbol: selectedSource?.token.symbol,
7462
+ selectedChainName: selectedWallet?.chain.name,
6932
7463
  minDepositFloor
6933
7464
  }
6934
7465
  );
@@ -6969,7 +7500,7 @@ function StepRendererContent({
6969
7500
  chains: state.chains,
6970
7501
  onSelectAuthorized: handlers.onSelectAuthorizedToken,
6971
7502
  onAuthorizeToken: handlers.onAuthorizeToken,
6972
- onBack: () => handlers.onSetPhase({ step: "deposit" }),
7503
+ onBack: handlers.onBackFromSubflow,
6973
7504
  onLogout: handlers.onLogout,
6974
7505
  depositAmount: depositAmount ?? void 0,
6975
7506
  selectedTokenSymbol: selectedSource?.token.symbol,
@@ -7048,7 +7579,7 @@ function StepRendererContent({
7048
7579
  })() : void 0,
7049
7580
  onDone: onDismiss ?? handlers.onNewPayment,
7050
7581
  onLogout: authenticated ? handlers.onLogout : void 0,
7051
- onPreauthorize: state.isGuestFlow ? handlers.onPreauthorize : void 0
7582
+ onPreauthorize: state.isGuestFlow && isDesktop ? handlers.onPreauthorize : void 0
7052
7583
  }
7053
7584
  );
7054
7585
  }
@@ -7447,6 +7978,9 @@ function useTransferHandlers(deps) {
7447
7978
  sourceTokenAddress,
7448
7979
  activeCredentialId,
7449
7980
  selectedAccountId,
7981
+ selectedWalletId,
7982
+ selectedTokenSymbol,
7983
+ quoteId,
7450
7984
  transfer,
7451
7985
  accounts
7452
7986
  } = deps;
@@ -7460,9 +7994,32 @@ function useTransferHandlers(deps) {
7460
7994
  fetchProviders(apiBaseUrl, token)
7461
7995
  ]);
7462
7996
  const parsedAmt = depositAmount != null ? depositAmount : 0;
7463
- const defaults = resolveDepositSelection(accts, parsedAmt, selectedAccountId);
7464
- dispatch({ type: "ACCOUNTS_RELOADED", accounts: accts, providers: prov, defaults });
7465
- }, [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
+ ]);
7466
8023
  const handlePay = react.useCallback(async (payAmount, sourceOverrides) => {
7467
8024
  const minUsd = effectiveMinTransferAmountUsd(depositAmount);
7468
8025
  if (isNaN(payAmount) || payAmount < minUsd) {
@@ -7506,7 +8063,8 @@ function useTransferHandlers(deps) {
7506
8063
  sourceId: effectiveSourceId,
7507
8064
  sourceTokenAddress,
7508
8065
  destination,
7509
- amount: payAmount
8066
+ amount: payAmount,
8067
+ ...quoteId ? { quoteId } : {}
7510
8068
  });
7511
8069
  dispatch({ type: "TRANSFER_CREATED", transfer: t });
7512
8070
  if (t.status === "COMPLETED") {
@@ -7538,6 +8096,7 @@ function useTransferHandlers(deps) {
7538
8096
  accounts,
7539
8097
  destination,
7540
8098
  apiBaseUrl,
8099
+ quoteId,
7541
8100
  getAccessToken,
7542
8101
  transferSigning,
7543
8102
  polling,
@@ -7588,20 +8147,20 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7588
8147
  const selectSourceAvailableBalance = react.useMemo(() => {
7589
8148
  if (!pendingSelectSourceAction) return 0;
7590
8149
  const options2 = pendingSelectSourceAction.metadata?.options ?? [];
7591
- const recommended = selectSourceRecommended;
7592
- if (recommended) {
7593
- const match = options2.find(
7594
- (opt) => opt.chainName === recommended.chainName && opt.tokenSymbol === recommended.tokenSymbol
7595
- );
7596
- if (match) return Number(match.rawBalance) / Math.pow(10, match.decimals);
7597
- }
7598
- let max = 0;
7599
- for (const opt of options2) {
7600
- const bal = Number(opt.rawBalance) / Math.pow(10, opt.decimals);
7601
- if (bal > max) max = bal;
7602
- }
7603
- return max;
7604
- }, [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
+ ]);
7605
8164
  const handleSelectSourceChainChange = react.useCallback(
7606
8165
  (chainName) => {
7607
8166
  setSelectSourceChainName(chainName);
@@ -7652,7 +8211,7 @@ function useSourceSelectionHandlers(dispatch, authExecutor, options) {
7652
8211
  initializedSelectSourceActionRef
7653
8212
  };
7654
8213
  }
7655
- function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete) {
8214
+ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransferIdRef, stateTransfer, refs, guestCheckout, onComplete, lastResumedAt) {
7656
8215
  const {
7657
8216
  mobileSetupFlowRef,
7658
8217
  handlingMobileReturnRef,
@@ -7663,6 +8222,8 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7663
8222
  onCompleteRef.current = onComplete;
7664
8223
  const guestPollingActiveRef = react.useRef(false);
7665
8224
  const guestPollingCleanupRef = react.useRef(null);
8225
+ const mobileFlowRef = react.useRef(guestCheckout.mobileFlow);
8226
+ mobileFlowRef.current = guestCheckout.mobileFlow;
7666
8227
  const guestTransferIdRef = react.useRef(guestCheckout.guestTransferId);
7667
8228
  guestTransferIdRef.current = guestCheckout.guestTransferId;
7668
8229
  const guestSessionTokenRef = react.useRef(guestCheckout.guestSessionToken);
@@ -7718,27 +8279,15 @@ function useMobileFlowHandlers(dispatch, polling, reloadAccounts, pollingTransfe
7718
8279
  startGuestPolling
7719
8280
  ]);
7720
8281
  react.useEffect(() => {
7721
- const tryStartPolling = () => {
7722
- if (guestPollingActiveRef.current) return;
7723
- const transferId = guestTransferIdRef.current;
7724
- const token = guestSessionTokenRef.current;
7725
- if (transferId && token) {
7726
- startGuestPolling(transferId, token);
7727
- }
7728
- };
7729
- const handleVisibility = () => {
7730
- if (document.visibilityState === "visible") tryStartPolling();
7731
- };
7732
- const handlePageShow = (e) => {
7733
- if (e.persisted) tryStartPolling();
7734
- };
7735
- document.addEventListener("visibilitychange", handleVisibility);
7736
- window.addEventListener("pageshow", handlePageShow);
7737
- return () => {
7738
- document.removeEventListener("visibilitychange", handleVisibility);
7739
- window.removeEventListener("pageshow", handlePageShow);
7740
- };
7741
- }, [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]);
7742
8291
  const handleAuthorizedMobileReturn = react.useCallback(async (authorizedTransfer, isSetup) => {
7743
8292
  if (handlingMobileReturnRef.current) return;
7744
8293
  handlingMobileReturnRef.current = true;
@@ -7925,9 +8474,6 @@ function useProviderHandlers(deps) {
7925
8474
  useWalletConnector: useWalletConnectorProp,
7926
8475
  userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
7927
8476
  });
7928
- if (!isMobile) {
7929
- dispatch({ type: "PAY_STARTED" });
7930
- }
7931
8477
  try {
7932
8478
  let accountId;
7933
8479
  let sessionId;
@@ -7966,18 +8512,22 @@ function useProviderHandlers(deps) {
7966
8512
  triggerDeeplink(sessionUri);
7967
8513
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: sessionUri });
7968
8514
  } else {
8515
+ dispatch({
8516
+ type: "SELECT_ACCOUNT",
8517
+ accountId,
8518
+ walletId: null
8519
+ });
7969
8520
  await authExecutor.executeSessionById(sessionId);
7970
8521
  await reloadAccounts();
7971
8522
  }
7972
8523
  } catch (err) {
8524
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8525
+ return;
8526
+ }
7973
8527
  captureException(err);
7974
8528
  const msg = err instanceof Error ? err.message : "Failed to set up wallet";
7975
8529
  dispatch({ type: "PAY_ERROR", error: msg });
7976
8530
  onError?.(msg);
7977
- } finally {
7978
- if (!isMobile) {
7979
- dispatch({ type: "PAY_ENDED" });
7980
- }
7981
8531
  }
7982
8532
  }, [
7983
8533
  authenticated,
@@ -8040,7 +8590,12 @@ function useProviderHandlers(deps) {
8040
8590
  }
8041
8591
  const acct = accounts.find((a) => a.id === selectedAccountId);
8042
8592
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8043
- 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" });
8044
8599
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8045
8600
  }
8046
8601
  dispatch({ type: "SET_ERROR", error: null });
@@ -8060,10 +8615,6 @@ function useProviderHandlers(deps) {
8060
8615
  activeCredentialId,
8061
8616
  { tokenAddress: source?.address, chainId: evmChainId }
8062
8617
  );
8063
- const isMobile = !shouldUseWalletConnector({
8064
- useWalletConnector: useWalletConnectorProp,
8065
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8066
- });
8067
8618
  if (isMobile) {
8068
8619
  handlingMobileReturnRef.current = false;
8069
8620
  mobileSetupFlowRef.current = true;
@@ -8080,12 +8631,20 @@ function useProviderHandlers(deps) {
8080
8631
  });
8081
8632
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8082
8633
  triggerDeeplink(session.uri);
8634
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8083
8635
  } else {
8084
8636
  await authExecutor.executeSessionById(session.id);
8085
8637
  await reloadAccounts();
8638
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8086
8639
  }
8087
8640
  } catch (err) {
8641
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8642
+ return;
8643
+ }
8088
8644
  captureException(err);
8645
+ if (isMobile) {
8646
+ dispatch({ type: "RESTORE_SELECTION" });
8647
+ }
8089
8648
  const msg = err instanceof Error ? err.message : "Failed to increase limit";
8090
8649
  dispatch({ type: "SET_ERROR", error: msg });
8091
8650
  onError?.(msg);
@@ -8130,7 +8689,12 @@ function useProviderHandlers(deps) {
8130
8689
  }
8131
8690
  const acct = accounts.find((a) => a.id === selectedAccountId);
8132
8691
  const matchedProvider = acct ? providers.find((p) => p.name === acct.name) : void 0;
8133
- 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" });
8134
8698
  dispatch({ type: "SELECT_PROVIDER", providerId: matchedProvider.id });
8135
8699
  }
8136
8700
  dispatch({ type: "SET_ERROR", error: null });
@@ -8145,10 +8709,6 @@ function useProviderHandlers(deps) {
8145
8709
  activeCredentialId,
8146
8710
  { tokenAddress, chainId }
8147
8711
  );
8148
- const isMobile = !shouldUseWalletConnector({
8149
- useWalletConnector: useWalletConnectorProp,
8150
- userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
8151
- });
8152
8712
  if (isMobile) {
8153
8713
  handlingMobileReturnRef.current = false;
8154
8714
  mobileSetupFlowRef.current = true;
@@ -8166,13 +8726,21 @@ function useProviderHandlers(deps) {
8166
8726
  });
8167
8727
  dispatch({ type: "MOBILE_DEEPLINK_READY", deeplinkUri: session.uri });
8168
8728
  triggerDeeplink(session.uri);
8729
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8169
8730
  } else {
8170
8731
  await authExecutor.executeSessionById(session.id);
8171
8732
  await reloadAccounts();
8172
8733
  dispatch({ type: "SELECT_TOKEN", walletId: _walletId, tokenSymbol });
8734
+ dispatch({ type: "DISCARD_SAVED_SELECTION" });
8173
8735
  }
8174
8736
  } catch (err) {
8737
+ if (isAuthorizationSessionCancelled(err) || isUserDismissedAuthorizationError(err)) {
8738
+ return;
8739
+ }
8175
8740
  captureException(err);
8741
+ if (isMobile) {
8742
+ dispatch({ type: "RESTORE_SELECTION" });
8743
+ }
8176
8744
  const msg = err instanceof Error ? err.message : "Failed to authorize token";
8177
8745
  dispatch({ type: "SET_ERROR", error: msg });
8178
8746
  onError?.(msg);
@@ -9109,7 +9677,15 @@ function useDataLoadEffect(deps) {
9109
9677
  ]);
9110
9678
  if (cancelled) return;
9111
9679
  const parsedAmt = depositAmount != null ? depositAmount : 0;
9112
- 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
+ );
9113
9689
  const persisted = loadMobileFlowState();
9114
9690
  const clearMobile = persisted?.isSetup && accts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
9115
9691
  dispatch({
@@ -9118,7 +9694,8 @@ function useDataLoadEffect(deps) {
9118
9694
  accounts: accts,
9119
9695
  chains: chn,
9120
9696
  defaults,
9121
- clearMobileState: clearMobile
9697
+ clearMobileState: clearMobile,
9698
+ resetSelectedTokenSymbol
9122
9699
  });
9123
9700
  if (clearMobile) clearMobileFlowState();
9124
9701
  } catch (err) {
@@ -9147,6 +9724,8 @@ function useDataLoadEffect(deps) {
9147
9724
  apiBaseUrl,
9148
9725
  state.activeCredentialId,
9149
9726
  state.selectedAccountId,
9727
+ state.selectedWalletId,
9728
+ state.selectedTokenSymbol,
9150
9729
  depositAmount
9151
9730
  ]);
9152
9731
  react.useEffect(() => {
@@ -9375,25 +9954,16 @@ function useMobilePollingEffect(deps) {
9375
9954
  const poll = isReauth ? pollReauthorization : pollWalletActive;
9376
9955
  poll();
9377
9956
  const intervalId = window.setInterval(poll, POLL_INTERVAL_MS);
9378
- const handleVisibility = () => {
9379
- if (document.visibilityState === "visible" && !cancelled) poll();
9380
- };
9381
- const handlePageShow = (e) => {
9382
- if (e.persisted && !cancelled) poll();
9383
- };
9384
- document.addEventListener("visibilitychange", handleVisibility);
9385
- window.addEventListener("pageshow", handlePageShow);
9386
9957
  return () => {
9387
9958
  cancelled = true;
9388
9959
  window.clearInterval(intervalId);
9389
- document.removeEventListener("visibilitychange", handleVisibility);
9390
- window.removeEventListener("pageshow", handlePageShow);
9391
9960
  };
9392
9961
  }, [
9393
9962
  state.mobileFlow,
9394
9963
  state.isGuestFlow,
9395
9964
  state.guestPreauthSessionId,
9396
9965
  state.activeCredentialId,
9966
+ state.lastResumedAt,
9397
9967
  apiBaseUrl,
9398
9968
  reloadAccounts,
9399
9969
  dispatch,
@@ -9410,28 +9980,13 @@ function useMobilePollingEffect(deps) {
9410
9980
  const transferIdToResume = pollingTransferIdRef.current ?? state.transfer?.id;
9411
9981
  if (!transferIdToResume) return;
9412
9982
  if (!polling.isPolling) polling.startPolling(transferIdToResume);
9413
- const handleVisibility = () => {
9414
- if (document.visibilityState === "visible" && !handlingMobileReturnRef.current) {
9415
- polling.startPolling(transferIdToResume);
9416
- }
9417
- };
9418
- const handlePageShow = (e) => {
9419
- if (e.persisted && !handlingMobileReturnRef.current) {
9420
- polling.startPolling(transferIdToResume);
9421
- }
9422
- };
9423
- document.addEventListener("visibilitychange", handleVisibility);
9424
- window.addEventListener("pageshow", handlePageShow);
9425
- return () => {
9426
- document.removeEventListener("visibilitychange", handleVisibility);
9427
- window.removeEventListener("pageshow", handlePageShow);
9428
- };
9429
9983
  }, [
9430
9984
  state.mobileFlow,
9431
9985
  state.transfer?.id,
9432
9986
  state.isGuestFlow,
9433
9987
  state.guestPreauthSessionId,
9434
9988
  state.guestPreauthAccountId,
9989
+ state.lastResumedAt,
9435
9990
  polling.isPolling,
9436
9991
  polling.startPolling,
9437
9992
  handlingMobileReturnRef,
@@ -9442,6 +9997,7 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9442
9997
  const {
9443
9998
  dispatch,
9444
9999
  privyReady,
10000
+ lastResumedAt,
9445
10001
  mobileSetupFlowRef,
9446
10002
  setupAccountIdRef,
9447
10003
  startGuestAccountPolling
@@ -9452,68 +10008,43 @@ function useGuestPreauthMobileRestoreEffect(deps) {
9452
10008
  console.info("[blink-sdk] guestPreauthMobileRestore: skipping, privyReady=false");
9453
10009
  return;
9454
10010
  }
9455
- const tryStart = (trigger) => {
9456
- if (startedRef.current) {
9457
- console.info("[blink-sdk] guestPreauthMobileRestore.tryStart: already started", { trigger });
9458
- return;
9459
- }
9460
- const persisted = loadMobileFlowState();
9461
- console.info("[blink-sdk] guestPreauthMobileRestore.tryStart", {
9462
- trigger,
9463
- hasPersistedState: !!persisted,
9464
- isGuestPreauth: persisted?.isGuestPreauth ?? false,
9465
- hasGuestSessionToken: !!persisted?.guestSessionToken,
9466
- hasSessionId: !!persisted?.sessionId,
9467
- accountId: persisted?.accountId ?? null
9468
- });
9469
- if (!persisted?.isGuestPreauth || !persisted.guestSessionToken || !persisted.sessionId) {
9470
- return;
9471
- }
9472
- startedRef.current = true;
9473
- mobileSetupFlowRef.current = true;
9474
- if (persisted.accountId) {
9475
- setupAccountIdRef.current = persisted.accountId;
9476
- }
9477
- if (persisted.accountId) {
9478
- dispatch({
9479
- type: "GUEST_PREAUTH_DETECTED",
9480
- accountId: persisted.accountId,
9481
- sessionId: persisted.sessionId
9482
- });
9483
- }
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) {
9484
10033
  dispatch({
9485
- type: "MOBILE_DEEPLINK_READY",
9486
- deeplinkUri: persisted.deeplinkUri
9487
- });
9488
- console.info("[blink-sdk] guestPreauthMobileRestore: starting guest account polling", {
9489
- trigger,
10034
+ type: "GUEST_PREAUTH_DETECTED",
10035
+ accountId: persisted.accountId,
9490
10036
  sessionId: persisted.sessionId
9491
10037
  });
9492
- startGuestAccountPolling(persisted.guestSessionToken, persisted.sessionId);
9493
- };
9494
- const onVisibility = () => {
9495
- console.info("[blink-sdk] guestPreauthMobileRestore: visibilitychange fired", {
9496
- visibilityState: document.visibilityState,
9497
- startedRef: startedRef.current
9498
- });
9499
- if (document.visibilityState === "visible") tryStart("visibilitychange");
9500
- };
9501
- const onPageShow = (e) => {
9502
- console.info("[blink-sdk] guestPreauthMobileRestore: pageshow fired", {
9503
- persisted: e.persisted,
9504
- visibilityState: document.visibilityState,
9505
- startedRef: startedRef.current
9506
- });
9507
- if (document.visibilityState === "visible") tryStart("pageshow");
9508
- };
9509
- tryStart("initial");
9510
- document.addEventListener("visibilitychange", onVisibility);
9511
- window.addEventListener("pageshow", onPageShow);
9512
- return () => {
9513
- document.removeEventListener("visibilitychange", onVisibility);
9514
- window.removeEventListener("pageshow", onPageShow);
9515
- };
9516
- }, [privyReady, dispatch, mobileSetupFlowRef, setupAccountIdRef, startGuestAccountPolling]);
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]);
9517
10048
  }
9518
10049
  function useSelectSourceEffect(deps) {
9519
10050
  const {
@@ -9714,7 +10245,9 @@ function useGuestDesktopPreauthSessionEffect(deps) {
9714
10245
  function useGuestPreauthPhaseSyncEffect(deps) {
9715
10246
  const { state, dispatch, authExecutor, isDesktop, privyAuthenticated } = deps;
9716
10247
  react.useEffect(() => {
9717
- if (!state.guestPreauthorizing || !isDesktop) return;
10248
+ if (!isDesktop) return;
10249
+ const standardDesktopAuth = privyAuthenticated && state.activeCredentialId != null && !state.guestPreauthorizing;
10250
+ if (!state.guestPreauthorizing && !standardDesktopAuth) return;
9718
10251
  const pending = authExecutor.pendingSelectSource;
9719
10252
  if (pending) {
9720
10253
  if (state.phase.step === "token-picker") {
@@ -9738,7 +10271,7 @@ function useGuestPreauthPhaseSyncEffect(deps) {
9738
10271
  dispatch({ type: "SET_USER_INTENT", intent: { step: "one-tap-setup", action: null } });
9739
10272
  return;
9740
10273
  }
9741
- if (!privyAuthenticated && authExecutor.executing && state.guestPreauthAccountId) {
10274
+ if (state.guestPreauthorizing && !privyAuthenticated && authExecutor.executing && state.guestPreauthAccountId) {
9742
10275
  const accountId = state.guestPreauthAccountId;
9743
10276
  const p = state.phase;
9744
10277
  if (p.step === "wallet-setup" && p.mobile == null && p.guestDesktopExtension && p.accountId === accountId) {
@@ -9766,6 +10299,7 @@ function useGuestPreauthPhaseSyncEffect(deps) {
9766
10299
  state.guestPreauthorizing,
9767
10300
  state.phase,
9768
10301
  state.guestPreauthAccountId,
10302
+ state.activeCredentialId,
9769
10303
  isDesktop,
9770
10304
  privyAuthenticated,
9771
10305
  authExecutor.pendingSelectSource,
@@ -9814,7 +10348,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9814
10348
  react.useEffect(() => {
9815
10349
  if (!isDesktop || state.guestPreauthorizing) return;
9816
10350
  if (!state.privyAuthenticated || !state.activeCredentialId || !state.selectedAccountId) return;
9817
- const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && !authExecutor.pendingOneTapSetup;
10351
+ const shouldPin = authExecutor.executing && !authExecutor.pendingSelectSource && (!authExecutor.pendingOneTapSetup || authExecutor.authorizationSessionStackDepth > 1);
9818
10352
  if (shouldPin && !state.standardDesktopInlineOpenWallet) {
9819
10353
  dispatch({ type: "SET_STANDARD_DESKTOP_INLINE_OPEN_WALLET", value: true });
9820
10354
  return;
@@ -9832,6 +10366,7 @@ function useStandardDesktopInlineOpenWalletEffect(deps) {
9832
10366
  authExecutor.executing,
9833
10367
  authExecutor.pendingSelectSource,
9834
10368
  authExecutor.pendingOneTapSetup,
10369
+ authExecutor.authorizationSessionStackDepth,
9835
10370
  dispatch
9836
10371
  ]);
9837
10372
  }
@@ -9850,7 +10385,84 @@ function useGuestAccountAutoPollingEffect(deps) {
9850
10385
  startPolling(guestSessionToken);
9851
10386
  }, [mobileFlow, isGuestFlow, guestSessionToken, isPolling, guestAccount, startPolling]);
9852
10387
  }
9853
- 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) {
9854
10466
  const { apiBaseUrl } = useBlinkConfig();
9855
10467
  const [guestAccount, setGuestAccount] = react.useState(null);
9856
10468
  const [isPolling, setIsPolling] = react.useState(false);
@@ -9948,36 +10560,71 @@ function useGuestAccountPolling(intervalMs = 3e3) {
9948
10560
  [poll, intervalMs, stopPolling]
9949
10561
  );
9950
10562
  react.useEffect(() => {
9951
- const handleVisibility = () => {
9952
- console.info("[blink-sdk] useGuestAccountPolling: visibilitychange fired", {
9953
- visibilityState: document.visibilityState,
9954
- hasToken: !!guestTokenRef.current,
9955
- hasSessionId: !!sessionIdRef.current
9956
- });
9957
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9958
- void poll();
9959
- }
9960
- };
9961
- const handlePageShow = (e) => {
9962
- console.info("[blink-sdk] useGuestAccountPolling: pageshow fired", {
9963
- persisted: e.persisted,
9964
- visibilityState: document.visibilityState,
9965
- hasToken: !!guestTokenRef.current,
9966
- 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" }
9967
10606
  });
9968
- if (document.visibilityState === "visible" && guestTokenRef.current) {
9969
- 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);
9970
10618
  }
9971
- };
9972
- document.addEventListener("visibilitychange", handleVisibility);
9973
- window.addEventListener("pageshow", handlePageShow);
10619
+ }
10620
+ }, [enabled, walletId, sourceTokenAddress, destination, amount, apiBaseUrl, getAccessToken]);
10621
+ react.useEffect(() => {
10622
+ fetchQuote();
9974
10623
  return () => {
9975
- document.removeEventListener("visibilitychange", handleVisibility);
9976
- window.removeEventListener("pageshow", handlePageShow);
10624
+ abortRef.current?.abort();
9977
10625
  };
9978
- }, [poll]);
9979
- react.useEffect(() => () => stopPolling(), [stopPolling]);
9980
- return { guestAccount, error, isPolling, startPolling, stopPolling };
10626
+ }, [fetchQuote]);
10627
+ return { quoteId, quoteFee, quoteLoading };
9981
10628
  }
9982
10629
  function BlinkPayment(props) {
9983
10630
  const resetKey = react.useRef(0);
@@ -10017,7 +10664,7 @@ function BlinkPaymentInner({
10017
10664
  );
10018
10665
  const authExecutor = useAuthorizationExecutor();
10019
10666
  const polling = useTransferPolling();
10020
- const guestAccountPolling = useGuestAccountPolling();
10667
+ const guestAccountPolling = useGuestAccountPolling(3e3, state.lastResumedAt);
10021
10668
  const transferSigning = useTransferSigning();
10022
10669
  const mobileFlowRefs = {
10023
10670
  mobileSetupFlowRef: react.useRef(false),
@@ -10035,6 +10682,15 @@ function BlinkPaymentInner({
10035
10682
  state.accounts,
10036
10683
  state.knownCredentialIds
10037
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
+ });
10038
10694
  const transfer = useTransferHandlers({
10039
10695
  dispatch,
10040
10696
  getAccessToken,
@@ -10052,6 +10708,9 @@ function BlinkPaymentInner({
10052
10708
  sourceTokenAddress: derived.selectedSource?.address,
10053
10709
  activeCredentialId: state.activeCredentialId,
10054
10710
  selectedAccountId: state.selectedAccountId,
10711
+ selectedWalletId: state.selectedWalletId,
10712
+ selectedTokenSymbol: state.selectedTokenSymbol,
10713
+ quoteId: depositFee.quoteId,
10055
10714
  transfer: state.transfer,
10056
10715
  accounts: state.accounts
10057
10716
  });
@@ -10068,7 +10727,8 @@ function BlinkPaymentInner({
10068
10727
  guestTransferId: state.guestTransferId,
10069
10728
  guestSessionToken: state.guestSessionToken
10070
10729
  },
10071
- onComplete
10730
+ onComplete,
10731
+ state.lastResumedAt
10072
10732
  );
10073
10733
  const sourceSelection = useSourceSelectionHandlers(dispatch, authExecutor, {
10074
10734
  guestPreauthSessionId: state.guestPreauthSessionId,
@@ -10164,10 +10824,15 @@ function BlinkPaymentInner({
10164
10824
  dispatch({ type: "SYNC_AMOUNT", amount: depositAmount.toString() });
10165
10825
  }
10166
10826
  }, [depositAmount, dispatch]);
10827
+ usePageResumeEffect({
10828
+ dispatch,
10829
+ handlingMobileReturnRef: mobileFlowRefs.handlingMobileReturnRef
10830
+ });
10167
10831
  usePrivySessionSyncEffect({ dispatch, ready, authenticated });
10168
10832
  useGuestPreauthMobileRestoreEffect({
10169
10833
  dispatch,
10170
10834
  privyReady: state.privyReady,
10835
+ lastResumedAt: state.lastResumedAt,
10171
10836
  mobileSetupFlowRef: mobileFlowRefs.mobileSetupFlowRef,
10172
10837
  setupAccountIdRef: mobileFlowRefs.setupAccountIdRef,
10173
10838
  startGuestAccountPolling: guestAccountPolling.startPolling
@@ -10180,25 +10845,24 @@ function BlinkPaymentInner({
10180
10845
  guestAccount: guestAccountPolling.guestAccount,
10181
10846
  startPolling: guestAccountPolling.startPolling
10182
10847
  });
10183
- const guestSessionTokenRef = react.useRef(state.guestSessionToken);
10184
- guestSessionTokenRef.current = state.guestSessionToken;
10185
- const guestPreauthSessionIdRef = react.useRef(state.guestPreauthSessionId);
10186
- guestPreauthSessionIdRef.current = state.guestPreauthSessionId;
10187
10848
  react.useEffect(() => {
10188
- const handleVisibility = () => {
10189
- if (document.visibilityState !== "visible") return;
10190
- const token = guestSessionTokenRef.current;
10191
- const sessionId = guestPreauthSessionIdRef.current;
10192
- if (!token) return;
10193
- if (guestAccountPolling.isPolling || guestAccountPolling.guestAccount) return;
10194
- console.info("[blink-sdk] warm-return: restarting guest account polling from React state", {
10195
- sessionId: sessionId ?? "(none \u2014 account-only mode)"
10196
- });
10197
- guestAccountPolling.startPolling(token, sessionId ?? void 0);
10198
- };
10199
- document.addEventListener("visibilitychange", handleVisibility);
10200
- return () => document.removeEventListener("visibilitychange", handleVisibility);
10201
- }, [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
+ ]);
10202
10866
  react.useEffect(() => {
10203
10867
  console.info("[blink-sdk] guestPreauthCompletion effect", {
10204
10868
  hasGuestAccount: !!guestAccountPolling.guestAccount,
@@ -10363,6 +11027,11 @@ function BlinkPaymentInner({
10363
11027
  onLogout: handleLogout,
10364
11028
  onNewPayment: handleNewPayment,
10365
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
+ },
10366
11035
  onSetAuthInput: auth.setAuthInput,
10367
11036
  onSetOtpCode: (code) => {
10368
11037
  auth.setOtpCode(code);
@@ -10396,7 +11065,8 @@ function BlinkPaymentInner({
10396
11065
  handleLogout,
10397
11066
  handleNewPayment,
10398
11067
  onDismiss,
10399
- dispatch
11068
+ dispatch,
11069
+ authExecutor
10400
11070
  ]);
10401
11071
  return /* @__PURE__ */ jsxRuntime.jsx(
10402
11072
  StepRenderer,
@@ -10445,7 +11115,10 @@ function BlinkPaymentInner({
10445
11115
  guestSettingSender: guestTransfer.settingSender,
10446
11116
  guestPendingEntry: guestTransfer.pendingGuestEntry,
10447
11117
  guestQuoteFee: guestTransfer.guestFee?.quote ?? null,
10448
- guestQuoteLoading: guestTransfer.guestQuoteLoading
11118
+ guestQuoteLoading: guestTransfer.guestQuoteLoading,
11119
+ depositQuoteId: depositFee.quoteId,
11120
+ depositQuoteFee: depositFee.quoteFee,
11121
+ depositQuoteLoading: depositFee.quoteLoading
10449
11122
  },
10450
11123
  handlers
10451
11124
  }
@@ -10453,6 +11126,7 @@ function BlinkPaymentInner({
10453
11126
  }
10454
11127
 
10455
11128
  exports.AdvancedSourceScreen = AdvancedSourceScreen;
11129
+ exports.AuthorizationSessionCancelledError = AuthorizationSessionCancelledError;
10456
11130
  exports.BLINK_LOGO = BLINK_LOGO;
10457
11131
  exports.BLINK_MASCOT = BLINK_MASCOT;
10458
11132
  exports.BlinkLoadingScreen = BlinkLoadingScreen;
@@ -10497,6 +11171,8 @@ exports.findDevicePasskey = findDevicePasskey;
10497
11171
  exports.findDevicePasskeyViaPopup = findDevicePasskeyViaPopup;
10498
11172
  exports.getTheme = getTheme;
10499
11173
  exports.guestEntryMatchingRecommended = guestEntryMatchingRecommended;
11174
+ exports.isAuthorizationSessionCancelled = isAuthorizationSessionCancelled;
11175
+ exports.isUserDismissedAuthorizationError = isUserDismissedAuthorizationError;
10500
11176
  exports.lightTheme = lightTheme;
10501
11177
  exports.mapGuestPickerEntries = mapGuestPickerEntries;
10502
11178
  exports.resolvePasskeyRpId = resolvePasskeyRpId;